Merge "Add a button for floating tasks to taskbar (behind a flag)" into tm-qpr-dev am: 13b7e00fff am: 6d37424c94

Original change: https://googleplex-android-review.googlesource.com/c/platform/packages/apps/Launcher3/+/19874561

Change-Id: I7ca43d2d6c8f6fbc7015d95061c30d6160daf0f2
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
This commit is contained in:
Mady Mellor
2022-09-23 05:43:24 +00:00
committed by Automerger Merge Worker
8 changed files with 250 additions and 4 deletions
@@ -0,0 +1,97 @@
/*
* Copyright (C) 2022 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.taskbar;
import static android.content.pm.PackageManager.MATCH_DEFAULT_ONLY;
import android.annotation.Nullable;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.text.TextUtils;
import android.util.Log;
import com.android.launcher3.R;
// TODO: This would be replaced by the thing that has the role and provides the intent.
/**
* Helper to determine what intent should be used to display in a floating window, if one
* exists.
*/
public class FloatingTaskIntentResolver {
private static final String TAG = FloatingTaskIntentResolver.class.getSimpleName();
@Nullable
/** Gets an intent for a floating task, if one exists. */
public static Intent getIntent(Context context) {
PackageManager pm = context.getPackageManager();
String pkg = context.getString(R.string.floating_task_package);
String action = context.getString(R.string.floating_task_action);
if (TextUtils.isEmpty(pkg) || TextUtils.isEmpty(action)) {
Log.d(TAG, "intent could not be found, pkg= " + pkg + " action= " + action);
return null;
}
Intent intent = createIntent(pm, null, pkg, action);
if (intent != null) {
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
return intent;
}
Log.d(TAG, "No valid intent found!");
return null;
}
@Nullable
private static Intent createIntent(PackageManager pm, @Nullable String activityName,
String packageName, String action) {
if (TextUtils.isEmpty(activityName)) {
activityName = queryActivityForAction(pm, packageName, action);
}
if (TextUtils.isEmpty(activityName)) {
Log.d(TAG, "Activity name is empty even after action search: " + action);
return null;
}
ComponentName component = new ComponentName(packageName, activityName);
Intent intent = new Intent(action).setComponent(component);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
Log.d(TAG, "createIntent returning: " + intent);
return intent;
}
@Nullable
private static String queryActivityForAction(PackageManager pm, String packageName,
String action) {
Intent intent = new Intent(action).setPackage(packageName);
ResolveInfo resolveInfo = pm.resolveActivity(intent, MATCH_DEFAULT_ONLY);
if (resolveInfo == null || resolveInfo.activityInfo == null) {
Log.d(TAG, "queryActivityForAction: + " + resolveInfo);
return null;
}
ActivityInfo info = resolveInfo.activityInfo;
if (!info.exported) {
Log.d(TAG, "queryActivityForAction: + " + info + " not exported");
return null;
}
if (!info.enabled) {
Log.d(TAG, "queryActivityForAction: + " + info + " not enabled");
return null;
}
return resolveInfo.activityInfo.name;
}
}
@@ -0,0 +1,51 @@
/*
* Copyright (C) 2022 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.taskbar;
import android.content.Context;
import android.graphics.Bitmap;
import android.util.AttributeSet;
import android.view.ContextThemeWrapper;
import com.android.launcher3.BubbleTextView;
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.R;
import com.android.launcher3.icons.FastBitmapDrawable;
/**
* Button in Taskbar that opens something in a floating task.
*/
public class LaunchFloatingTaskButton extends BubbleTextView {
public LaunchFloatingTaskButton(Context context) {
this(context, null);
}
public LaunchFloatingTaskButton(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public LaunchFloatingTaskButton(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
Context theme = new ContextThemeWrapper(context, R.style.AllAppsButtonTheme);
Bitmap bitmap = LauncherAppState.getInstance(context).getIconCache().getIconFactory()
.createScaledBitmapWithShadow(
theme.getDrawable(R.drawable.ic_floating_task_button));
setIcon(new FastBitmapDrawable(bitmap));
}
}
@@ -16,10 +16,13 @@
package com.android.launcher3.taskbar;
import android.content.Context;
import android.content.Intent;
import android.content.res.Resources;
import android.graphics.Canvas;
import android.graphics.Rect;
import android.os.SystemProperties;
import android.util.AttributeSet;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
@@ -53,6 +56,7 @@ import java.util.function.Predicate;
* Hosts the Taskbar content such as Hotseat and Recent Apps. Drawn on top of other apps.
*/
public class TaskbarView extends FrameLayout implements FolderIcon.FolderIconParent, Insettable {
private static final String TAG = TaskbarView.class.getSimpleName();
private static final float TASKBAR_BACKGROUND_LUMINANCE = 0.30f;
public int mThemeIconsBackground;
@@ -81,6 +85,12 @@ public class TaskbarView extends FrameLayout implements FolderIcon.FolderIconPar
private View mQsb;
// Only non-null when device supports having a floating task.
private @Nullable BubbleTextView mFloatingTaskButton;
private @Nullable Intent mFloatingTaskIntent;
private static final boolean FLOATING_TASKS_ENABLED =
SystemProperties.getBoolean("persist.wm.debug.floating_tasks", false);
public TaskbarView(@NonNull Context context) {
this(context, null);
}
@@ -123,6 +133,19 @@ public class TaskbarView extends FrameLayout implements FolderIcon.FolderIconPar
// TODO: Disable touch events on QSB otherwise it can crash.
mQsb = LayoutInflater.from(context).inflate(R.layout.search_container_hotseat, this, false);
if (FLOATING_TASKS_ENABLED) {
mFloatingTaskIntent = FloatingTaskIntentResolver.getIntent(context);
if (mFloatingTaskIntent != null) {
mFloatingTaskButton = new LaunchFloatingTaskButton(context);
mFloatingTaskButton.setLayoutParams(
new ViewGroup.LayoutParams(mIconTouchSize, mIconTouchSize));
mFloatingTaskButton.setPadding(mItemPadding, mItemPadding, mItemPadding,
mItemPadding);
} else {
Log.d(TAG, "Floating tasks is enabled but no intent was found!");
}
}
}
private int getColorWithGivenLuminance(int color, float luminance) {
@@ -150,6 +173,10 @@ public class TaskbarView extends FrameLayout implements FolderIcon.FolderIconPar
if (mAllAppsButton != null) {
mAllAppsButton.setOnClickListener(mControllerCallbacks.getAllAppsButtonClickListener());
}
if (mFloatingTaskButton != null) {
mFloatingTaskButton.setOnClickListener(
mControllerCallbacks.getFloatingTaskButtonListener(mFloatingTaskIntent));
}
}
private void removeAndRecycle(View view) {
@@ -174,6 +201,10 @@ public class TaskbarView extends FrameLayout implements FolderIcon.FolderIconPar
}
removeView(mQsb);
if (mFloatingTaskButton != null) {
removeView(mFloatingTaskButton);
}
for (int i = 0; i < hotseatItemInfos.length; i++) {
ItemInfo hotseatItemInfo = hotseatItemInfos[i];
if (hotseatItemInfo == null) {
@@ -255,6 +286,11 @@ public class TaskbarView extends FrameLayout implements FolderIcon.FolderIconPar
mQsb.setVisibility(View.INVISIBLE);
}
if (mFloatingTaskButton != null) {
int index = Utilities.isRtl(getResources()) ? 0 : getChildCount();
addView(mFloatingTaskButton, index);
}
mThemeIconsBackground = calculateThemeIconsBackground();
setThemedIconsBackgroundColor(mThemeIconsBackground);
}
@@ -24,6 +24,7 @@ import static com.android.launcher3.taskbar.TaskbarManager.isPhoneMode;
import static com.android.quickstep.AnimatedFloat.VALUE;
import android.annotation.NonNull;
import android.content.Intent;
import android.graphics.Rect;
import android.util.FloatProperty;
import android.util.Log;
@@ -51,6 +52,7 @@ import com.android.launcher3.util.ItemInfoMatcher;
import com.android.launcher3.util.LauncherBindableItemsContainer;
import com.android.launcher3.util.MultiValueAlpha;
import com.android.quickstep.AnimatedFloat;
import com.android.quickstep.SystemUiProxy;
import java.io.PrintWriter;
import java.util.function.Predicate;
@@ -427,6 +429,13 @@ public class TaskbarViewController implements TaskbarControllers.LoggableTaskbar
};
}
public View.OnClickListener getFloatingTaskButtonListener(@NonNull Intent intent) {
return v -> {
SystemUiProxy proxy = SystemUiProxy.INSTANCE.get(v.getContext());
proxy.showFloatingTask(intent);
};
}
public View.OnLongClickListener getIconOnLongClickListener() {
return mControllers.taskbarDragController::startDragOnLongClick;
}
@@ -58,6 +58,7 @@ import com.android.systemui.shared.system.smartspace.ILauncherUnlockAnimationCon
import com.android.systemui.shared.system.smartspace.ISysuiUnlockAnimationController;
import com.android.systemui.shared.system.smartspace.SmartspaceState;
import com.android.wm.shell.back.IBackAnimation;
import com.android.wm.shell.floating.IFloatingTasks;
import com.android.wm.shell.onehanded.IOneHanded;
import com.android.wm.shell.pip.IPip;
import com.android.wm.shell.pip.IPipAnimationListener;
@@ -88,6 +89,7 @@ public class SystemUiProxy implements ISystemUiProxy {
private IPip mPip;
private ISysuiUnlockAnimationController mSysuiUnlockAnimationController;
private ISplitScreen mSplitScreen;
private IFloatingTasks mFloatingTasks;
private IOneHanded mOneHanded;
private IShellTransitions mShellTransitions;
private IStartingWindow mStartingWindow;
@@ -165,7 +167,7 @@ public class SystemUiProxy implements ISystemUiProxy {
}
public void setProxy(ISystemUiProxy proxy, IPip pip, ISplitScreen splitScreen,
IOneHanded oneHanded, IShellTransitions shellTransitions,
IFloatingTasks floatingTasks, IOneHanded oneHanded, IShellTransitions shellTransitions,
IStartingWindow startingWindow, IRecentTasks recentTasks,
ISysuiUnlockAnimationController sysuiUnlockAnimationController,
IBackAnimation backAnimation) {
@@ -173,6 +175,7 @@ public class SystemUiProxy implements ISystemUiProxy {
mSystemUiProxy = proxy;
mPip = pip;
mSplitScreen = splitScreen;
mFloatingTasks = floatingTasks;
mOneHanded = oneHanded;
mShellTransitions = shellTransitions;
mStartingWindow = startingWindow;
@@ -206,7 +209,7 @@ public class SystemUiProxy implements ISystemUiProxy {
}
public void clearProxy() {
setProxy(null, null, null, null, null, null, null, null, null);
setProxy(null, null, null, null, null, null, null, null, null, null);
}
// TODO(141886704): Find a way to remove this
@@ -664,6 +667,20 @@ public class SystemUiProxy implements ISystemUiProxy {
return null;
}
//
// Floating tasks
//
public void showFloatingTask(Intent intent) {
if (mFloatingTasks != null) {
try {
mFloatingTasks.showTask(intent);
} catch (RemoteException e) {
Log.w(TAG, "Launcher: Failed call showFloatingTask", e);
}
}
}
//
// One handed
//
@@ -28,6 +28,7 @@ import static com.android.quickstep.util.ActiveGestureErrorDetector.GestureEvent
import static com.android.systemui.shared.system.ActivityManagerWrapper.CLOSE_SYSTEM_WINDOWS_REASON_RECENTS;
import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_RECENT_TASKS;
import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SHELL_BACK_ANIMATION;
import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SHELL_FLOATING_TASKS;
import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SHELL_ONE_HANDED;
import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SHELL_PIP;
import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SHELL_SHELL_TRANSITIONS;
@@ -110,6 +111,7 @@ import com.android.systemui.shared.system.InputMonitorCompat;
import com.android.systemui.shared.system.smartspace.ISysuiUnlockAnimationController;
import com.android.systemui.shared.tracing.ProtoTraceable;
import com.android.wm.shell.back.IBackAnimation;
import com.android.wm.shell.floating.IFloatingTasks;
import com.android.wm.shell.onehanded.IOneHanded;
import com.android.wm.shell.pip.IPip;
import com.android.wm.shell.recents.IRecentTasks;
@@ -167,6 +169,8 @@ public class TouchInteractionService extends Service
IPip pip = IPip.Stub.asInterface(bundle.getBinder(KEY_EXTRA_SHELL_PIP));
ISplitScreen splitscreen = ISplitScreen.Stub.asInterface(bundle.getBinder(
KEY_EXTRA_SHELL_SPLIT_SCREEN));
IFloatingTasks floatingTasks = IFloatingTasks.Stub.asInterface(bundle.getBinder(
KEY_EXTRA_SHELL_FLOATING_TASKS));
IOneHanded onehanded = IOneHanded.Stub.asInterface(
bundle.getBinder(KEY_EXTRA_SHELL_ONE_HANDED));
IShellTransitions shellTransitions = IShellTransitions.Stub.asInterface(
@@ -182,8 +186,8 @@ public class TouchInteractionService extends Service
bundle.getBinder(KEY_EXTRA_SHELL_BACK_ANIMATION));
MAIN_EXECUTOR.execute(() -> {
SystemUiProxy.INSTANCE.get(TouchInteractionService.this).setProxy(proxy, pip,
splitscreen, onehanded, shellTransitions, startingWindow, recentTasks,
launcherUnlockAnimationController, backAnimation);
splitscreen, floatingTasks, onehanded, shellTransitions, startingWindow,
recentTasks, launcherUnlockAnimationController, backAnimation);
TouchInteractionService.this.initInputMonitor("TISBinder#onInitialize()");
preloadOverview(true /* fromInit */);
});