Snap for 7456046 from 012e978e31 to sc-release

Change-Id: If661343407508ed906a12f19cf37e05f17b5fb01
This commit is contained in:
android-build-team Robot
2021-06-15 01:08:54 +00:00
44 changed files with 510 additions and 532 deletions
-2
View File
@@ -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>
+19
View File
@@ -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>
+19
View File
@@ -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>
+19
View File
@@ -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>
+2 -2
View File
@@ -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>
+18
View File
@@ -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>
+3 -5
View File
@@ -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>
+33
View File
@@ -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" />
+3 -6
View File
@@ -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" />
-61
View File
@@ -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>
+4
View File
@@ -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
View File
@@ -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 &amp; hidden from work apps</string>
<!--- User onboarding title for work profile apps -->
<string name="work_profile_edu_work_apps">Work apps &amp; 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 cant send you notifications, use your battery, or access your location</string>
<string name="work_apps_paused_body">Your work apps cant 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 cant 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 cant 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());
}