Snap for 7456046 from 012e978e31 to sc-release
Change-Id: If661343407508ed906a12f19cf37e05f17b5fb01
This commit is contained in:
@@ -16,8 +16,6 @@
|
||||
<resources>
|
||||
<!-- The component to receive app sharing Intents -->
|
||||
<string name="app_sharing_component" translatable="false"/>
|
||||
<!-- The package to receive Listen, Translate, and Search Intents -->
|
||||
<string name="niu_actions_package" translatable="false"/>
|
||||
|
||||
<!-- Feature Flags -->
|
||||
<bool name="enable_niu_actions">true</bool>
|
||||
|
||||
@@ -21,6 +21,7 @@ import static com.android.quickstep.views.OverviewActionsView.DISABLED_ROTATED;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.app.assist.AssistContent;
|
||||
import android.content.ActivityNotFoundException;
|
||||
import android.content.ContentResolver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
@@ -29,10 +30,10 @@ import android.os.SystemClock;
|
||||
import android.os.UserManager;
|
||||
import android.provider.Settings;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.annotation.VisibleForTesting;
|
||||
|
||||
import com.android.launcher3.R;
|
||||
import com.android.quickstep.util.AssistContentRequester;
|
||||
import com.android.quickstep.views.OverviewActionsView;
|
||||
import com.android.quickstep.views.TaskThumbnailView;
|
||||
@@ -90,9 +91,7 @@ public final class TaskOverlayFactoryGo extends TaskOverlayFactory {
|
||||
public void initOverlay(Task task, ThumbnailData thumbnail, Matrix matrix,
|
||||
boolean rotated) {
|
||||
getActionsView().updateDisabledFlags(DISABLED_NO_THUMBNAIL, thumbnail == null);
|
||||
mNIUPackageName =
|
||||
mApplicationContext.getString(R.string.niu_actions_package);
|
||||
|
||||
checkSettings();
|
||||
if (thumbnail == null || TextUtils.isEmpty(mNIUPackageName)) {
|
||||
return;
|
||||
}
|
||||
@@ -105,7 +104,6 @@ public final class TaskOverlayFactoryGo extends TaskOverlayFactory {
|
||||
getActionsView().setCallbacks(new OverlayUICallbacksGoImpl(isAllowedByPolicy, task));
|
||||
mTaskPackageName = task.key.getPackageName();
|
||||
|
||||
checkPermissions();
|
||||
if (!mAssistPermissionsEnabled) {
|
||||
return;
|
||||
}
|
||||
@@ -137,7 +135,11 @@ public final class TaskOverlayFactoryGo extends TaskOverlayFactory {
|
||||
mImageApi.shareAsDataWithExplicitIntent(/* crop */ null, intent);
|
||||
} else {
|
||||
intent.putExtra(ACTIONS_ERROR_CODE, ERROR_PERMISSIONS);
|
||||
mApplicationContext.startActivity(intent);
|
||||
try {
|
||||
mApplicationContext.startActivity(intent);
|
||||
} catch (ActivityNotFoundException e) {
|
||||
Log.e(TAG, "No activity found to receive permission error intent");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -160,13 +162,17 @@ public final class TaskOverlayFactoryGo extends TaskOverlayFactory {
|
||||
* Checks whether the Assistant has screen context permissions
|
||||
*/
|
||||
@VisibleForTesting
|
||||
public void checkPermissions() {
|
||||
public void checkSettings() {
|
||||
ContentResolver contentResolver = mApplicationContext.getContentResolver();
|
||||
boolean structureEnabled = Settings.Secure.getInt(contentResolver,
|
||||
Settings.Secure.ASSIST_STRUCTURE_ENABLED, 1) != 0;
|
||||
boolean screenshotEnabled = Settings.Secure.getInt(contentResolver,
|
||||
Settings.Secure.ASSIST_SCREENSHOT_ENABLED, 1) != 0;
|
||||
mAssistPermissionsEnabled = structureEnabled && screenshotEnabled;
|
||||
|
||||
String assistantPackage =
|
||||
Settings.Secure.getString(contentResolver, Settings.Secure.ASSISTANT);
|
||||
mNIUPackageName = assistantPackage.split("/", 2)[0];
|
||||
}
|
||||
|
||||
protected class OverlayUICallbacksGoImpl extends OverlayUICallbacksImpl
|
||||
|
||||
@@ -117,6 +117,7 @@ import com.android.systemui.shared.system.LatencyTrackerCompat;
|
||||
import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
|
||||
import com.android.systemui.shared.system.TaskInfoCompat;
|
||||
import com.android.systemui.shared.system.TaskStackChangeListener;
|
||||
import com.android.systemui.shared.system.TaskStackChangeListeners;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.function.Consumer;
|
||||
@@ -1072,7 +1073,7 @@ public abstract class AbsSwipeUpHandler<T extends StatefulActivity<S>,
|
||||
|
||||
protected abstract HomeAnimationFactory createHomeAnimationFactory(
|
||||
ArrayList<IBinder> launchCookies, long duration, boolean isTargetTranslucent,
|
||||
RemoteAnimationTargetCompat runningTaskTarget);
|
||||
boolean appCanEnterPip, RemoteAnimationTargetCompat runningTaskTarget);
|
||||
|
||||
private final TaskStackChangeListener mActivityRestartListener = new TaskStackChangeListener() {
|
||||
@Override
|
||||
@@ -1083,7 +1084,7 @@ public abstract class AbsSwipeUpHandler<T extends StatefulActivity<S>,
|
||||
// Since this is an edge case, just cancel and relaunch with default activity
|
||||
// options (since we don't know if there's an associated app icon to launch from)
|
||||
endRunningWindowAnim(true /* cancel */);
|
||||
ActivityManagerWrapper.getInstance().unregisterTaskStackListener(
|
||||
TaskStackChangeListeners.getInstance().unregisterTaskStackListener(
|
||||
mActivityRestartListener);
|
||||
ActivityManagerWrapper.getInstance().startActivityFromRecents(task.taskId, null);
|
||||
}
|
||||
@@ -1098,7 +1099,7 @@ public abstract class AbsSwipeUpHandler<T extends StatefulActivity<S>,
|
||||
// If we are transitioning to launcher, then listen for the activity to be restarted while
|
||||
// the transition is in progress
|
||||
if (mGestureState.getEndTarget().isLauncher) {
|
||||
ActivityManagerWrapper.getInstance().registerTaskStackListener(
|
||||
TaskStackChangeListeners.getInstance().registerTaskStackListener(
|
||||
mActivityRestartListener);
|
||||
|
||||
mParallelRunningAnim = mActivityInterface.getParallelAnimationToLauncher(
|
||||
@@ -1117,13 +1118,15 @@ public abstract class AbsSwipeUpHandler<T extends StatefulActivity<S>,
|
||||
? runningTaskTarget.taskInfo.launchCookies
|
||||
: new ArrayList<>();
|
||||
boolean isTranslucent = runningTaskTarget != null && runningTaskTarget.isTranslucent;
|
||||
HomeAnimationFactory homeAnimFactory =
|
||||
createHomeAnimationFactory(cookies, duration, isTranslucent, runningTaskTarget);
|
||||
mIsSwipingPipToHome = homeAnimFactory.supportSwipePipToHome()
|
||||
boolean appCanEnterPip = !mDeviceState.isPipActive()
|
||||
&& runningTaskTarget != null
|
||||
&& runningTaskTarget.taskInfo.pictureInPictureParams != null
|
||||
&& TaskInfoCompat.isAutoEnterPipEnabled(
|
||||
runningTaskTarget.taskInfo.pictureInPictureParams);
|
||||
HomeAnimationFactory homeAnimFactory =
|
||||
createHomeAnimationFactory(cookies, duration, isTranslucent, appCanEnterPip,
|
||||
runningTaskTarget);
|
||||
mIsSwipingPipToHome = homeAnimFactory.supportSwipePipToHome() && appCanEnterPip;
|
||||
if (mIsSwipingPipToHome) {
|
||||
mSwipePipToHomeAnimator = getSwipePipToHomeAnimator(
|
||||
homeAnimFactory, runningTaskTarget, start);
|
||||
@@ -1399,7 +1402,8 @@ public abstract class AbsSwipeUpHandler<T extends StatefulActivity<S>,
|
||||
}
|
||||
|
||||
mActivityInitListener.unregister();
|
||||
ActivityManagerWrapper.getInstance().unregisterTaskStackListener(mActivityRestartListener);
|
||||
TaskStackChangeListeners.getInstance().unregisterTaskStackListener(
|
||||
mActivityRestartListener);
|
||||
mTaskSnapshot = null;
|
||||
}
|
||||
|
||||
|
||||
@@ -129,7 +129,7 @@ public class FallbackSwipeHandler extends
|
||||
|
||||
@Override
|
||||
protected HomeAnimationFactory createHomeAnimationFactory(ArrayList<IBinder> launchCookies,
|
||||
long duration, boolean isTargetTranslucent,
|
||||
long duration, boolean isTargetTranslucent, boolean appCanEnterPip,
|
||||
RemoteAnimationTargetCompat runningTaskTarget) {
|
||||
mActiveAnimationFactory = new FallbackHomeAnimationFactory(duration);
|
||||
ActivityOptions options = ActivityOptions.makeCustomAnimation(mContext, 0, 0);
|
||||
|
||||
@@ -88,7 +88,7 @@ public class LauncherSwipeHandlerV2 extends
|
||||
|
||||
@Override
|
||||
protected HomeAnimationFactory createHomeAnimationFactory(ArrayList<IBinder> launchCookies,
|
||||
long duration, boolean isTargetTranslucent,
|
||||
long duration, boolean isTargetTranslucent, boolean appCanEnterPip,
|
||||
RemoteAnimationTargetCompat runningTaskTarget) {
|
||||
if (mActivity == null) {
|
||||
mStateCallback.addChangeListener(STATE_LAUNCHER_PRESENT | STATE_HANDLER_INVALIDATED,
|
||||
@@ -108,7 +108,7 @@ public class LauncherSwipeHandlerV2 extends
|
||||
mActivity.getRootView().setForceHideBackArrow(true);
|
||||
mActivity.setHintUserWillBeActive();
|
||||
|
||||
if (!canUseWorkspaceView) {
|
||||
if (!canUseWorkspaceView || appCanEnterPip) {
|
||||
return new LauncherHomeAnimationFactory();
|
||||
}
|
||||
if (workspaceView instanceof LauncherAppWidgetHostView) {
|
||||
|
||||
@@ -33,6 +33,7 @@ import com.android.systemui.shared.recents.model.Task;
|
||||
import com.android.systemui.shared.system.ActivityManagerWrapper;
|
||||
import com.android.systemui.shared.system.KeyguardManagerCompat;
|
||||
import com.android.systemui.shared.system.TaskStackChangeListener;
|
||||
import com.android.systemui.shared.system.TaskStackChangeListeners;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
@@ -66,7 +67,7 @@ public class RecentTasksList extends TaskStackChangeListener {
|
||||
mKeyguardManager = keyguardManager;
|
||||
mChangeId = 1;
|
||||
mActivityManagerWrapper = activityManagerWrapper;
|
||||
mActivityManagerWrapper.registerTaskStackListener(this);
|
||||
TaskStackChangeListeners.getInstance().registerTaskStackListener(this);
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
|
||||
@@ -353,7 +353,8 @@ public final class RecentsActivity extends StatefulActivity<RecentsState> {
|
||||
public void startHome() {
|
||||
if (LIVE_TILE.get()) {
|
||||
RecentsView recentsView = getOverviewPanel();
|
||||
recentsView.switchToScreenshotAndFinishAnimationToRecents(this::startHomeInternal);
|
||||
recentsView.switchToScreenshot(() -> recentsView.finishRecentsAnimation(true,
|
||||
this::startHomeInternal));
|
||||
} else {
|
||||
startHomeInternal();
|
||||
}
|
||||
|
||||
@@ -15,6 +15,8 @@
|
||||
*/
|
||||
package com.android.quickstep;
|
||||
|
||||
import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
|
||||
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
|
||||
import static android.content.Intent.ACTION_USER_UNLOCKED;
|
||||
|
||||
import static com.android.launcher3.util.DisplayController.CHANGE_ALL;
|
||||
@@ -41,6 +43,7 @@ import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_S
|
||||
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING_OCCLUDED;
|
||||
|
||||
import android.app.ActivityManager;
|
||||
import android.app.ActivityTaskManager;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
@@ -50,6 +53,7 @@ import android.content.res.Resources;
|
||||
import android.graphics.Region;
|
||||
import android.net.Uri;
|
||||
import android.os.Process;
|
||||
import android.os.RemoteException;
|
||||
import android.os.SystemProperties;
|
||||
import android.os.UserManager;
|
||||
import android.provider.Settings;
|
||||
@@ -75,6 +79,8 @@ import com.android.systemui.shared.system.ActivityManagerWrapper;
|
||||
import com.android.systemui.shared.system.QuickStepContract;
|
||||
import com.android.systemui.shared.system.QuickStepContract.SystemUiStateFlags;
|
||||
import com.android.systemui.shared.system.SystemGestureExclusionListenerCompat;
|
||||
import com.android.systemui.shared.system.TaskStackChangeListener;
|
||||
import com.android.systemui.shared.system.TaskStackChangeListeners;
|
||||
|
||||
import java.io.PrintWriter;
|
||||
import java.util.ArrayList;
|
||||
@@ -96,6 +102,8 @@ public class RecentsAnimationDeviceState implements
|
||||
private final DisplayController mDisplayController;
|
||||
private final int mDisplayId;
|
||||
private final RotationTouchHelper mRotationTouchHelper;
|
||||
private final TaskStackChangeListener mPipListener;
|
||||
private final List<ComponentName> mGestureBlockedActivities;
|
||||
|
||||
private final ArrayList<Runnable> mOnDestroyActions = new ArrayList<>();
|
||||
|
||||
@@ -106,9 +114,11 @@ public class RecentsAnimationDeviceState implements
|
||||
private final Region mDeferredGestureRegion = new Region();
|
||||
private boolean mAssistantAvailable;
|
||||
private float mAssistantVisibility;
|
||||
private boolean mIsUserSetupComplete;
|
||||
private boolean mIsOneHandedModeEnabled;
|
||||
private boolean mIsSwipeToNotificationEnabled;
|
||||
private final boolean mIsOneHandedModeSupported;
|
||||
private boolean mPipIsActive;
|
||||
|
||||
private boolean mIsUserUnlocked;
|
||||
private final ArrayList<Runnable> mUserUnlockedActions = new ArrayList<>();
|
||||
@@ -125,10 +135,6 @@ public class RecentsAnimationDeviceState implements
|
||||
private Region mExclusionRegion;
|
||||
private SystemGestureExclusionListenerCompat mExclusionListener;
|
||||
|
||||
private final List<ComponentName> mGestureBlockedActivities;
|
||||
|
||||
private boolean mIsUserSetupComplete;
|
||||
|
||||
public RecentsAnimationDeviceState(Context context) {
|
||||
this(context, false);
|
||||
}
|
||||
@@ -204,7 +210,6 @@ public class RecentsAnimationDeviceState implements
|
||||
mIsOneHandedModeEnabled = false;
|
||||
}
|
||||
|
||||
|
||||
Uri swipeBottomNotificationUri =
|
||||
Settings.Secure.getUriFor(ONE_HANDED_SWIPE_BOTTOM_TO_NOTIFICATION_ENABLED);
|
||||
SettingsCache.OnChangeListener onChangeListener =
|
||||
@@ -220,6 +225,27 @@ public class RecentsAnimationDeviceState implements
|
||||
settingsCache.register(setupCompleteUri, userSetupChangeListener);
|
||||
runOnDestroy(() -> settingsCache.unregister(setupCompleteUri, userSetupChangeListener));
|
||||
}
|
||||
|
||||
try {
|
||||
mPipIsActive = ActivityTaskManager.getService().getRootTaskInfo(
|
||||
WINDOWING_MODE_PINNED, ACTIVITY_TYPE_UNDEFINED) != null;
|
||||
} catch (RemoteException e) {
|
||||
// Do nothing
|
||||
}
|
||||
mPipListener = new TaskStackChangeListener() {
|
||||
@Override
|
||||
public void onActivityPinned(String packageName, int userId, int taskId, int stackId) {
|
||||
mPipIsActive = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityUnpinned() {
|
||||
mPipIsActive = false;
|
||||
}
|
||||
};
|
||||
TaskStackChangeListeners.getInstance().registerTaskStackListener(mPipListener);
|
||||
runOnDestroy(() ->
|
||||
TaskStackChangeListeners.getInstance().unregisterTaskStackListener(mPipListener));
|
||||
}
|
||||
|
||||
private void runOnDestroy(Runnable action) {
|
||||
@@ -579,6 +605,10 @@ public class RecentsAnimationDeviceState implements
|
||||
return mIsSwipeToNotificationEnabled;
|
||||
}
|
||||
|
||||
public boolean isPipActive() {
|
||||
return mPipIsActive;
|
||||
}
|
||||
|
||||
public RotationTouchHelper getRotationTouchHelper() {
|
||||
return mRotationTouchHelper;
|
||||
}
|
||||
@@ -596,6 +626,7 @@ public class RecentsAnimationDeviceState implements
|
||||
pw.println(" isOneHandedModeEnabled=" + mIsOneHandedModeEnabled);
|
||||
pw.println(" isSwipeToNotificationEnabled=" + mIsSwipeToNotificationEnabled);
|
||||
pw.println(" deferredGestureRegion=" + mDeferredGestureRegion);
|
||||
pw.println(" pipIsActive=" + mPipIsActive);
|
||||
mRotationTouchHelper.dump(pw);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,6 +39,7 @@ import com.android.systemui.shared.recents.model.ThumbnailData;
|
||||
import com.android.systemui.shared.system.ActivityManagerWrapper;
|
||||
import com.android.systemui.shared.system.KeyguardManagerCompat;
|
||||
import com.android.systemui.shared.system.TaskStackChangeListener;
|
||||
import com.android.systemui.shared.system.TaskStackChangeListeners;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
@@ -75,7 +76,7 @@ public class RecentsModel extends TaskStackChangeListener implements IconChangeL
|
||||
mIconCache = new TaskIconCache(context, RECENTS_MODEL_EXECUTOR, iconProvider);
|
||||
mThumbnailCache = new TaskThumbnailCache(context, RECENTS_MODEL_EXECUTOR);
|
||||
|
||||
ActivityManagerWrapper.getInstance().registerTaskStackListener(this);
|
||||
TaskStackChangeListeners.getInstance().registerTaskStackListener(this);
|
||||
iconProvider.registerIconChangeListener(this, MAIN_EXECUTOR.getHandler());
|
||||
}
|
||||
|
||||
|
||||
@@ -38,6 +38,7 @@ import com.android.quickstep.util.RecentsOrientedState;
|
||||
import com.android.systemui.shared.system.ActivityManagerWrapper;
|
||||
import com.android.systemui.shared.system.QuickStepContract;
|
||||
import com.android.systemui.shared.system.TaskStackChangeListener;
|
||||
import com.android.systemui.shared.system.TaskStackChangeListeners;
|
||||
|
||||
import java.io.PrintWriter;
|
||||
import java.util.ArrayList;
|
||||
@@ -178,14 +179,14 @@ public class RotationTouchHelper implements
|
||||
}
|
||||
|
||||
private void setupOrientationSwipeHandler() {
|
||||
ActivityManagerWrapper.getInstance().registerTaskStackListener(mFrozenTaskListener);
|
||||
mOnDestroyFrozenTaskRunnable = () -> ActivityManagerWrapper.getInstance()
|
||||
TaskStackChangeListeners.getInstance().registerTaskStackListener(mFrozenTaskListener);
|
||||
mOnDestroyFrozenTaskRunnable = () -> TaskStackChangeListeners.getInstance()
|
||||
.unregisterTaskStackListener(mFrozenTaskListener);
|
||||
runOnDestroy(mOnDestroyFrozenTaskRunnable);
|
||||
}
|
||||
|
||||
private void destroyOrientationSwipeHandlerCallback() {
|
||||
ActivityManagerWrapper.getInstance().unregisterTaskStackListener(mFrozenTaskListener);
|
||||
TaskStackChangeListeners.getInstance().unregisterTaskStackListener(mFrozenTaskListener);
|
||||
mOnDestroyActions.remove(mOnDestroyFrozenTaskRunnable);
|
||||
}
|
||||
|
||||
|
||||
@@ -39,6 +39,7 @@ import com.android.systemui.shared.system.ActivityOptionsCompat;
|
||||
import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
|
||||
import com.android.systemui.shared.system.RemoteTransitionCompat;
|
||||
import com.android.systemui.shared.system.TaskStackChangeListener;
|
||||
import com.android.systemui.shared.system.TaskStackChangeListeners;
|
||||
|
||||
public class TaskAnimationManager implements RecentsAnimationCallbacks.RecentsAnimationListener {
|
||||
public static final boolean ENABLE_SHELL_TRANSITIONS =
|
||||
@@ -58,7 +59,7 @@ public class TaskAnimationManager implements RecentsAnimationCallbacks.RecentsAn
|
||||
public void onActivityRestartAttempt(ActivityManager.RunningTaskInfo task,
|
||||
boolean homeTaskVisible, boolean clearedTask, boolean wasVisible) {
|
||||
if (mLastGestureState == null) {
|
||||
ActivityManagerWrapper.getInstance().unregisterTaskStackListener(
|
||||
TaskStackChangeListeners.getInstance().unregisterTaskStackListener(
|
||||
mLiveTileRestartListener);
|
||||
return;
|
||||
}
|
||||
@@ -68,7 +69,7 @@ public class TaskAnimationManager implements RecentsAnimationCallbacks.RecentsAn
|
||||
RecentsView recentsView = activityInterface.getCreatedActivity().getOverviewPanel();
|
||||
if (recentsView != null) {
|
||||
recentsView.launchSideTaskInLiveTileModeForRestartedApp(task.taskId);
|
||||
ActivityManagerWrapper.getInstance().unregisterTaskStackListener(
|
||||
TaskStackChangeListeners.getInstance().unregisterTaskStackListener(
|
||||
mLiveTileRestartListener);
|
||||
}
|
||||
}
|
||||
@@ -197,7 +198,7 @@ public class TaskAnimationManager implements RecentsAnimationCallbacks.RecentsAn
|
||||
}
|
||||
|
||||
public void enableLiveTileRestartListener() {
|
||||
ActivityManagerWrapper.getInstance().registerTaskStackListener(mLiveTileRestartListener);
|
||||
TaskStackChangeListeners.getInstance().registerTaskStackListener(mLiveTileRestartListener);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -241,7 +242,7 @@ public class TaskAnimationManager implements RecentsAnimationCallbacks.RecentsAn
|
||||
mLiveTileCleanUpHandler.run();
|
||||
mLiveTileCleanUpHandler = null;
|
||||
}
|
||||
ActivityManagerWrapper.getInstance().unregisterTaskStackListener(mLiveTileRestartListener);
|
||||
TaskStackChangeListeners.getInstance().unregisterTaskStackListener(mLiveTileRestartListener);
|
||||
|
||||
// Release all the target leashes
|
||||
if (mTargets != null) {
|
||||
|
||||
@@ -26,6 +26,7 @@ import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
|
||||
import android.app.Activity;
|
||||
import android.app.ActivityOptions;
|
||||
import android.app.prediction.AppTarget;
|
||||
import android.content.ActivityNotFoundException;
|
||||
import android.content.ClipData;
|
||||
import android.content.ClipDescription;
|
||||
import android.content.ComponentName;
|
||||
@@ -68,6 +69,7 @@ public class ImageActionUtils {
|
||||
private static final long FILE_LIFE = 1000L /*ms*/ * 60L /*s*/ * 60L /*m*/ * 24L /*h*/;
|
||||
private static final String SUB_FOLDER = "Overview";
|
||||
private static final String BASE_NAME = "overview_image_";
|
||||
private static final String TAG = "ImageActionUtils";
|
||||
|
||||
/**
|
||||
* Saves screenshot to location determine by SystemUiProxy
|
||||
@@ -154,11 +156,15 @@ public class ImageActionUtils {
|
||||
Intent intent, BiFunction<Uri, Intent, Intent[]> uriToIntentMap, String tag) {
|
||||
Intent[] intents = uriToIntentMap.apply(getImageUri(bitmap, crop, context, tag), intent);
|
||||
|
||||
// Work around b/159412574
|
||||
if (intents.length == 1) {
|
||||
context.startActivity(intents[0]);
|
||||
} else {
|
||||
context.startActivities(intents);
|
||||
try {
|
||||
// Work around b/159412574
|
||||
if (intents.length == 1) {
|
||||
context.startActivity(intents[0]);
|
||||
} else {
|
||||
context.startActivities(intents);
|
||||
}
|
||||
} catch (ActivityNotFoundException e) {
|
||||
Log.e(TAG, "No activity found to receive image intent");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -121,7 +121,7 @@ public class MotionPauseDetector {
|
||||
mForcePauseTimeout.setAlarm(mMakePauseHarderToTrigger
|
||||
? HARDER_TRIGGER_TIMEOUT
|
||||
: FORCE_PAUSE_TIMEOUT);
|
||||
float newVelocity = mVelocityProvider.addMotionEvent(ev, pointerIndex);
|
||||
float newVelocity = mVelocityProvider.addMotionEvent(ev, ev.getPointerId(pointerIndex));
|
||||
if (mPreviousVelocity != null) {
|
||||
checkMotionPaused(newVelocity, mPreviousVelocity, ev.getEventTime());
|
||||
}
|
||||
|
||||
@@ -121,6 +121,15 @@ public class SwipePipToHomeAnimator extends ValueAnimator {
|
||||
mDestinationBoundsAnimation.set(mDestinationBounds);
|
||||
mSurfaceTransactionHelper = new PipSurfaceTransactionHelper(cornerRadius);
|
||||
|
||||
if (sourceRectHint != null && (sourceRectHint.width() < destinationBounds.width()
|
||||
|| sourceRectHint.height() < destinationBounds.height())) {
|
||||
// This is a situation in which the source hint rect on at least one axis is smaller
|
||||
// than the destination bounds, which presents a problem because we would have to scale
|
||||
// up that axis to fit the bounds. So instead, just fallback to the non-source hint
|
||||
// animation in this case.
|
||||
sourceRectHint = null;
|
||||
}
|
||||
|
||||
if (sourceRectHint == null) {
|
||||
mSourceHintRectInsets = null;
|
||||
|
||||
|
||||
@@ -2357,7 +2357,8 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T
|
||||
public void accept(Boolean success) {
|
||||
if (LIVE_TILE.get() && mEnableDrawingLiveTile && taskView.isRunningTask()
|
||||
&& success) {
|
||||
finishRecentsAnimation(true /* toHome */, () -> onEnd(success));
|
||||
finishRecentsAnimation(true /* toRecents */, false /* shouldPip */,
|
||||
() -> onEnd(success));
|
||||
} else {
|
||||
onEnd(success);
|
||||
}
|
||||
@@ -2368,7 +2369,8 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T
|
||||
if (success) {
|
||||
if (shouldRemoveTask) {
|
||||
if (taskView.getTask() != null) {
|
||||
switchToScreenshotAndFinishAnimationToRecents(() -> {
|
||||
finishRecentsAnimation(true /* toRecents */, false /* shouldPip */,
|
||||
() -> {
|
||||
UI_HELPER_EXECUTOR.getHandler().postDelayed(() ->
|
||||
ActivityManagerWrapper.getInstance().removeTask(
|
||||
taskView.getTask().key.id),
|
||||
@@ -2478,7 +2480,7 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T
|
||||
mPendingAnimation.addEndListener(isSuccess -> {
|
||||
if (isSuccess) {
|
||||
// Remove all the task views now
|
||||
switchToScreenshotAndFinishAnimationToRecents(() -> {
|
||||
finishRecentsAnimation(true /* toRecents */, false /* shouldPip */, () -> {
|
||||
UI_HELPER_EXECUTOR.getHandler().postDelayed(
|
||||
ActivityManagerWrapper.getInstance()::removeAllRecentTasks,
|
||||
REMOVE_TASK_WAIT_FOR_APP_STOP_MS);
|
||||
@@ -2639,7 +2641,9 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T
|
||||
protected void onConfigurationChanged(Configuration newConfig) {
|
||||
super.onConfigurationChanged(newConfig);
|
||||
if (LIVE_TILE.get() && mEnableDrawingLiveTile && newConfig.orientation != mOrientation) {
|
||||
switchToScreenshotAndFinishAnimationToRecents(this::updateRecentsRotation);
|
||||
switchToScreenshot(
|
||||
() -> finishRecentsAnimation(true /* toRecents */, false /* showPip */,
|
||||
this::updateRecentsRotation));
|
||||
mEnableDrawingLiveTile = false;
|
||||
} else {
|
||||
updateRecentsRotation();
|
||||
@@ -3632,10 +3636,6 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T
|
||||
}
|
||||
}
|
||||
|
||||
public void switchToScreenshotAndFinishAnimationToRecents(Runnable onFinishRunnable) {
|
||||
switchToScreenshot(() -> finishRecentsAnimation(true /* toRecents */, onFinishRunnable));
|
||||
}
|
||||
|
||||
/**
|
||||
* Switch the current running task view to static snapshot mode,
|
||||
* capturing the snapshot at the same time.
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Copyright (C) 2021 The Android Open Source Project
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:color="@android:color/system_accent2_100"/>
|
||||
</selector>
|
||||
@@ -0,0 +1,19 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Copyright (C) 2021 The Android Open Source Project
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:color="@android:color/system_neutral1_50" android:state_selected="true"/>
|
||||
<item android:color="@android:color/system_neutral2_700"/>
|
||||
</selector>
|
||||
@@ -0,0 +1,18 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Copyright (C) 2021 The Android Open Source Project
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:color="@android:color/system_neutral2_100"/>
|
||||
</selector>
|
||||
@@ -0,0 +1,18 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Copyright (C) 2021 The Android Open Source Project
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:color="#BFEBE3"/>
|
||||
</selector>
|
||||
@@ -0,0 +1,19 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Copyright (C) 2021 The Android Open Source Project
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:color="#F0F0F0" android:state_selected="true"/>
|
||||
<item android:color="#464646"/>
|
||||
</selector>
|
||||
@@ -0,0 +1,18 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Copyright (C) 2021 The Android Open Source Project
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:color="#E2E2E2"/>
|
||||
</selector>
|
||||
@@ -0,0 +1,18 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Copyright (C) 2021 The Android Open Source Project
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:color="@android:color/system_accent1_100"/>
|
||||
</selector>
|
||||
@@ -0,0 +1,19 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Copyright (C) 2021 The Android Open Source Project
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:color="@android:color/system_neutral1_900" android:state_selected="true"/>
|
||||
<item android:color="@android:color/system_neutral2_700"/>
|
||||
</selector>
|
||||
@@ -0,0 +1,18 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Copyright (C) 2021 The Android Open Source Project
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:color="@android:color/system_neutral1_500" android:lStar="97" />
|
||||
</selector>
|
||||
@@ -13,7 +13,6 @@
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:androidprv="http://schemas.android.com/apk/prv/res/android">
|
||||
<item android:color="?androidprv:attr/colorAccentPrimary"/>
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:color="#8DF5E3"/>
|
||||
</selector>
|
||||
@@ -14,6 +14,6 @@
|
||||
limitations under the License.
|
||||
-->
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:color="@android:color/black" android:state_selected="true"/>
|
||||
<item android:color="?android:attr/textColorTertiary"/>
|
||||
<item android:color="#1B1B1B" android:state_selected="true"/>
|
||||
<item android:color="#464646"/>
|
||||
</selector>
|
||||
@@ -0,0 +1,18 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Copyright (C) 2021 The Android Open Source Project
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:color="#F6F6F6"/>
|
||||
</selector>
|
||||
@@ -13,14 +13,12 @@
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
<layer-list xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:androidprv="http://schemas.android.com/apk/prv/res/android">
|
||||
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item
|
||||
android:top="@dimen/all_apps_tabs_vertical_padding"
|
||||
android:bottom="@dimen/all_apps_tabs_vertical_padding
|
||||
">
|
||||
android:bottom="@dimen/all_apps_tabs_vertical_padding">
|
||||
<shape android:shape="rectangle">
|
||||
<solid android:color="?androidprv:attr/colorSurface" />
|
||||
<solid android:color="@color/all_apps_tabs_background" />
|
||||
<corners android:radius="@dimen/all_apps_header_pill_corner_radius" />
|
||||
</shape>
|
||||
</item>
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Copyright (C) 2021 The Android Open Source Project
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:state_enabled="false">
|
||||
<shape android:shape="rectangle">
|
||||
<corners android:radius="@dimen/work_fab_radius" />
|
||||
<solid android:color="?android:attr/colorControlHighlight" />
|
||||
<padding android:left="@dimen/work_fab_radius" android:right="@dimen/work_fab_radius" />
|
||||
</shape>
|
||||
</item>
|
||||
<item>
|
||||
<shape android:shape="rectangle">
|
||||
<corners android:radius="@dimen/work_fab_radius" />
|
||||
<solid android:color="?android:attr/colorAccent" />
|
||||
<padding android:left="@dimen/work_fab_radius" android:right="@dimen/work_fab_radius" />
|
||||
</shape>
|
||||
</item>
|
||||
</selector>
|
||||
@@ -0,0 +1,33 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- 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.
|
||||
-->
|
||||
<com.android.launcher3.allapps.WorkModeSwitch
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/work_mode_toggle"
|
||||
android:layout_alignParentBottom="true"
|
||||
android:layout_alignParentEnd="true"
|
||||
android:layout_height="@dimen/work_fab_height"
|
||||
android:layout_width="wrap_content"
|
||||
android:gravity="center"
|
||||
android:includeFontPadding="false"
|
||||
android:drawableTint="@android:color/white"
|
||||
android:textColor="@android:color/white"
|
||||
android:background="@drawable/work_apps_toggle_background"
|
||||
android:drawablePadding="16dp"
|
||||
android:drawableStart="@drawable/ic_corp_off"
|
||||
android:elevation="10dp"
|
||||
android:layout_marginBottom="@dimen/work_fab_margin"
|
||||
android:layout_marginEnd="@dimen/work_fab_margin"
|
||||
android:text="@string/work_apps_pause_btn_text" />
|
||||
@@ -1,5 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Copyright (C) 2017 The Android Open Source Project
|
||||
<?xml version="1.0" encoding="utf-8"?><!-- 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.
|
||||
@@ -13,8 +12,7 @@
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
<com.android.launcher3.allapps.WorkModeSwitch
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
<com.android.launcher3.allapps.WorkModeSwitch xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
style="@style/PrimaryHeadline"
|
||||
@@ -35,5 +33,4 @@
|
||||
android:paddingBottom="@dimen/work_profile_footer_padding"
|
||||
android:paddingLeft="@dimen/work_profile_footer_padding"
|
||||
android:paddingRight="@dimen/work_profile_footer_padding"
|
||||
android:paddingTop="@dimen/work_profile_footer_padding"
|
||||
/>
|
||||
android:paddingTop="@dimen/work_profile_footer_padding" />
|
||||
|
||||
@@ -1,61 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?><!-- Copyright (C) 2020 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.
|
||||
-->
|
||||
<com.android.launcher3.views.WorkEduView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="bottom"
|
||||
android:gravity="bottom"
|
||||
android:orientation="vertical">
|
||||
|
||||
<View
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="32dp"
|
||||
android:background="@drawable/bottom_sheet_top_border"
|
||||
android:backgroundTint="?attr/eduHalfSheetBGColor" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/view_wrapper"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="?attr/eduHalfSheetBGColor"
|
||||
android:orientation="vertical"
|
||||
android:paddingLeft="@dimen/bottom_sheet_edu_padding"
|
||||
android:paddingRight="@dimen/bottom_sheet_edu_padding">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/content_text"
|
||||
style="@style/TextHeadline"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="48dp"
|
||||
android:layout_marginBottom="48dp"
|
||||
android:gravity="center"
|
||||
android:text="@string/work_profile_edu_personal_apps"
|
||||
android:textAlignment="center"
|
||||
android:textColor="@android:color/white"
|
||||
android:textSize="20sp" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/proceed"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="48dp"
|
||||
android:layout_gravity="end"
|
||||
android:background="?android:attr/selectableItemBackground"
|
||||
android:gravity="center"
|
||||
android:text="@string/work_profile_edu_next"
|
||||
android:textAlignment="center"
|
||||
android:textColor="@android:color/white" />
|
||||
</LinearLayout>
|
||||
</com.android.launcher3.views.WorkEduView>
|
||||
@@ -116,6 +116,10 @@
|
||||
|
||||
<dimen name="all_apps_divider_margin_vertical">8dp</dimen>
|
||||
|
||||
<!-- Floating action button inside work tab to toggle work profile -->
|
||||
<dimen name="work_fab_height">48dp</dimen>
|
||||
<dimen name="work_fab_radius">24dp</dimen>
|
||||
<dimen name="work_fab_margin">18dp</dimen>
|
||||
<dimen name="work_profile_footer_padding">20dp</dimen>
|
||||
<dimen name="work_profile_footer_text_size">16sp</dimen>
|
||||
|
||||
|
||||
+6
-13
@@ -388,37 +388,30 @@
|
||||
|
||||
<!-- This string is in the work profile tab when a user has All Apps open on their phone. This is a label for a toggle to turn the work profile on and off. "Work profile" means a separate profile on a user's phone that's specifically for their work apps and managed by their company. "Work" is used as an adjective.-->
|
||||
<string name="work_profile_toggle_label">Work profile</string>
|
||||
<!--- User onboarding title for personal apps -->
|
||||
<string name="work_profile_edu_personal_apps">Personal data is separate & hidden from work apps</string>
|
||||
<!--- User onboarding title for work profile apps -->
|
||||
<string name="work_profile_edu_work_apps">Work apps & data are visible to your IT admin</string>
|
||||
<!-- Action label to proceed to the next work profile edu section-->
|
||||
<string name="work_profile_edu_next">Next</string>
|
||||
<string name="work_profile_edu_work_apps">Work apps are badged andare visible to your IT admin</string>
|
||||
<!-- Action label to finish work profile edu-->
|
||||
<string name="work_profile_edu_accept">Got it</string>
|
||||
|
||||
<!--- heading shown when user opens work apps tab while work apps are paused -->
|
||||
<string name="work_apps_paused_title">Work profile is paused</string>
|
||||
<string name="work_apps_paused_title">Work apps are off</string>
|
||||
<!--- body shown when user opens work apps tab while work apps are paused -->
|
||||
<string name="work_apps_paused_body">Work apps can’t send you notifications, use your battery, or access your location</string>
|
||||
<string name="work_apps_paused_body">Your work apps can’t send you notifications, use your battery, or access your location</string>
|
||||
<!-- content description for paused work apps list -->
|
||||
<string name="work_apps_paused_content_description">Work profile is paused. Work apps can’t send you notifications, use your battery, or access your location</string>
|
||||
<string name="work_apps_paused_content_description">Work apps are off. Your work apps can’t send you notifications, use your battery, or access your location</string>
|
||||
<!-- string shown in educational banner about work profile -->
|
||||
<string name="work_apps_paused_edu_banner">Work apps are badged and visible to your IT admin</string>
|
||||
<!-- button string shown to dismiss work tab education -->
|
||||
<string name="work_apps_paused_edu_accept">Got it</string>
|
||||
|
||||
<!-- button string shown pause work profile -->
|
||||
<string name="work_apps_pause_btn_text">Pause work apps</string>
|
||||
<string name="work_apps_pause_btn_text">Turn off work apps</string>
|
||||
<!-- button string shown enable work profile -->
|
||||
<string name="work_apps_enable_btn_text">Turn on</string>
|
||||
<string name="work_apps_enable_btn_text">Turn on work apps</string>
|
||||
|
||||
<!-- A hint shown in launcher settings develop options filter box -->
|
||||
<string name="developer_options_filter_hint">Filter</string>
|
||||
|
||||
<!-- A tip shown pointing at work toggle -->
|
||||
<string name="work_switch_tip">Pause work apps and notifications</string>
|
||||
|
||||
<!-- Failed action error message: e.g. Failed: Pause -->
|
||||
<string name="remote_action_failed">Failed: <xliff:g id="what" example="Pause">%1$s</xliff:g></string>
|
||||
</resources>
|
||||
|
||||
@@ -18,6 +18,9 @@ package com.android.launcher3;
|
||||
|
||||
import static android.view.MotionEvent.ACTION_DOWN;
|
||||
|
||||
import static com.android.launcher3.CellLayout.FOLDER;
|
||||
import static com.android.launcher3.CellLayout.WORKSPACE;
|
||||
|
||||
import android.app.WallpaperManager;
|
||||
import android.content.Context;
|
||||
import android.graphics.Rect;
|
||||
@@ -124,21 +127,27 @@ public class ShortcutAndWidgetContainer extends ViewGroup implements FolderIcon.
|
||||
|
||||
public void measureChild(View child) {
|
||||
CellLayout.LayoutParams lp = (CellLayout.LayoutParams) child.getLayoutParams();
|
||||
final DeviceProfile profile = mActivity.getDeviceProfile();
|
||||
final DeviceProfile dp = mActivity.getDeviceProfile();
|
||||
|
||||
if (child instanceof LauncherAppWidgetHostView) {
|
||||
((LauncherAppWidgetHostView) child).getWidgetInset(profile, mTempRect);
|
||||
((LauncherAppWidgetHostView) child).getWidgetInset(dp, mTempRect);
|
||||
lp.setup(mCellWidth, mCellHeight, invertLayoutHorizontally(), mCountX, mCountY,
|
||||
profile.appWidgetScale.x, profile.appWidgetScale.y, mBorderSpacing, mTempRect);
|
||||
dp.appWidgetScale.x, dp.appWidgetScale.y, mBorderSpacing, mTempRect);
|
||||
} else {
|
||||
lp.setup(mCellWidth, mCellHeight, invertLayoutHorizontally(), mCountX, mCountY,
|
||||
mBorderSpacing, null);
|
||||
// Center the icon/folder
|
||||
int cHeight = getCellContentHeight();
|
||||
int cellPaddingY = (int) Math.max(0, ((lp.height - cHeight) / 2f));
|
||||
int cellPaddingX = mContainerType == CellLayout.WORKSPACE
|
||||
? profile.workspaceCellPaddingXPx
|
||||
: (int) (profile.edgeMarginPx / 2f);
|
||||
|
||||
// No need to add padding when cell layout border spacing is present.
|
||||
boolean noPadding = (dp.cellLayoutBorderSpacingPx > 0 && mContainerType == WORKSPACE)
|
||||
|| (dp.folderCellLayoutBorderSpacingPx > 0 && mContainerType == FOLDER);
|
||||
int cellPaddingX = noPadding
|
||||
? 0
|
||||
: mContainerType == WORKSPACE
|
||||
? dp.workspaceCellPaddingXPx
|
||||
: (int) (dp.edgeMarginPx / 2f);
|
||||
child.setPadding(cellPaddingX, cellPaddingY, cellPaddingX, 0);
|
||||
}
|
||||
int childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(lp.width, MeasureSpec.EXACTLY);
|
||||
|
||||
@@ -228,7 +228,7 @@ public class AllAppsContainerView extends SpringRelativeLayout implements DragSo
|
||||
}
|
||||
|
||||
private void resetWorkProfile() {
|
||||
mWorkModeSwitch.update(!mAllAppsStore.hasModelFlag(FLAG_QUIET_MODE_ENABLED));
|
||||
mWorkModeSwitch.updateCurrentState(!mAllAppsStore.hasModelFlag(FLAG_QUIET_MODE_ENABLED));
|
||||
mAH[AdapterHolder.WORK].setupOverlay();
|
||||
mAH[AdapterHolder.WORK].applyPadding();
|
||||
}
|
||||
@@ -482,7 +482,7 @@ public class AllAppsContainerView extends SpringRelativeLayout implements DragSo
|
||||
private void setupWorkToggle() {
|
||||
if (Utilities.ATLEAST_P) {
|
||||
mWorkModeSwitch = (WorkModeSwitch) mLauncher.getLayoutInflater().inflate(
|
||||
R.layout.work_mode_switch, this, false);
|
||||
R.layout.work_mode_fab, this, false);
|
||||
this.addView(mWorkModeSwitch);
|
||||
mWorkModeSwitch.setInsets(mInsets);
|
||||
mWorkModeSwitch.post(this::resetWorkProfile);
|
||||
|
||||
@@ -25,7 +25,6 @@ import android.view.MotionEvent;
|
||||
import com.android.launcher3.Launcher;
|
||||
import com.android.launcher3.LauncherState;
|
||||
import com.android.launcher3.statemanager.StateManager.StateListener;
|
||||
import com.android.launcher3.views.WorkEduView;
|
||||
|
||||
/**
|
||||
* AllAppsContainerView with launcher specific callbacks
|
||||
@@ -88,13 +87,6 @@ public class LauncherAllAppsContainerView extends AllAppsContainerView {
|
||||
@Override
|
||||
public void onActivePageChanged(int currentActivePage) {
|
||||
super.onActivePageChanged(currentActivePage);
|
||||
if (mUsingTabs) {
|
||||
if (currentActivePage == AdapterHolder.WORK) {
|
||||
WorkEduView.showWorkEduIfNeeded(mLauncher);
|
||||
} else {
|
||||
mWorkTabListener = WorkEduView.showEduFlowIfNeeded(mLauncher, mWorkTabListener);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -15,108 +15,61 @@
|
||||
*/
|
||||
package com.android.launcher3.allapps;
|
||||
|
||||
import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.graphics.Rect;
|
||||
import android.os.AsyncTask;
|
||||
import android.os.Build;
|
||||
import android.os.Process;
|
||||
import android.os.UserHandle;
|
||||
import android.os.UserManager;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.ViewConfiguration;
|
||||
import android.widget.Switch;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.Button;
|
||||
|
||||
import androidx.annotation.RequiresApi;
|
||||
|
||||
import com.android.launcher3.Insettable;
|
||||
import com.android.launcher3.R;
|
||||
import com.android.launcher3.Utilities;
|
||||
import com.android.launcher3.pm.UserCache;
|
||||
import com.android.launcher3.views.ArrowTipView;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
|
||||
/**
|
||||
* Work profile toggle switch shown at the bottom of AllApps work tab
|
||||
*/
|
||||
public class WorkModeSwitch extends Switch implements Insettable {
|
||||
|
||||
private static final int WORK_TIP_THRESHOLD = 2;
|
||||
public static final String KEY_WORK_TIP_COUNTER = "worked_tip_counter";
|
||||
public class WorkModeSwitch extends Button implements Insettable, View.OnClickListener {
|
||||
|
||||
private Rect mInsets = new Rect();
|
||||
|
||||
private final float[] mTouch = new float[2];
|
||||
private int mTouchSlop;
|
||||
private boolean mWorkEnabled;
|
||||
|
||||
public WorkModeSwitch(Context context) {
|
||||
super(context);
|
||||
init();
|
||||
this(context, null, 0);
|
||||
}
|
||||
|
||||
public WorkModeSwitch(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
init();
|
||||
this(context, attrs, 0);
|
||||
}
|
||||
|
||||
public WorkModeSwitch(Context context, AttributeSet attrs, int defStyleAttr) {
|
||||
super(context, attrs, defStyleAttr);
|
||||
init();
|
||||
}
|
||||
|
||||
private void init() {
|
||||
ViewConfiguration viewConfiguration = ViewConfiguration.get(getContext());
|
||||
mTouchSlop = viewConfiguration.getScaledTouchSlop();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setChecked(boolean checked) { }
|
||||
|
||||
@Override
|
||||
public void toggle() {
|
||||
// don't show tip if user uses toggle
|
||||
Utilities.getPrefs(getContext()).edit().putInt(KEY_WORK_TIP_COUNTER, -1).apply();
|
||||
trySetQuietModeEnabledToAllProfilesAsync(isChecked());
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the enabled or disabled state of the button
|
||||
* @param isChecked
|
||||
*/
|
||||
public void update(boolean isChecked) {
|
||||
super.setChecked(isChecked);
|
||||
setCompoundDrawablesRelativeWithIntrinsicBounds(
|
||||
isChecked ? R.drawable.ic_corp : R.drawable.ic_corp_off, 0, 0, 0);
|
||||
setEnabled(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onTouchEvent(MotionEvent ev) {
|
||||
if (ev.getActionMasked() == MotionEvent.ACTION_DOWN) {
|
||||
mTouch[0] = ev.getX();
|
||||
mTouch[1] = ev.getY();
|
||||
} else if (ev.getActionMasked() == MotionEvent.ACTION_MOVE) {
|
||||
if (Math.abs(mTouch[0] - ev.getX()) > mTouchSlop
|
||||
|| Math.abs(mTouch[1] - ev.getY()) > mTouchSlop) {
|
||||
int action = ev.getAction();
|
||||
ev.setAction(MotionEvent.ACTION_CANCEL);
|
||||
super.onTouchEvent(ev);
|
||||
ev.setAction(action);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return super.onTouchEvent(ev);
|
||||
}
|
||||
|
||||
private void trySetQuietModeEnabledToAllProfilesAsync(boolean enabled) {
|
||||
new SetQuietModeEnabledAsyncTask(enabled, new WeakReference<>(this)).execute();
|
||||
protected void onFinishInflate() {
|
||||
super.onFinishInflate();
|
||||
setOnClickListener(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setInsets(Rect insets) {
|
||||
int bottomInset = insets.bottom - mInsets.bottom;
|
||||
mInsets.set(insets);
|
||||
setPadding(getPaddingLeft(), getPaddingTop(), getPaddingRight(),
|
||||
getPaddingBottom() + bottomInset);
|
||||
ViewGroup.MarginLayoutParams marginLayoutParams =
|
||||
(ViewGroup.MarginLayoutParams) getLayoutParams();
|
||||
if (marginLayoutParams != null) {
|
||||
marginLayoutParams.bottomMargin = bottomInset + marginLayoutParams.bottomMargin;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -125,78 +78,44 @@ public class WorkModeSwitch extends Switch implements Insettable {
|
||||
public void setWorkTabVisible(boolean workTabVisible) {
|
||||
clearAnimation();
|
||||
if (workTabVisible) {
|
||||
setEnabled(true);
|
||||
setVisibility(VISIBLE);
|
||||
setAlpha(0);
|
||||
animate().alpha(1).start();
|
||||
showTipIfNeeded();
|
||||
} else {
|
||||
animate().alpha(0).withEndAction(() -> this.setVisibility(GONE)).start();
|
||||
}
|
||||
}
|
||||
|
||||
private static final class SetQuietModeEnabledAsyncTask
|
||||
extends AsyncTask<Void, Void, Boolean> {
|
||||
|
||||
private final boolean enabled;
|
||||
private final WeakReference<WorkModeSwitch> switchWeakReference;
|
||||
|
||||
SetQuietModeEnabledAsyncTask(boolean enabled,
|
||||
WeakReference<WorkModeSwitch> switchWeakReference) {
|
||||
this.enabled = enabled;
|
||||
this.switchWeakReference = switchWeakReference;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPreExecute() {
|
||||
super.onPreExecute();
|
||||
WorkModeSwitch workModeSwitch = switchWeakReference.get();
|
||||
if (workModeSwitch != null) {
|
||||
workModeSwitch.setEnabled(false);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Boolean doInBackground(Void... voids) {
|
||||
WorkModeSwitch workModeSwitch = switchWeakReference.get();
|
||||
if (workModeSwitch == null || !Utilities.ATLEAST_P) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Context context = workModeSwitch.getContext();
|
||||
UserManager userManager = context.getSystemService(UserManager.class);
|
||||
boolean showConfirm = false;
|
||||
for (UserHandle userProfile : UserCache.INSTANCE.get(context).getUserProfiles()) {
|
||||
if (Process.myUserHandle().equals(userProfile)) {
|
||||
continue;
|
||||
}
|
||||
showConfirm |= !userManager.requestQuietModeEnabled(enabled, userProfile);
|
||||
}
|
||||
return showConfirm;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(Boolean showConfirm) {
|
||||
if (showConfirm) {
|
||||
WorkModeSwitch workModeSwitch = switchWeakReference.get();
|
||||
if (workModeSwitch != null) {
|
||||
workModeSwitch.setEnabled(true);
|
||||
}
|
||||
}
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
if (Utilities.ATLEAST_P) {
|
||||
setEnabled(false);
|
||||
UI_HELPER_EXECUTOR.post(() -> setToState(!mWorkEnabled));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows a work tip on the Nth work tab open
|
||||
* Sets the enabled or disabled state of the button
|
||||
*/
|
||||
public void showTipIfNeeded() {
|
||||
Context context = getContext();
|
||||
SharedPreferences prefs = Utilities.getPrefs(context);
|
||||
int tipCounter = prefs.getInt(KEY_WORK_TIP_COUNTER, WORK_TIP_THRESHOLD);
|
||||
if (tipCounter < 0) return;
|
||||
if (tipCounter == 0) {
|
||||
new ArrowTipView(context)
|
||||
.show(context.getString(R.string.work_switch_tip), getTop());
|
||||
public void updateCurrentState(boolean active) {
|
||||
mWorkEnabled = active;
|
||||
setEnabled(true);
|
||||
setCompoundDrawablesRelativeWithIntrinsicBounds(
|
||||
active ? R.drawable.ic_corp_off : R.drawable.ic_corp, 0, 0, 0);
|
||||
setText(active ? R.string.work_apps_pause_btn_text : R.string.work_apps_enable_btn_text);
|
||||
}
|
||||
|
||||
@RequiresApi(Build.VERSION_CODES.P)
|
||||
protected Boolean setToState(boolean toState) {
|
||||
UserManager userManager = getContext().getSystemService(UserManager.class);
|
||||
boolean showConfirm = false;
|
||||
for (UserHandle userProfile : UserCache.INSTANCE.get(getContext()).getUserProfiles()) {
|
||||
if (Process.myUserHandle().equals(userProfile)) {
|
||||
continue;
|
||||
}
|
||||
showConfirm |= !userManager.requestQuietModeEnabled(!toState, userProfile);
|
||||
}
|
||||
prefs.edit().putInt(KEY_WORK_TIP_COUNTER, tipCounter - 1).apply();
|
||||
return showConfirm;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -290,7 +290,10 @@ public class AnimatorPlaybackController implements ValueAnimator.AnimatorUpdateL
|
||||
callAnimatorCommandRecursively(mAnim, a -> a.setInterpolator(interpolator));
|
||||
}
|
||||
|
||||
private static void callListenerCommandRecursively(
|
||||
/**
|
||||
* Recursively calls a command on all the listeners of the provided animation
|
||||
*/
|
||||
public static void callListenerCommandRecursively(
|
||||
Animator anim, BiConsumer<AnimatorListener, Animator> command) {
|
||||
callAnimatorCommandRecursively(anim, a-> {
|
||||
for (AnimatorListener l : nonNullList(a.getListeners())) {
|
||||
|
||||
@@ -18,6 +18,7 @@ package com.android.launcher3.statemanager;
|
||||
|
||||
import static android.animation.ValueAnimator.areAnimatorsEnabled;
|
||||
|
||||
import static com.android.launcher3.anim.AnimatorPlaybackController.callListenerCommandRecursively;
|
||||
import static com.android.launcher3.states.StateAnimationConfig.SKIP_ALL_ANIMATIONS;
|
||||
|
||||
import android.animation.Animator;
|
||||
@@ -514,8 +515,15 @@ public class StateManager<STATE_TYPE extends BaseState<STATE_TYPE>> {
|
||||
playbackController.getAnimationPlayer().cancel();
|
||||
playbackController.dispatchOnCancel();
|
||||
} else if (currentAnimation != null) {
|
||||
currentAnimation.setDuration(0);
|
||||
currentAnimation.cancel();
|
||||
AnimatorSet anim = currentAnimation;
|
||||
anim.setDuration(0);
|
||||
if (!anim.isStarted()) {
|
||||
// If the animation is not started the listeners do not get notified,
|
||||
// notify manually.
|
||||
callListenerCommandRecursively(anim, AnimatorListener::onAnimationCancel);
|
||||
callListenerCommandRecursively(anim, AnimatorListener::onAnimationEnd);
|
||||
}
|
||||
anim.cancel();
|
||||
}
|
||||
|
||||
currentAnimation = null;
|
||||
|
||||
@@ -1,230 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2020 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.launcher3.views;
|
||||
|
||||
|
||||
import android.animation.Animator;
|
||||
import android.animation.ObjectAnimator;
|
||||
import android.animation.PropertyValuesHolder;
|
||||
import android.content.Context;
|
||||
import android.graphics.Rect;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.widget.Button;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.android.launcher3.Insettable;
|
||||
import com.android.launcher3.Launcher;
|
||||
import com.android.launcher3.LauncherState;
|
||||
import com.android.launcher3.R;
|
||||
import com.android.launcher3.allapps.AllAppsContainerView;
|
||||
import com.android.launcher3.allapps.AllAppsPagedView;
|
||||
import com.android.launcher3.anim.AnimationSuccessListener;
|
||||
import com.android.launcher3.anim.Interpolators;
|
||||
import com.android.launcher3.statemanager.StateManager.StateListener;
|
||||
|
||||
/**
|
||||
* On boarding flow for users right after setting up work profile
|
||||
*/
|
||||
public class WorkEduView extends AbstractSlideInView<Launcher>
|
||||
implements Insettable, StateListener<LauncherState> {
|
||||
|
||||
private static final int DEFAULT_CLOSE_DURATION = 200;
|
||||
public static final String KEY_WORK_EDU_STEP = "showed_work_profile_edu";
|
||||
public static final String KEY_LEGACY_WORK_EDU_SEEN = "showed_bottom_user_education";
|
||||
|
||||
private static final int WORK_EDU_NOT_STARTED = 0;
|
||||
private static final int WORK_EDU_PERSONAL_APPS = 1;
|
||||
private static final int WORK_EDU_WORK_APPS = 2;
|
||||
|
||||
protected static final int FINAL_SCRIM_BG_COLOR = 0x88000000;
|
||||
|
||||
|
||||
private Rect mInsets = new Rect();
|
||||
private View mViewWrapper;
|
||||
private Button mProceedButton;
|
||||
private TextView mContentText;
|
||||
|
||||
private int mNextWorkEduStep = WORK_EDU_PERSONAL_APPS;
|
||||
|
||||
|
||||
public WorkEduView(Context context, AttributeSet attr) {
|
||||
this(context, attr, 0);
|
||||
}
|
||||
|
||||
public WorkEduView(Context context, AttributeSet attrs,
|
||||
int defStyleAttr) {
|
||||
super(context, attrs, defStyleAttr);
|
||||
mContent = this;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void handleClose(boolean animate) {
|
||||
mActivityContext.getSharedPrefs().edit()
|
||||
.putInt(KEY_WORK_EDU_STEP, mNextWorkEduStep).apply();
|
||||
handleClose(true, DEFAULT_CLOSE_DURATION);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCloseComplete() {
|
||||
super.onCloseComplete();
|
||||
mActivityContext.getStateManager().removeStateListener(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isOfType(int type) {
|
||||
return (type & TYPE_ON_BOARD_POPUP) != 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onFinishInflate() {
|
||||
super.onFinishInflate();
|
||||
mViewWrapper = findViewById(R.id.view_wrapper);
|
||||
mProceedButton = findViewById(R.id.proceed);
|
||||
mContentText = findViewById(R.id.content_text);
|
||||
|
||||
// make sure layout does not shrink when we change the text
|
||||
mContentText.post(() -> mContentText.setMinLines(mContentText.getLineCount()));
|
||||
|
||||
mProceedButton.setOnClickListener(view -> {
|
||||
if (getAllAppsPagedView() != null) {
|
||||
getAllAppsPagedView().snapToPage(AllAppsContainerView.AdapterHolder.WORK);
|
||||
}
|
||||
goToWorkTab(true);
|
||||
});
|
||||
}
|
||||
|
||||
private void goToWorkTab(boolean animate) {
|
||||
mProceedButton.setText(R.string.work_profile_edu_accept);
|
||||
if (animate) {
|
||||
ObjectAnimator animator = ObjectAnimator.ofFloat(mContentText, ALPHA, 0);
|
||||
animator.addListener(new AnimationSuccessListener() {
|
||||
@Override
|
||||
public void onAnimationSuccess(Animator animator) {
|
||||
mContentText.setText(
|
||||
mActivityContext.getString(R.string.work_profile_edu_work_apps));
|
||||
ObjectAnimator.ofFloat(mContentText, ALPHA, 1).start();
|
||||
}
|
||||
});
|
||||
animator.start();
|
||||
} else {
|
||||
mContentText.setText(mActivityContext.getString(R.string.work_profile_edu_work_apps));
|
||||
}
|
||||
mNextWorkEduStep = WORK_EDU_WORK_APPS;
|
||||
mProceedButton.setOnClickListener(v -> handleClose(true));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setInsets(Rect insets) {
|
||||
int leftInset = insets.left - mInsets.left;
|
||||
int rightInset = insets.right - mInsets.right;
|
||||
int bottomInset = insets.bottom - mInsets.bottom;
|
||||
mInsets.set(insets);
|
||||
setPadding(leftInset, getPaddingTop(), rightInset, 0);
|
||||
mViewWrapper.setPaddingRelative(mViewWrapper.getPaddingStart(),
|
||||
mViewWrapper.getPaddingTop(), mViewWrapper.getPaddingEnd(), bottomInset);
|
||||
}
|
||||
|
||||
private void show() {
|
||||
attachToContainer();
|
||||
animateOpen();
|
||||
mActivityContext.getStateManager().addStateListener(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getScrimColor(Context context) {
|
||||
return FINAL_SCRIM_BG_COLOR;
|
||||
}
|
||||
|
||||
private void goToFirstPage() {
|
||||
if (getAllAppsPagedView() != null) {
|
||||
getAllAppsPagedView().snapToPageImmediately(AllAppsContainerView.AdapterHolder.MAIN);
|
||||
}
|
||||
}
|
||||
|
||||
private void animateOpen() {
|
||||
if (mIsOpen || mOpenCloseAnimator.isRunning()) {
|
||||
return;
|
||||
}
|
||||
mIsOpen = true;
|
||||
mOpenCloseAnimator.setValues(
|
||||
PropertyValuesHolder.ofFloat(TRANSLATION_SHIFT, TRANSLATION_SHIFT_OPENED));
|
||||
mOpenCloseAnimator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
|
||||
mOpenCloseAnimator.start();
|
||||
}
|
||||
|
||||
private AllAppsPagedView getAllAppsPagedView() {
|
||||
View v = mActivityContext.getAppsView().getContentView();
|
||||
return (v instanceof AllAppsPagedView) ? (AllAppsPagedView) v : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if user has not seen onboarding UI yet and shows it when user navigates to all apps
|
||||
*/
|
||||
public static StateListener<LauncherState> showEduFlowIfNeeded(Launcher launcher,
|
||||
@Nullable StateListener<LauncherState> oldListener) {
|
||||
if (oldListener != null) {
|
||||
launcher.getStateManager().removeStateListener(oldListener);
|
||||
}
|
||||
if (hasSeenLegacyEdu(launcher) || launcher.getSharedPrefs().getInt(KEY_WORK_EDU_STEP,
|
||||
WORK_EDU_NOT_STARTED) != WORK_EDU_NOT_STARTED) {
|
||||
return null;
|
||||
}
|
||||
|
||||
StateListener<LauncherState> listener = new StateListener<LauncherState>() {
|
||||
@Override
|
||||
public void onStateTransitionComplete(LauncherState finalState) {
|
||||
if (finalState != LauncherState.ALL_APPS) return;
|
||||
LayoutInflater layoutInflater = LayoutInflater.from(launcher);
|
||||
WorkEduView v = (WorkEduView) layoutInflater.inflate(
|
||||
R.layout.work_profile_edu, launcher.getDragLayer(),
|
||||
false);
|
||||
v.show();
|
||||
v.goToFirstPage();
|
||||
launcher.getStateManager().removeStateListener(this);
|
||||
}
|
||||
};
|
||||
launcher.getStateManager().addStateListener(listener);
|
||||
return listener;
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows work apps edu if user had dismissed full edu flow
|
||||
*/
|
||||
public static void showWorkEduIfNeeded(Launcher launcher) {
|
||||
if (hasSeenLegacyEdu(launcher) || launcher.getSharedPrefs().getInt(KEY_WORK_EDU_STEP,
|
||||
WORK_EDU_NOT_STARTED) != WORK_EDU_PERSONAL_APPS) {
|
||||
return;
|
||||
}
|
||||
LayoutInflater layoutInflater = LayoutInflater.from(launcher);
|
||||
WorkEduView v = (WorkEduView) layoutInflater.inflate(
|
||||
R.layout.work_profile_edu, launcher.getDragLayer(), false);
|
||||
v.show();
|
||||
v.goToWorkTab(false);
|
||||
}
|
||||
|
||||
private static boolean hasSeenLegacyEdu(Launcher launcher) {
|
||||
return launcher.getSharedPrefs().getBoolean(KEY_LEGACY_WORK_EDU_SEEN, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStateTransitionComplete(LauncherState finalState) {
|
||||
close(false);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* Copyright (C) 2021 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.android.launcher3.widget.picker;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import com.android.launcher3.widget.picker.SearchAndRecommendationsScrollController.OnContentChangeListener;
|
||||
|
||||
/**
|
||||
* A layout manager for the {@link WidgetsRecyclerView}.
|
||||
*
|
||||
* {@link #setOnContentChangeListener(OnContentChangeListener)} can be used to register a callback
|
||||
* for when the content of the layout manager has changed, following measurement and animation.
|
||||
*/
|
||||
public final class WidgetsListLayoutManager extends LinearLayoutManager {
|
||||
@Nullable
|
||||
private OnContentChangeListener mOnContentChangeListener;
|
||||
|
||||
public WidgetsListLayoutManager(Context context) {
|
||||
super(context);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLayoutCompleted(RecyclerView.State state) {
|
||||
super.onLayoutCompleted(state);
|
||||
if (mOnContentChangeListener != null) {
|
||||
mOnContentChangeListener.onContentChanged();
|
||||
}
|
||||
}
|
||||
|
||||
public void setOnContentChangeListener(@Nullable OnContentChangeListener listener) {
|
||||
mOnContentChangeListener = listener;
|
||||
}
|
||||
}
|
||||
@@ -23,7 +23,6 @@ import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
import android.widget.TableLayout;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
@@ -83,7 +82,9 @@ public class WidgetsRecyclerView extends BaseRecyclerView implements OnItemTouch
|
||||
super.onFinishInflate();
|
||||
// create a layout manager with Launcher's context so that scroll position
|
||||
// can be preserved during screen rotation.
|
||||
setLayoutManager(new LinearLayoutManager(getContext()));
|
||||
WidgetsListLayoutManager layoutManager = new WidgetsListLayoutManager(getContext());
|
||||
layoutManager.setOnContentChangeListener(mOnContentChangeListener);
|
||||
setLayoutManager(layoutManager);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -92,22 +93,6 @@ public class WidgetsRecyclerView extends BaseRecyclerView implements OnItemTouch
|
||||
mAdapter = (WidgetsListAdapter) adapter;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onChildAttachedToWindow(@NonNull View child) {
|
||||
super.onChildAttachedToWindow(child);
|
||||
if (mOnContentChangeListener != null) {
|
||||
mOnContentChangeListener.onContentChanged();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onChildDetachedFromWindow(@NonNull View child) {
|
||||
super.onChildDetachedFromWindow(child);
|
||||
if (mOnContentChangeListener != null) {
|
||||
mOnContentChangeListener.onContentChanged();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Maps the touch (from 0..1) to the adapter position that should be visible.
|
||||
*/
|
||||
@@ -268,6 +253,10 @@ public class WidgetsRecyclerView extends BaseRecyclerView implements OnItemTouch
|
||||
|
||||
public void setOnContentChangeListener(@Nullable OnContentChangeListener listener) {
|
||||
mOnContentChangeListener = listener;
|
||||
WidgetsListLayoutManager layoutManager = (WidgetsListLayoutManager) getLayoutManager();
|
||||
if (layoutManager != null) {
|
||||
layoutManager.setOnContentChangeListener(listener);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -59,7 +59,8 @@ public class PersonalWorkSlidingTabStrip extends LinearLayout implements PageInd
|
||||
R.dimen.all_apps_header_pill_corner_radius);
|
||||
|
||||
mSelectedIndicatorPaint = new Paint();
|
||||
mSelectedIndicatorPaint.setColor(context.getColor(R.color.all_apps_tab_bg));
|
||||
mSelectedIndicatorPaint.setColor(
|
||||
context.getColor(R.color.all_apps_tab_background_selected));
|
||||
|
||||
mIsRtl = Utilities.isRtl(getResources());
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user