Snap for 7961093 from eba2a6aa8a to tm-release
Change-Id: I2e90cfb4725790808fbd3f645ada61a41a622ac4
This commit is contained in:
@@ -39,4 +39,8 @@
|
||||
<string name="wellbeing_provider_pkg" translatable="false"/>
|
||||
|
||||
<integer name="max_depth_blur_radius">23</integer>
|
||||
|
||||
<!-- Accessibility actions -->
|
||||
<item type="id" name="action_move_to_top_or_left" />
|
||||
<item type="id" name="action_move_to_bottom_or_right" />
|
||||
</resources>
|
||||
|
||||
@@ -233,4 +233,9 @@
|
||||
<string name="taskbar_edu_close">Close</string>
|
||||
<!-- Text on button to finish a tutorial [CHAR_LIMIT=16] -->
|
||||
<string name="taskbar_edu_done">Done</string>
|
||||
|
||||
<!-- Label for moving drop target to the top or left side of the screen, depending on orientation (from the taskbar only). -->
|
||||
<string name="move_drop_target_top_or_left">Move to top/left</string>
|
||||
<!-- Label for moving drop target to the bottom or right side of the screen, depending on orientation (from the taskbar only). -->
|
||||
<string name="move_drop_target_bottom_or_right">Move to bottom/right</string>
|
||||
</resources>
|
||||
|
||||
@@ -44,7 +44,7 @@ public class QuickstepAccessibilityDelegate extends LauncherAccessibilityDelegat
|
||||
|
||||
@Override
|
||||
protected boolean performAction(View host, ItemInfo item, int action, boolean fromKeyboard) {
|
||||
QuickstepLauncher launcher = (QuickstepLauncher) mLauncher;
|
||||
QuickstepLauncher launcher = (QuickstepLauncher) mContext;
|
||||
if (action == PIN_PREDICTION) {
|
||||
if (launcher.getHotseatPredictionController() == null) {
|
||||
return false;
|
||||
|
||||
@@ -60,6 +60,7 @@ public class FallbackTaskbarUIController extends TaskbarUIController {
|
||||
|
||||
@Override
|
||||
protected void onDestroy() {
|
||||
super.onDestroy();
|
||||
mRecentsActivity.setTaskbarUIController(null);
|
||||
mRecentsActivity.getStateManager().removeStateListener(mStateListener);
|
||||
}
|
||||
|
||||
@@ -99,6 +99,7 @@ public class LauncherTaskbarUIController extends TaskbarUIController {
|
||||
|
||||
@Override
|
||||
protected void onDestroy() {
|
||||
super.onDestroy();
|
||||
onLauncherResumedOrPaused(false);
|
||||
mTaskbarLauncherStateController.onDestroy();
|
||||
|
||||
|
||||
@@ -31,6 +31,7 @@ import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_I
|
||||
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED;
|
||||
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_OVERVIEW_DISABLED;
|
||||
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_QUICK_SETTINGS_EXPANDED;
|
||||
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_SCREEN_PINNING;
|
||||
|
||||
import android.animation.ArgbEvaluator;
|
||||
import android.animation.ObjectAnimator;
|
||||
@@ -86,6 +87,7 @@ public class NavbarButtonsViewController {
|
||||
private static final int FLAG_DISABLE_RECENTS = 1 << 8;
|
||||
private static final int FLAG_DISABLE_BACK = 1 << 9;
|
||||
private static final int FLAG_NOTIFICATION_SHADE_EXPANDED = 1 << 10;
|
||||
private static final int FLAG_SCREEN_PINNING_ACTIVE = 1 << 10;
|
||||
|
||||
private static final int MASK_IME_SWITCHER_VISIBLE = FLAG_SWITCHER_SUPPORTED | FLAG_IME_VISIBLE;
|
||||
|
||||
@@ -152,7 +154,9 @@ public class NavbarButtonsViewController {
|
||||
mPropertyHolders.add(new StatePropertyHolder(
|
||||
mControllers.taskbarViewController.getTaskbarIconAlpha()
|
||||
.getProperty(ALPHA_INDEX_KEYGUARD),
|
||||
flags -> (flags & FLAG_KEYGUARD_VISIBLE) == 0, MultiValueAlpha.VALUE, 1, 0));
|
||||
flags -> (flags & FLAG_KEYGUARD_VISIBLE) == 0
|
||||
&& (flags & FLAG_SCREEN_PINNING_ACTIVE) == 0,
|
||||
MultiValueAlpha.VALUE, 1, 0));
|
||||
|
||||
mPropertyHolders.add(new StatePropertyHolder(mControllers.taskbarDragLayerController
|
||||
.getKeyguardBgTaskbar(),
|
||||
@@ -286,6 +290,7 @@ public class NavbarButtonsViewController {
|
||||
int shadeExpandedFlags = SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED
|
||||
| SYSUI_STATE_QUICK_SETTINGS_EXPANDED;
|
||||
boolean isNotificationShadeExpanded = (sysUiStateFlags & shadeExpandedFlags) != 0;
|
||||
boolean isScreenPinningActive = (sysUiStateFlags & SYSUI_STATE_SCREEN_PINNING) != 0;
|
||||
|
||||
// TODO(b/202218289) we're getting IME as not visible on lockscreen from system
|
||||
updateStateForFlag(FLAG_IME_VISIBLE, isImeVisible);
|
||||
@@ -295,6 +300,7 @@ public class NavbarButtonsViewController {
|
||||
updateStateForFlag(FLAG_DISABLE_RECENTS, isRecentsDisabled);
|
||||
updateStateForFlag(FLAG_DISABLE_BACK, isBackDisabled);
|
||||
updateStateForFlag(FLAG_NOTIFICATION_SHADE_EXPANDED, isNotificationShadeExpanded);
|
||||
updateStateForFlag(FLAG_SCREEN_PINNING_ACTIVE, isScreenPinningActive);
|
||||
|
||||
if (mA11yButton != null) {
|
||||
// Only used in 3 button
|
||||
|
||||
@@ -53,6 +53,7 @@ import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.android.launcher3.AbstractFloatingView;
|
||||
import com.android.launcher3.BubbleTextView;
|
||||
import com.android.launcher3.DeviceProfile;
|
||||
import com.android.launcher3.LauncherSettings.Favorites;
|
||||
import com.android.launcher3.R;
|
||||
@@ -113,6 +114,8 @@ public class TaskbarActivityContext extends ContextThemeWrapper implements Activ
|
||||
// The flag to know if the window is excluded from magnification region computation.
|
||||
private boolean mIsExcludeFromMagnificationRegion = false;
|
||||
|
||||
private final TaskbarShortcutMenuAccessibilityDelegate mAccessibilityDelegate;
|
||||
|
||||
public TaskbarActivityContext(Context windowContext, DeviceProfile dp,
|
||||
TaskbarNavButtonController buttonController, ScopedUnfoldTransitionProgressProvider
|
||||
unfoldTransitionProgressProvider) {
|
||||
@@ -148,6 +151,8 @@ public class TaskbarActivityContext extends ContextThemeWrapper implements Activ
|
||||
mLeftCorner = display.getRoundedCorner(RoundedCorner.POSITION_BOTTOM_LEFT);
|
||||
mRightCorner = display.getRoundedCorner(RoundedCorner.POSITION_BOTTOM_RIGHT);
|
||||
|
||||
mAccessibilityDelegate = new TaskbarShortcutMenuAccessibilityDelegate(this);
|
||||
|
||||
// Construct controllers.
|
||||
mControllers = new TaskbarControllers(this,
|
||||
new TaskbarDragController(this),
|
||||
@@ -333,6 +338,11 @@ public class TaskbarActivityContext extends ContextThemeWrapper implements Activ
|
||||
return mControllers.taskbarPopupController.getPopupDataProvider();
|
||||
}
|
||||
|
||||
@Override
|
||||
public View.AccessibilityDelegate getAccessibilityDelegate() {
|
||||
return mAccessibilityDelegate;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a new data-source for this taskbar instance
|
||||
*/
|
||||
@@ -375,6 +385,7 @@ public class TaskbarActivityContext extends ContextThemeWrapper implements Activ
|
||||
mControllers.taskbarStashController.updateStateForSysuiFlags(systemUiStateFlags, fromInit);
|
||||
mControllers.taskbarScrimViewController.updateStateForSysuiFlags(systemUiStateFlags,
|
||||
fromInit);
|
||||
mControllers.navButtonController.updateSysuiFlags(systemUiStateFlags);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -563,4 +574,9 @@ public class TaskbarActivityContext extends ContextThemeWrapper implements Activ
|
||||
}
|
||||
mWindowManager.updateViewLayout(mDragLayer, mWindowLayoutParams);
|
||||
}
|
||||
|
||||
public void showPopupMenuForIcon(BubbleTextView btv) {
|
||||
setTaskbarWindowFullscreen(true);
|
||||
btv.post(() -> mControllers.taskbarPopupController.showForIcon(btv));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -51,6 +51,8 @@ import com.android.launcher3.logging.StatsLogManager;
|
||||
import com.android.launcher3.model.data.ItemInfo;
|
||||
import com.android.launcher3.model.data.WorkspaceItemInfo;
|
||||
import com.android.launcher3.popup.PopupContainerWithArrow;
|
||||
import com.android.launcher3.shortcuts.DeepShortcutView;
|
||||
import com.android.launcher3.shortcuts.ShortcutDragPreviewProvider;
|
||||
import com.android.systemui.shared.recents.model.Task;
|
||||
import com.android.systemui.shared.system.ClipDescriptionCompat;
|
||||
import com.android.systemui.shared.system.LauncherAppsCompat;
|
||||
@@ -88,6 +90,21 @@ public class TaskbarDragController extends DragController<TaskbarActivityContext
|
||||
* @return Whether {@link View#startDragAndDrop} started successfully.
|
||||
*/
|
||||
protected boolean startDragOnLongClick(View view) {
|
||||
return startDragOnLongClick(view, null, null);
|
||||
}
|
||||
|
||||
protected boolean startDragOnLongClick(
|
||||
DeepShortcutView shortcutView, Point iconShift) {
|
||||
return startDragOnLongClick(
|
||||
shortcutView.getBubbleText(),
|
||||
new ShortcutDragPreviewProvider(shortcutView.getIconView(), iconShift),
|
||||
iconShift);
|
||||
}
|
||||
|
||||
private boolean startDragOnLongClick(
|
||||
View view,
|
||||
@Nullable DragPreviewProvider dragPreviewProvider,
|
||||
@Nullable Point iconShift) {
|
||||
if (!(view instanceof BubbleTextView)) {
|
||||
return false;
|
||||
}
|
||||
@@ -96,7 +113,10 @@ public class TaskbarDragController extends DragController<TaskbarActivityContext
|
||||
|
||||
mActivity.setTaskbarWindowFullscreen(true);
|
||||
btv.post(() -> {
|
||||
startInternalDrag(btv);
|
||||
DragView dragView = startInternalDrag(btv, dragPreviewProvider);
|
||||
if (iconShift != null) {
|
||||
dragView.animateShift(-iconShift.x, -iconShift.y);
|
||||
}
|
||||
btv.getIcon().setIsDisabled(true);
|
||||
mControllers.taskbarAutohideSuspendController.updateFlag(
|
||||
TaskbarAutohideSuspendController.FLAG_AUTOHIDE_SUSPEND_DRAGGING, true);
|
||||
@@ -104,7 +124,8 @@ public class TaskbarDragController extends DragController<TaskbarActivityContext
|
||||
return true;
|
||||
}
|
||||
|
||||
private void startInternalDrag(BubbleTextView btv) {
|
||||
private DragView startInternalDrag(
|
||||
BubbleTextView btv, @Nullable DragPreviewProvider dragPreviewProvider) {
|
||||
float iconScale = btv.getIcon().getAnimatedScale();
|
||||
|
||||
// Clear the pressed state if necessary
|
||||
@@ -112,7 +133,8 @@ public class TaskbarDragController extends DragController<TaskbarActivityContext
|
||||
btv.setPressed(false);
|
||||
btv.clearPressedBackground();
|
||||
|
||||
final DragPreviewProvider previewProvider = new DragPreviewProvider(btv);
|
||||
final DragPreviewProvider previewProvider = dragPreviewProvider == null
|
||||
? new DragPreviewProvider(btv) : dragPreviewProvider;
|
||||
final Drawable drawable = previewProvider.createDrawable();
|
||||
final float scale = previewProvider.getScaleAndPosition(drawable, mTempXY);
|
||||
int dragLayerX = mTempXY[0];
|
||||
@@ -149,7 +171,7 @@ public class TaskbarDragController extends DragController<TaskbarActivityContext
|
||||
}
|
||||
}
|
||||
|
||||
startDrag(
|
||||
return startDrag(
|
||||
drawable,
|
||||
/* view = */ null,
|
||||
/* originalView = */ btv,
|
||||
@@ -241,7 +263,8 @@ public class TaskbarDragController extends DragController<TaskbarActivityContext
|
||||
|
||||
@Override
|
||||
public void onProvideShadowMetrics(Point shadowSize, Point shadowTouchPoint) {
|
||||
shadowSize.set(mDragIconSize, mDragIconSize);
|
||||
int iconSize = Math.max(mDragIconSize, btv.getWidth());
|
||||
shadowSize.set(iconSize, iconSize);
|
||||
// The registration point was taken before the icon scaled to mDragIconSize, so
|
||||
// offset the registration to where the touch is on the new size.
|
||||
int offsetX = (mDragIconSize - mDragObject.dragView.getDragRegionWidth()) / 2;
|
||||
@@ -273,6 +296,12 @@ public class TaskbarDragController extends DragController<TaskbarActivityContext
|
||||
});
|
||||
intent = new Intent();
|
||||
if (item.itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT) {
|
||||
intent.putExtra(ClipDescriptionCompat.EXTRA_PENDING_INTENT,
|
||||
launcherApps.getShortcutIntent(
|
||||
item.getIntent().getPackage(),
|
||||
item.getDeepShortcutId(),
|
||||
null,
|
||||
item.user));
|
||||
intent.putExtra(Intent.EXTRA_PACKAGE_NAME, item.getIntent().getPackage());
|
||||
intent.putExtra(Intent.EXTRA_SHORTCUT_ID, item.getDeepShortcutId());
|
||||
} else {
|
||||
|
||||
@@ -30,8 +30,8 @@ import android.content.pm.ActivityInfo;
|
||||
import android.content.res.Configuration;
|
||||
import android.hardware.display.DisplayManager;
|
||||
import android.net.Uri;
|
||||
import android.os.Handler;
|
||||
import android.provider.Settings;
|
||||
import android.util.Log;
|
||||
import android.view.Display;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
@@ -94,7 +94,8 @@ public class TaskbarManager implements DisplayController.DisplayInfoChangeListen
|
||||
Display display =
|
||||
service.getSystemService(DisplayManager.class).getDisplay(DEFAULT_DISPLAY);
|
||||
mContext = service.createWindowContext(display, TYPE_NAVIGATION_BAR_PANEL, null);
|
||||
mNavButtonController = new TaskbarNavButtonController(service);
|
||||
mNavButtonController = new TaskbarNavButtonController(service,
|
||||
SystemUiProxy.INSTANCE.get(mContext), new Handler());
|
||||
mUserSetupCompleteListener = isUserSetupComplete -> recreateTaskbar();
|
||||
mComponentCallbacks = new ComponentCallbacks() {
|
||||
private Configuration mOldConfig = mContext.getResources().getConfiguration();
|
||||
|
||||
@@ -19,8 +19,10 @@ package com.android.launcher3.taskbar;
|
||||
|
||||
import static com.android.internal.app.AssistUtils.INVOCATION_TYPE_HOME_BUTTON_LONG_PRESS;
|
||||
import static com.android.internal.app.AssistUtils.INVOCATION_TYPE_KEY;
|
||||
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_SCREEN_PINNING;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
|
||||
import androidx.annotation.IntDef;
|
||||
|
||||
@@ -40,6 +42,13 @@ import java.lang.annotation.RetentionPolicy;
|
||||
*/
|
||||
public class TaskbarNavButtonController {
|
||||
|
||||
/** Allow some time in between the long press for back and recents. */
|
||||
static final int SCREEN_PIN_LONG_PRESS_THRESHOLD = 200;
|
||||
static final int SCREEN_PIN_LONG_PRESS_RESET = SCREEN_PIN_LONG_PRESS_THRESHOLD + 100;
|
||||
|
||||
private long mLastScreenPinLongPress;
|
||||
private boolean mScreenPinned;
|
||||
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
@IntDef(value = {
|
||||
BUTTON_BACK,
|
||||
@@ -57,10 +66,20 @@ public class TaskbarNavButtonController {
|
||||
static final int BUTTON_IME_SWITCH = BUTTON_RECENTS << 1;
|
||||
static final int BUTTON_A11Y = BUTTON_IME_SWITCH << 1;
|
||||
|
||||
private final TouchInteractionService mService;
|
||||
private static final int SCREEN_UNPIN_COMBO = BUTTON_BACK | BUTTON_RECENTS;
|
||||
private int mLongPressedButtons = 0;
|
||||
|
||||
public TaskbarNavButtonController(TouchInteractionService service) {
|
||||
private final TouchInteractionService mService;
|
||||
private final SystemUiProxy mSystemUiProxy;
|
||||
private final Handler mHandler;
|
||||
|
||||
private final Runnable mResetLongPress = this::resetScreenUnpin;
|
||||
|
||||
public TaskbarNavButtonController(TouchInteractionService service,
|
||||
SystemUiProxy systemUiProxy, Handler handler) {
|
||||
mService = service;
|
||||
mSystemUiProxy = systemUiProxy;
|
||||
mHandler = handler;
|
||||
}
|
||||
|
||||
public void onButtonClick(@TaskbarButton int buttonType) {
|
||||
@@ -72,13 +91,13 @@ public class TaskbarNavButtonController {
|
||||
navigateHome();
|
||||
break;
|
||||
case BUTTON_RECENTS:
|
||||
navigateToOverview();;
|
||||
navigateToOverview();
|
||||
break;
|
||||
case BUTTON_IME_SWITCH:
|
||||
showIMESwitcher();
|
||||
break;
|
||||
case BUTTON_A11Y:
|
||||
notifyImeClick(false /* longClick */);
|
||||
notifyA11yClick(false /* longClick */);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -89,46 +108,98 @@ public class TaskbarNavButtonController {
|
||||
startAssistant();
|
||||
return true;
|
||||
case BUTTON_A11Y:
|
||||
notifyImeClick(true /* longClick */);
|
||||
notifyA11yClick(true /* longClick */);
|
||||
return true;
|
||||
case BUTTON_BACK:
|
||||
case BUTTON_IME_SWITCH:
|
||||
case BUTTON_RECENTS:
|
||||
mLongPressedButtons |= buttonType;
|
||||
return determineScreenUnpin();
|
||||
case BUTTON_IME_SWITCH:
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the user has long pressed back and recents buttons
|
||||
* "together" (within {@link #SCREEN_PIN_LONG_PRESS_THRESHOLD})ms
|
||||
* If so, then requests the system to turn off screen pinning.
|
||||
*
|
||||
* @return true if the long press is a valid user action in attempting to unpin an app
|
||||
* Will always return {@code false} when screen pinning is not active.
|
||||
* NOTE: Returning true does not mean that screen pinning has stopped
|
||||
*/
|
||||
private boolean determineScreenUnpin() {
|
||||
long timeNow = System.currentTimeMillis();
|
||||
if (!mScreenPinned) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (mLastScreenPinLongPress == 0) {
|
||||
// First button long press registered, just mark time and wait for second button press
|
||||
mLastScreenPinLongPress = System.currentTimeMillis();
|
||||
mHandler.postDelayed(mResetLongPress, SCREEN_PIN_LONG_PRESS_RESET);
|
||||
return true;
|
||||
}
|
||||
|
||||
if ((timeNow - mLastScreenPinLongPress) > SCREEN_PIN_LONG_PRESS_THRESHOLD) {
|
||||
// Too long in-between presses, reset the clock
|
||||
resetScreenUnpin();
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((mLongPressedButtons & SCREEN_UNPIN_COMBO) == SCREEN_UNPIN_COMBO) {
|
||||
// Hooray! They did it (finally...)
|
||||
mSystemUiProxy.stopScreenPinning();
|
||||
mHandler.removeCallbacks(mResetLongPress);
|
||||
resetScreenUnpin();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private void resetScreenUnpin() {
|
||||
mLongPressedButtons = 0;
|
||||
mLastScreenPinLongPress = 0;
|
||||
}
|
||||
|
||||
public void updateSysuiFlags(int sysuiFlags) {
|
||||
mScreenPinned = (sysuiFlags & SYSUI_STATE_SCREEN_PINNING) != 0;
|
||||
}
|
||||
|
||||
private void navigateHome() {
|
||||
mService.getOverviewCommandHelper().addCommand(OverviewCommandHelper.TYPE_HOME);
|
||||
}
|
||||
|
||||
private void navigateToOverview() {
|
||||
if (mScreenPinned) {
|
||||
return;
|
||||
}
|
||||
TestLogging.recordEvent(TestProtocol.SEQUENCE_MAIN, "onOverviewToggle");
|
||||
mService.getOverviewCommandHelper().addCommand(OverviewCommandHelper.TYPE_TOGGLE);
|
||||
}
|
||||
|
||||
private void executeBack() {
|
||||
SystemUiProxy.INSTANCE.getNoCreate().onBackPressed();
|
||||
mSystemUiProxy.onBackPressed();
|
||||
}
|
||||
|
||||
private void showIMESwitcher() {
|
||||
SystemUiProxy.INSTANCE.getNoCreate().onImeSwitcherPressed();
|
||||
mSystemUiProxy.onImeSwitcherPressed();
|
||||
}
|
||||
|
||||
private void notifyImeClick(boolean longClick) {
|
||||
SystemUiProxy systemUiProxy = SystemUiProxy.INSTANCE.getNoCreate();
|
||||
private void notifyA11yClick(boolean longClick) {
|
||||
if (longClick) {
|
||||
systemUiProxy.notifyAccessibilityButtonLongClicked();
|
||||
mSystemUiProxy.notifyAccessibilityButtonLongClicked();
|
||||
} else {
|
||||
systemUiProxy.notifyAccessibilityButtonClicked(mService.getDisplayId());
|
||||
mSystemUiProxy.notifyAccessibilityButtonClicked(mService.getDisplayId());
|
||||
}
|
||||
}
|
||||
|
||||
private void startAssistant() {
|
||||
if (mScreenPinned) {
|
||||
return;
|
||||
}
|
||||
Bundle args = new Bundle();
|
||||
args.putInt(INVOCATION_TYPE_KEY, INVOCATION_TYPE_HOME_BUTTON_LONG_PRESS);
|
||||
SystemUiProxy systemUiProxy = SystemUiProxy.INSTANCE.getNoCreate();
|
||||
systemUiProxy.startAssistant(args);
|
||||
mSystemUiProxy.startAssistant(args);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,6 +15,10 @@
|
||||
*/
|
||||
package com.android.launcher3.taskbar;
|
||||
|
||||
import android.graphics.Point;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import com.android.launcher3.BubbleTextView;
|
||||
@@ -30,6 +34,7 @@ import com.android.launcher3.popup.PopupContainerWithArrow;
|
||||
import com.android.launcher3.popup.PopupDataProvider;
|
||||
import com.android.launcher3.popup.PopupLiveUpdateHandler;
|
||||
import com.android.launcher3.popup.SystemShortcut;
|
||||
import com.android.launcher3.shortcuts.DeepShortcutView;
|
||||
import com.android.launcher3.util.ComponentKey;
|
||||
import com.android.launcher3.util.LauncherBindableItemsContainer;
|
||||
import com.android.launcher3.util.PackageUserKey;
|
||||
@@ -131,8 +136,14 @@ public class TaskbarPopupController {
|
||||
(PopupContainerWithArrow) context.getLayoutInflater().inflate(
|
||||
R.layout.popup_container, context.getDragLayer(), false);
|
||||
container.addOnAttachStateChangeListener(
|
||||
new PopupLiveUpdateHandler<>(mContext, container));
|
||||
new PopupLiveUpdateHandler<TaskbarActivityContext>(mContext, container) {
|
||||
@Override
|
||||
protected void showPopupContainerForIcon(BubbleTextView originalIcon) {
|
||||
showForIcon(originalIcon);
|
||||
}
|
||||
});
|
||||
// TODO (b/198438631): configure for taskbar/context
|
||||
container.setPopupItemDragHandler(new TaskbarPopupItemDragHandler());
|
||||
|
||||
container.populateAndShow(icon,
|
||||
mPopupDataProvider.getShortcutCountForItem(item),
|
||||
@@ -145,4 +156,43 @@ public class TaskbarPopupController {
|
||||
container.requestFocus();
|
||||
return container;
|
||||
}
|
||||
|
||||
private class TaskbarPopupItemDragHandler implements
|
||||
PopupContainerWithArrow.PopupItemDragHandler {
|
||||
|
||||
protected final Point mIconLastTouchPos = new Point();
|
||||
|
||||
TaskbarPopupItemDragHandler() {}
|
||||
|
||||
@Override
|
||||
public boolean onTouch(View view, MotionEvent ev) {
|
||||
// Touched a shortcut, update where it was touched so we can drag from there on
|
||||
// long click.
|
||||
switch (ev.getAction()) {
|
||||
case MotionEvent.ACTION_DOWN:
|
||||
case MotionEvent.ACTION_MOVE:
|
||||
mIconLastTouchPos.set((int) ev.getX(), (int) ev.getY());
|
||||
break;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onLongClick(View v) {
|
||||
// Return early if not the correct view
|
||||
if (!(v.getParent() instanceof DeepShortcutView)) return false;
|
||||
|
||||
DeepShortcutView sv = (DeepShortcutView) v.getParent();
|
||||
sv.setWillDrawIcon(false);
|
||||
|
||||
// Move the icon to align with the center-top of the touch point
|
||||
Point iconShift = new Point();
|
||||
iconShift.x = mIconLastTouchPos.x - sv.getIconCenter().x;
|
||||
iconShift.y = mIconLastTouchPos.y - mContext.getDeviceProfile().iconSizePx;
|
||||
|
||||
mControllers.taskbarDragController.startDragOnLongClick(sv, iconShift);
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+117
@@ -0,0 +1,117 @@
|
||||
/*
|
||||
* 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.taskbar;
|
||||
|
||||
import static com.android.launcher3.accessibility.LauncherAccessibilityDelegate.DEEP_SHORTCUTS;
|
||||
import static com.android.launcher3.accessibility.LauncherAccessibilityDelegate.SHORTCUTS_AND_NOTIFICATIONS;
|
||||
import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT;
|
||||
import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_TOP_OR_LEFT;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.content.pm.LauncherApps;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.View;
|
||||
|
||||
import com.android.launcher3.BubbleTextView;
|
||||
import com.android.launcher3.LauncherSettings;
|
||||
import com.android.launcher3.R;
|
||||
import com.android.launcher3.accessibility.BaseAccessibilityDelegate;
|
||||
import com.android.launcher3.config.FeatureFlags;
|
||||
import com.android.launcher3.model.data.ItemInfo;
|
||||
import com.android.launcher3.model.data.WorkspaceItemInfo;
|
||||
import com.android.launcher3.notification.NotificationListener;
|
||||
import com.android.launcher3.util.ShortcutUtil;
|
||||
import com.android.quickstep.SystemUiProxy;
|
||||
import com.android.systemui.shared.system.LauncherAppsCompat;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Accessibility delegate for the Taskbar. This provides an accessible interface for taskbar
|
||||
* features.
|
||||
*/
|
||||
public class TaskbarShortcutMenuAccessibilityDelegate
|
||||
extends BaseAccessibilityDelegate<TaskbarActivityContext> {
|
||||
|
||||
public static final int MOVE_TO_TOP_OR_LEFT = R.id.action_move_to_top_or_left;
|
||||
public static final int MOVE_TO_BOTTOM_OR_RIGHT = R.id.action_move_to_bottom_or_right;
|
||||
|
||||
private final LauncherApps mLauncherApps;
|
||||
|
||||
public TaskbarShortcutMenuAccessibilityDelegate(TaskbarActivityContext context) {
|
||||
super(context);
|
||||
mLauncherApps = context.getSystemService(LauncherApps.class);
|
||||
|
||||
mActions.put(DEEP_SHORTCUTS, new LauncherAction(DEEP_SHORTCUTS,
|
||||
R.string.action_deep_shortcut, KeyEvent.KEYCODE_S));
|
||||
mActions.put(SHORTCUTS_AND_NOTIFICATIONS, new LauncherAction(DEEP_SHORTCUTS,
|
||||
R.string.shortcuts_menu_with_notifications_description, KeyEvent.KEYCODE_S));
|
||||
mActions.put(MOVE_TO_TOP_OR_LEFT, new LauncherAction(
|
||||
MOVE_TO_TOP_OR_LEFT, R.string.move_drop_target_top_or_left, KeyEvent.KEYCODE_L));
|
||||
mActions.put(MOVE_TO_BOTTOM_OR_RIGHT, new LauncherAction(
|
||||
MOVE_TO_BOTTOM_OR_RIGHT,
|
||||
R.string.move_drop_target_bottom_or_right,
|
||||
KeyEvent.KEYCODE_R));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void getSupportedActions(View host, ItemInfo item, List<LauncherAction> out) {
|
||||
if (ShortcutUtil.supportsShortcuts(item) && FeatureFlags.ENABLE_TASKBAR_POPUP_MENU.get()) {
|
||||
out.add(mActions.get(NotificationListener.getInstanceIfConnected() != null
|
||||
? SHORTCUTS_AND_NOTIFICATIONS : DEEP_SHORTCUTS));
|
||||
}
|
||||
out.add(mActions.get(MOVE_TO_TOP_OR_LEFT));
|
||||
out.add(mActions.get(MOVE_TO_BOTTOM_OR_RIGHT));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean performAction(View host, ItemInfo item, int action, boolean fromKeyboard) {
|
||||
if (item instanceof WorkspaceItemInfo
|
||||
&& (action == MOVE_TO_TOP_OR_LEFT || action == MOVE_TO_BOTTOM_OR_RIGHT)) {
|
||||
WorkspaceItemInfo info = (WorkspaceItemInfo) item;
|
||||
int side = action == MOVE_TO_TOP_OR_LEFT
|
||||
? SPLIT_POSITION_TOP_OR_LEFT : SPLIT_POSITION_BOTTOM_OR_RIGHT;
|
||||
|
||||
if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT) {
|
||||
SystemUiProxy.INSTANCE.get(mContext).startShortcut(
|
||||
info.getIntent().getPackage(),
|
||||
info.getDeepShortcutId(),
|
||||
side,
|
||||
/* bundleOpts= */ null,
|
||||
info.user);
|
||||
} else {
|
||||
SystemUiProxy.INSTANCE.get(mContext).startIntent(
|
||||
LauncherAppsCompat.getMainActivityLaunchIntent(
|
||||
mLauncherApps,
|
||||
item.getIntent().getComponent(),
|
||||
/* startActivityOptions= */null,
|
||||
item.user),
|
||||
new Intent(), side, null);
|
||||
}
|
||||
return true;
|
||||
} else if (action == DEEP_SHORTCUTS || action == SHORTCUTS_AND_NOTIFICATIONS) {
|
||||
mContext.showPopupMenuForIcon((BubbleTextView) host);
|
||||
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean beginAccessibleDrag(View item, ItemInfo info, boolean fromKeyboard) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -18,6 +18,8 @@ package com.android.launcher3.taskbar;
|
||||
import android.graphics.Rect;
|
||||
import android.view.View;
|
||||
|
||||
import androidx.annotation.CallSuper;
|
||||
|
||||
import com.android.launcher3.model.data.ItemInfoWithIcon;
|
||||
import com.android.launcher3.model.data.WorkspaceItemInfo;
|
||||
|
||||
@@ -33,11 +35,15 @@ public class TaskbarUIController {
|
||||
// Initialized in init.
|
||||
protected TaskbarControllers mControllers;
|
||||
|
||||
@CallSuper
|
||||
protected void init(TaskbarControllers taskbarControllers) {
|
||||
mControllers = taskbarControllers;
|
||||
}
|
||||
|
||||
protected void onDestroy() { }
|
||||
@CallSuper
|
||||
protected void onDestroy() {
|
||||
mControllers = null;
|
||||
}
|
||||
|
||||
protected boolean isTaskbarTouchable() {
|
||||
return true;
|
||||
|
||||
@@ -66,6 +66,12 @@ public final class RecentsViewStateController extends
|
||||
// In Overview, we may be layering app surfaces behind Launcher, so we need to notify
|
||||
// DepthController to prevent optimizations which might occlude the layers behind
|
||||
mLauncher.getDepthController().setHasContentBehindLauncher(state.overviewUi);
|
||||
|
||||
if (isSplitSelectionState(state)) {
|
||||
mRecentsView.applySplitPrimaryScrollOffset();
|
||||
} else {
|
||||
mRecentsView.resetSplitPrimaryScrollOffset();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -90,9 +96,10 @@ public final class RecentsViewStateController extends
|
||||
LauncherState currentState = mLauncher.getStateManager().getState();
|
||||
if (isSplitSelectionState(toState) && !isSplitSelectionState(currentState)) {
|
||||
builder.add(mRecentsView.createSplitSelectInitAnimation().buildAnim());
|
||||
}
|
||||
if (isSplitSelectionState(toState)) {
|
||||
mRecentsView.applySplitPrimaryScrollOffset();
|
||||
} else if (!isSplitSelectionState(toState) && isSplitSelectionState(currentState)) {
|
||||
builder.add(mRecentsView.cancelSplitSelect(true).buildAnim());
|
||||
} else {
|
||||
mRecentsView.resetSplitPrimaryScrollOffset();
|
||||
}
|
||||
|
||||
|
||||
@@ -30,11 +30,6 @@ public class SplitScreenSelectState extends OverviewState {
|
||||
super(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBackPressed(Launcher launcher) {
|
||||
launcher.getStateManager().goToState(OVERVIEW);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getVisibleElements(Launcher launcher) {
|
||||
return SPLIT_PLACHOLDER_VIEW;
|
||||
|
||||
@@ -599,11 +599,11 @@ public class SystemUiProxy implements ISystemUiProxy,
|
||||
}
|
||||
}
|
||||
|
||||
public void startShortcut(String packageName, String shortcutId, int stage, int position,
|
||||
public void startShortcut(String packageName, String shortcutId, int position,
|
||||
Bundle options, UserHandle user) {
|
||||
if (mSplitScreen != null) {
|
||||
try {
|
||||
mSplitScreen.startShortcut(packageName, shortcutId, stage, position, options,
|
||||
mSplitScreen.startShortcut(packageName, shortcutId, position, options,
|
||||
user);
|
||||
} catch (RemoteException e) {
|
||||
Log.w(TAG, "Failed call startShortcut");
|
||||
@@ -611,11 +611,11 @@ public class SystemUiProxy implements ISystemUiProxy,
|
||||
}
|
||||
}
|
||||
|
||||
public void startIntent(PendingIntent intent, Intent fillInIntent, int stage, int position,
|
||||
public void startIntent(PendingIntent intent, Intent fillInIntent, int position,
|
||||
Bundle options) {
|
||||
if (mSplitScreen != null) {
|
||||
try {
|
||||
mSplitScreen.startIntent(intent, fillInIntent, stage, position, options);
|
||||
mSplitScreen.startIntent(intent, fillInIntent, position, options);
|
||||
} catch (RemoteException e) {
|
||||
Log.w(TAG, "Failed call startIntent");
|
||||
}
|
||||
|
||||
@@ -129,9 +129,7 @@ public class FloatingTaskView extends FrameLayout {
|
||||
public void update(RectF position, float progress, float windowRadius) {
|
||||
MarginLayoutParams lp = (MarginLayoutParams) getLayoutParams();
|
||||
|
||||
float dX = mIsRtl
|
||||
? position.left - (lp.getMarginStart() - lp.width)
|
||||
: position.left - lp.getMarginStart();
|
||||
float dX = position.left - lp.getMarginStart();
|
||||
float dY = position.top - lp.topMargin;
|
||||
|
||||
setTranslationX(dX);
|
||||
@@ -157,16 +155,10 @@ public class FloatingTaskView extends FrameLayout {
|
||||
lp.ignoreInsets = true;
|
||||
// Position the floating view exactly on top of the original
|
||||
lp.topMargin = Math.round(pos.top);
|
||||
if (mIsRtl) {
|
||||
lp.setMarginStart(Math.round(mLauncher.getDeviceProfile().widthPx - pos.right));
|
||||
} else {
|
||||
lp.setMarginStart(Math.round(pos.left));
|
||||
}
|
||||
lp.setMarginStart(Math.round(pos.left));
|
||||
// Set the properties here already to make sure they are available when running the first
|
||||
// animation frame.
|
||||
int left = mIsRtl
|
||||
? mLauncher.getDeviceProfile().widthPx - lp.getMarginStart() - lp.width
|
||||
: lp.leftMargin;
|
||||
int left = lp.leftMargin;
|
||||
layout(left, lp.topMargin, left + lp.width, lp.topMargin + lp.height);
|
||||
}
|
||||
|
||||
|
||||
@@ -157,10 +157,20 @@ public class GroupedTaskView extends TaskView {
|
||||
@Nullable
|
||||
@Override
|
||||
public RunnableList launchTaskAnimated() {
|
||||
getRecentsView().getSplitPlaceholder().launchTasks(this /*groupedTaskView*/,
|
||||
null /*callback*/,
|
||||
if (mTask == null || mSecondaryTask == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
RunnableList endCallback = new RunnableList();
|
||||
RecentsView recentsView = getRecentsView();
|
||||
// Callbacks run from remote animation when recents animation not currently running
|
||||
recentsView.getSplitPlaceholder().launchTasks(this /*groupedTaskView*/,
|
||||
success -> endCallback.executeAllAndDestroy(),
|
||||
false /* freezeTaskList */);
|
||||
return null;
|
||||
|
||||
// Callbacks get run from recentsView for case when recents animation already running
|
||||
recentsView.addSideTaskLaunchCallback(endCallback);
|
||||
return endCallback;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -248,4 +258,10 @@ public class GroupedTaskView extends TaskView {
|
||||
super.updateSnapshotRadius();
|
||||
mSnapshotView2.setFullscreenParams(mCurrentFullscreenParams);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void setIconAndDimTransitionProgress(float progress, boolean invert) {
|
||||
super.setIconAndDimTransitionProgress(progress, invert);
|
||||
mIconView2.setAlpha(mIconView.getAlpha());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1123,9 +1123,6 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T
|
||||
// Reset the running task when leaving overview since it can still have a reference to
|
||||
// its thumbnail
|
||||
mTmpRunningTasks = null;
|
||||
if (mSplitSelectStateController.isSplitSelectActive()) {
|
||||
cancelSplitSelect(false);
|
||||
}
|
||||
// Remove grouped tasks and recycle once we exit overview
|
||||
int taskCount = getTaskViewCount();
|
||||
for (int i = 0; i < taskCount; i++) {
|
||||
@@ -3940,109 +3937,6 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T
|
||||
pendingAnimation.buildAnim().start();
|
||||
}
|
||||
|
||||
public PendingAnimation cancelSplitSelect(boolean animate) {
|
||||
SplitSelectStateController splitController = mSplitSelectStateController;
|
||||
@StagePosition int stagePosition = splitController.getActiveSplitStagePosition();
|
||||
Rect initialBounds = splitController.getInitialBounds();
|
||||
splitController.resetState();
|
||||
int duration = mActivity.getStateManager().getState().getTransitionDuration(getContext());
|
||||
PendingAnimation pendingAnim = new PendingAnimation(duration);
|
||||
mSplitToast.cancel();
|
||||
mSplitUnsupportedToast.cancel();
|
||||
if (!animate) {
|
||||
resetFromSplitSelectionState();
|
||||
return pendingAnim;
|
||||
}
|
||||
|
||||
addViewInLayout(mSplitHiddenTaskView, mSplitHiddenTaskViewIndex,
|
||||
mSplitHiddenTaskView.getLayoutParams());
|
||||
mSplitHiddenTaskView.setAlpha(0);
|
||||
int[] oldScroll = new int[getChildCount()];
|
||||
getPageScrolls(oldScroll, false,
|
||||
view -> view.getVisibility() != GONE && view != mSplitHiddenTaskView);
|
||||
|
||||
int[] newScroll = new int[getChildCount()];
|
||||
getPageScrolls(newScroll, false, SIMPLE_SCROLL_LOGIC);
|
||||
|
||||
boolean needsCurveUpdates = false;
|
||||
for (int i = mSplitHiddenTaskViewIndex; i >= 0; i--) {
|
||||
View child = getChildAt(i);
|
||||
if (child == mSplitHiddenTaskView) {
|
||||
TaskView taskView = (TaskView) child;
|
||||
|
||||
int dir = mOrientationHandler.getSplitTaskViewDismissDirection(stagePosition,
|
||||
mActivity.getDeviceProfile());
|
||||
FloatProperty<TaskView> dismissingTaskViewTranslate;
|
||||
Rect hiddenBounds = new Rect(taskView.getLeft(), taskView.getTop(),
|
||||
taskView.getRight(), taskView.getBottom());
|
||||
int distanceDelta = 0;
|
||||
if (dir == PagedOrientationHandler.SPLIT_TRANSLATE_SECONDARY_NEGATIVE) {
|
||||
dismissingTaskViewTranslate = taskView
|
||||
.getSecondaryDissmissTranslationProperty();
|
||||
distanceDelta = initialBounds.top - hiddenBounds.top;
|
||||
taskView.layout(initialBounds.left, hiddenBounds.top, initialBounds.right,
|
||||
hiddenBounds.bottom);
|
||||
} else {
|
||||
dismissingTaskViewTranslate = taskView
|
||||
.getPrimaryDismissTranslationProperty();
|
||||
distanceDelta = initialBounds.left - hiddenBounds.left;
|
||||
taskView.layout(hiddenBounds.left, initialBounds.top, hiddenBounds.right,
|
||||
initialBounds.bottom);
|
||||
if (dir == PagedOrientationHandler.SPLIT_TRANSLATE_PRIMARY_POSITIVE) {
|
||||
distanceDelta *= -1;
|
||||
}
|
||||
}
|
||||
pendingAnim.add(ObjectAnimator.ofFloat(mSplitHiddenTaskView,
|
||||
dismissingTaskViewTranslate,
|
||||
distanceDelta));
|
||||
pendingAnim.add(ObjectAnimator.ofFloat(mSplitHiddenTaskView, ALPHA, 1));
|
||||
} else {
|
||||
// If insertion is on last index (furthest from clear all), we directly add the view
|
||||
// else we translate all views to the right of insertion index further right,
|
||||
// ignore views to left
|
||||
if (showAsGrid()) {
|
||||
// TODO(b/186800707) handle more elegantly for grid
|
||||
continue;
|
||||
}
|
||||
int scrollDiff = newScroll[i] - oldScroll[i];
|
||||
if (scrollDiff != 0) {
|
||||
FloatProperty translationProperty = child instanceof TaskView
|
||||
? ((TaskView) child).getPrimaryDismissTranslationProperty()
|
||||
: mOrientationHandler.getPrimaryViewTranslate();
|
||||
|
||||
ResourceProvider rp = DynamicResource.provider(mActivity);
|
||||
SpringProperty sp = new SpringProperty(SpringProperty.FLAG_CAN_SPRING_ON_END)
|
||||
.setDampingRatio(
|
||||
rp.getFloat(R.dimen.dismiss_task_trans_x_damping_ratio))
|
||||
.setStiffness(rp.getFloat(R.dimen.dismiss_task_trans_x_stiffness));
|
||||
pendingAnim.add(ObjectAnimator.ofFloat(child, translationProperty, scrollDiff)
|
||||
.setDuration(duration), ACCEL, sp);
|
||||
needsCurveUpdates = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (needsCurveUpdates) {
|
||||
pendingAnim.addOnFrameCallback(this::updateCurveProperties);
|
||||
}
|
||||
|
||||
pendingAnim.addListener(new AnimatorListenerAdapter() {
|
||||
@Override
|
||||
public void onAnimationEnd(Animator animation) {
|
||||
// TODO(b/186800707) Figure out how to undo for grid view
|
||||
// Need to handle cases where dismissed task is
|
||||
// * Top Row
|
||||
// * Bottom Row
|
||||
// * Focused Task
|
||||
updateGridProperties();
|
||||
resetFromSplitSelectionState();
|
||||
updateScrollSynchronously();
|
||||
}
|
||||
});
|
||||
|
||||
return pendingAnim;
|
||||
}
|
||||
|
||||
/** TODO(b/181707736) More gracefully handle exiting split selection state */
|
||||
private void resetFromSplitSelectionState() {
|
||||
if (!mActivity.getDeviceProfile().overviewShowAsGrid) {
|
||||
|
||||
@@ -912,7 +912,7 @@ public class TaskView extends FrameLayout implements Reusable {
|
||||
return deviceProfile.overviewShowAsGrid && !isFocusedTask();
|
||||
}
|
||||
|
||||
private void setIconAndDimTransitionProgress(float progress, boolean invert) {
|
||||
protected void setIconAndDimTransitionProgress(float progress, boolean invert) {
|
||||
if (invert) {
|
||||
progress = 1 - progress;
|
||||
}
|
||||
|
||||
+159
@@ -0,0 +1,159 @@
|
||||
package com.android.launcher3.taskbar;
|
||||
|
||||
import static com.android.launcher3.taskbar.TaskbarNavButtonController.BUTTON_A11Y;
|
||||
import static com.android.launcher3.taskbar.TaskbarNavButtonController.BUTTON_BACK;
|
||||
import static com.android.launcher3.taskbar.TaskbarNavButtonController.BUTTON_HOME;
|
||||
import static com.android.launcher3.taskbar.TaskbarNavButtonController.BUTTON_IME_SWITCH;
|
||||
import static com.android.launcher3.taskbar.TaskbarNavButtonController.BUTTON_RECENTS;
|
||||
import static com.android.launcher3.taskbar.TaskbarNavButtonController.SCREEN_PIN_LONG_PRESS_THRESHOLD;
|
||||
import static com.android.quickstep.OverviewCommandHelper.TYPE_HOME;
|
||||
import static com.android.quickstep.OverviewCommandHelper.TYPE_TOGGLE;
|
||||
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_SCREEN_PINNING;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import android.os.Handler;
|
||||
|
||||
import androidx.test.runner.AndroidJUnit4;
|
||||
|
||||
import com.android.quickstep.OverviewCommandHelper;
|
||||
import com.android.quickstep.SystemUiProxy;
|
||||
import com.android.quickstep.TouchInteractionService;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class TaskbarNavButtonControllerTest {
|
||||
|
||||
private final static int DISPLAY_ID = 2;
|
||||
|
||||
@Mock
|
||||
SystemUiProxy mockSystemUiProxy;
|
||||
@Mock
|
||||
TouchInteractionService mockService;
|
||||
@Mock
|
||||
OverviewCommandHelper mockCommandHelper;
|
||||
@Mock
|
||||
Handler mockHandler;
|
||||
|
||||
private TaskbarNavButtonController mNavButtonController;
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
when(mockService.getDisplayId()).thenReturn(DISPLAY_ID);
|
||||
when(mockService.getOverviewCommandHelper()).thenReturn(mockCommandHelper);
|
||||
mNavButtonController = new TaskbarNavButtonController(mockService,
|
||||
mockSystemUiProxy, mockHandler);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPressBack() {
|
||||
mNavButtonController.onButtonClick(BUTTON_BACK);
|
||||
verify(mockSystemUiProxy, times(1)).onBackPressed();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPressImeSwitcher() {
|
||||
mNavButtonController.onButtonClick(BUTTON_IME_SWITCH);
|
||||
verify(mockSystemUiProxy, times(1)).onImeSwitcherPressed();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPressA11yShortClick() {
|
||||
mNavButtonController.onButtonClick(BUTTON_A11Y);
|
||||
verify(mockSystemUiProxy, times(1))
|
||||
.notifyAccessibilityButtonClicked(DISPLAY_ID);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPressA11yLongClick() {
|
||||
mNavButtonController.onButtonLongClick(BUTTON_A11Y);
|
||||
verify(mockSystemUiProxy, times(1)).notifyAccessibilityButtonLongClicked();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLongPressHome() {
|
||||
mNavButtonController.onButtonLongClick(BUTTON_HOME);
|
||||
verify(mockSystemUiProxy, times(1)).startAssistant(any());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPressHome() {
|
||||
mNavButtonController.onButtonClick(BUTTON_HOME);
|
||||
verify(mockCommandHelper, times(1)).addCommand(TYPE_HOME);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPressRecents() {
|
||||
mNavButtonController.onButtonClick(BUTTON_RECENTS);
|
||||
verify(mockCommandHelper, times(1)).addCommand(TYPE_TOGGLE);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPressRecentsWithScreenPinned() {
|
||||
mNavButtonController.updateSysuiFlags(SYSUI_STATE_SCREEN_PINNING);
|
||||
mNavButtonController.onButtonClick(BUTTON_RECENTS);
|
||||
verify(mockCommandHelper, times(0)).addCommand(TYPE_TOGGLE);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLongPressBackRecentsNotPinned() {
|
||||
mNavButtonController.onButtonLongClick(BUTTON_RECENTS);
|
||||
mNavButtonController.onButtonLongClick(BUTTON_BACK);
|
||||
verify(mockSystemUiProxy, times(0)).stopScreenPinning();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLongPressBackRecentsPinned() {
|
||||
mNavButtonController.updateSysuiFlags(SYSUI_STATE_SCREEN_PINNING);
|
||||
mNavButtonController.onButtonLongClick(BUTTON_RECENTS);
|
||||
mNavButtonController.onButtonLongClick(BUTTON_BACK);
|
||||
verify(mockSystemUiProxy, times(1)).stopScreenPinning();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLongPressBackRecentsTooLongPinned() {
|
||||
mNavButtonController.updateSysuiFlags(SYSUI_STATE_SCREEN_PINNING);
|
||||
mNavButtonController.onButtonLongClick(BUTTON_RECENTS);
|
||||
try {
|
||||
Thread.sleep(SCREEN_PIN_LONG_PRESS_THRESHOLD + 5);
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
mNavButtonController.onButtonLongClick(BUTTON_BACK);
|
||||
verify(mockSystemUiProxy, times(0)).stopScreenPinning();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLongPressBackRecentsMultipleAttemptPinned() {
|
||||
mNavButtonController.updateSysuiFlags(SYSUI_STATE_SCREEN_PINNING);
|
||||
mNavButtonController.onButtonLongClick(BUTTON_RECENTS);
|
||||
try {
|
||||
Thread.sleep(SCREEN_PIN_LONG_PRESS_THRESHOLD + 5);
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
mNavButtonController.onButtonLongClick(BUTTON_BACK);
|
||||
verify(mockSystemUiProxy, times(0)).stopScreenPinning();
|
||||
|
||||
// Try again w/in threshold
|
||||
mNavButtonController.onButtonLongClick(BUTTON_RECENTS);
|
||||
mNavButtonController.onButtonLongClick(BUTTON_BACK);
|
||||
verify(mockSystemUiProxy, times(1)).stopScreenPinning();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLongPressHomeScreenPinned() {
|
||||
mNavButtonController.updateSysuiFlags(SYSUI_STATE_SCREEN_PINNING);
|
||||
mNavButtonController.onButtonLongClick(BUTTON_HOME);
|
||||
verify(mockSystemUiProxy, times(0)).startAssistant(any());
|
||||
}
|
||||
}
|
||||
@@ -48,7 +48,7 @@ import android.widget.TextView;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.UiThread;
|
||||
|
||||
import com.android.launcher3.accessibility.LauncherAccessibilityDelegate;
|
||||
import com.android.launcher3.accessibility.BaseAccessibilityDelegate;
|
||||
import com.android.launcher3.dot.DotInfo;
|
||||
import com.android.launcher3.dragndrop.DraggableView;
|
||||
import com.android.launcher3.folder.FolderIcon;
|
||||
@@ -296,7 +296,7 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver,
|
||||
|
||||
@Override
|
||||
public void setAccessibilityDelegate(AccessibilityDelegate delegate) {
|
||||
if (delegate instanceof LauncherAccessibilityDelegate) {
|
||||
if (delegate instanceof BaseAccessibilityDelegate) {
|
||||
super.setAccessibilityDelegate(delegate);
|
||||
} else {
|
||||
// NO-OP
|
||||
|
||||
@@ -115,8 +115,8 @@ import androidx.annotation.StringRes;
|
||||
import androidx.annotation.VisibleForTesting;
|
||||
|
||||
import com.android.launcher3.DropTarget.DragObject;
|
||||
import com.android.launcher3.accessibility.BaseAccessibilityDelegate.LauncherAction;
|
||||
import com.android.launcher3.accessibility.LauncherAccessibilityDelegate;
|
||||
import com.android.launcher3.accessibility.LauncherAccessibilityDelegate.LauncherAction;
|
||||
import com.android.launcher3.allapps.AllAppsContainerView;
|
||||
import com.android.launcher3.allapps.AllAppsStore;
|
||||
import com.android.launcher3.allapps.AllAppsTransitionController;
|
||||
|
||||
@@ -0,0 +1,192 @@
|
||||
/*
|
||||
* 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.accessibility;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Rect;
|
||||
import android.os.Bundle;
|
||||
import android.text.TextUtils;
|
||||
import android.util.SparseArray;
|
||||
import android.view.View;
|
||||
import android.view.accessibility.AccessibilityNodeInfo;
|
||||
|
||||
import com.android.launcher3.DropTarget;
|
||||
import com.android.launcher3.LauncherSettings;
|
||||
import com.android.launcher3.dragndrop.DragController;
|
||||
import com.android.launcher3.dragndrop.DragOptions;
|
||||
import com.android.launcher3.model.data.FolderInfo;
|
||||
import com.android.launcher3.model.data.ItemInfo;
|
||||
import com.android.launcher3.model.data.LauncherAppWidgetInfo;
|
||||
import com.android.launcher3.model.data.WorkspaceItemInfo;
|
||||
import com.android.launcher3.popup.PopupContainerWithArrow;
|
||||
import com.android.launcher3.util.Thunk;
|
||||
import com.android.launcher3.views.ActivityContext;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public abstract class BaseAccessibilityDelegate<T extends Context & ActivityContext>
|
||||
extends View.AccessibilityDelegate implements DragController.DragListener {
|
||||
|
||||
public enum DragType {
|
||||
ICON,
|
||||
FOLDER,
|
||||
WIDGET
|
||||
}
|
||||
|
||||
public static class DragInfo {
|
||||
public DragType dragType;
|
||||
public ItemInfo info;
|
||||
public View item;
|
||||
}
|
||||
|
||||
protected final SparseArray<LauncherAction> mActions = new SparseArray<>();
|
||||
protected final T mContext;
|
||||
|
||||
protected DragInfo mDragInfo = null;
|
||||
|
||||
protected BaseAccessibilityDelegate(T context) {
|
||||
mContext = context;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfo info) {
|
||||
super.onInitializeAccessibilityNodeInfo(host, info);
|
||||
if (host.getTag() instanceof ItemInfo) {
|
||||
ItemInfo item = (ItemInfo) host.getTag();
|
||||
|
||||
List<LauncherAction> actions = new ArrayList<>();
|
||||
getSupportedActions(host, item, actions);
|
||||
actions.forEach(la -> info.addAction(la.accessibilityAction));
|
||||
|
||||
if (!itemSupportsLongClick(host, item)) {
|
||||
info.setLongClickable(false);
|
||||
info.removeAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_LONG_CLICK);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds all the accessibility actions that can be handled.
|
||||
*/
|
||||
protected abstract void getSupportedActions(View host, ItemInfo item, List<LauncherAction> out);
|
||||
|
||||
private boolean itemSupportsLongClick(View host, ItemInfo info) {
|
||||
return PopupContainerWithArrow.canShow(host, info);
|
||||
}
|
||||
|
||||
protected boolean itemSupportsAccessibleDrag(ItemInfo item) {
|
||||
if (item instanceof WorkspaceItemInfo) {
|
||||
// Support the action unless the item is in a context menu.
|
||||
return item.screenId >= 0
|
||||
&& item.container != LauncherSettings.Favorites.CONTAINER_HOTSEAT_PREDICTION;
|
||||
}
|
||||
return (item instanceof LauncherAppWidgetInfo)
|
||||
|| (item instanceof FolderInfo);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean performAccessibilityAction(View host, int action, Bundle args) {
|
||||
if ((host.getTag() instanceof ItemInfo)
|
||||
&& performAction(host, (ItemInfo) host.getTag(), action, false)) {
|
||||
return true;
|
||||
}
|
||||
return super.performAccessibilityAction(host, action, args);
|
||||
}
|
||||
|
||||
protected abstract boolean performAction(
|
||||
View host, ItemInfo item, int action, boolean fromKeyboard);
|
||||
|
||||
@Thunk
|
||||
protected void announceConfirmation(String confirmation) {
|
||||
mContext.getDragLayer().announceForAccessibility(confirmation);
|
||||
|
||||
}
|
||||
|
||||
public boolean isInAccessibleDrag() {
|
||||
return mDragInfo != null;
|
||||
}
|
||||
|
||||
public DragInfo getDragInfo() {
|
||||
return mDragInfo;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param clickedTarget the actual view that was clicked
|
||||
* @param dropLocation relative to {@param clickedTarget}. If provided, its center is used
|
||||
* as the actual drop location otherwise the views center is used.
|
||||
*/
|
||||
public void handleAccessibleDrop(View clickedTarget, Rect dropLocation,
|
||||
String confirmation) {
|
||||
if (!isInAccessibleDrag()) return;
|
||||
|
||||
int[] loc = new int[2];
|
||||
if (dropLocation == null) {
|
||||
loc[0] = clickedTarget.getWidth() / 2;
|
||||
loc[1] = clickedTarget.getHeight() / 2;
|
||||
} else {
|
||||
loc[0] = dropLocation.centerX();
|
||||
loc[1] = dropLocation.centerY();
|
||||
}
|
||||
|
||||
mContext.getDragLayer().getDescendantCoordRelativeToSelf(clickedTarget, loc);
|
||||
mContext.getDragController().completeAccessibleDrag(loc);
|
||||
|
||||
if (!TextUtils.isEmpty(confirmation)) {
|
||||
announceConfirmation(confirmation);
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract boolean beginAccessibleDrag(View item, ItemInfo info, boolean fromKeyboard);
|
||||
|
||||
|
||||
@Override
|
||||
public void onDragEnd() {
|
||||
mContext.getDragController().removeDragListener(this);
|
||||
mDragInfo = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDragStart(DropTarget.DragObject dragObject, DragOptions options) {
|
||||
// No-op
|
||||
}
|
||||
|
||||
public class LauncherAction {
|
||||
public final int keyCode;
|
||||
public final AccessibilityNodeInfo.AccessibilityAction accessibilityAction;
|
||||
|
||||
private final BaseAccessibilityDelegate<T> mDelegate;
|
||||
|
||||
public LauncherAction(int id, int labelRes, int keyCode) {
|
||||
this.keyCode = keyCode;
|
||||
accessibilityAction = new AccessibilityNodeInfo.AccessibilityAction(
|
||||
id, mContext.getString(labelRes));
|
||||
mDelegate = BaseAccessibilityDelegate.this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Invokes the action for the provided host
|
||||
*/
|
||||
public boolean invokeFromKeyboard(View host) {
|
||||
if (host != null && host.getTag() instanceof ItemInfo) {
|
||||
return mDelegate.performAction(
|
||||
host, (ItemInfo) host.getTag(), accessibilityAction.getId(), true);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -10,27 +10,19 @@ import android.appwidget.AppWidgetProviderInfo;
|
||||
import android.graphics.Point;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.RectF;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
import android.util.SparseArray;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.View;
|
||||
import android.view.View.AccessibilityDelegate;
|
||||
import android.view.accessibility.AccessibilityNodeInfo;
|
||||
import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
|
||||
|
||||
import com.android.launcher3.BubbleTextView;
|
||||
import com.android.launcher3.ButtonDropTarget;
|
||||
import com.android.launcher3.CellLayout;
|
||||
import com.android.launcher3.DropTarget.DragObject;
|
||||
import com.android.launcher3.Launcher;
|
||||
import com.android.launcher3.LauncherSettings.Favorites;
|
||||
import com.android.launcher3.PendingAddItemInfo;
|
||||
import com.android.launcher3.R;
|
||||
import com.android.launcher3.Workspace;
|
||||
import com.android.launcher3.dragndrop.DragController.DragListener;
|
||||
import com.android.launcher3.dragndrop.DragOptions;
|
||||
import com.android.launcher3.dragndrop.DragView;
|
||||
import com.android.launcher3.folder.Folder;
|
||||
@@ -57,7 +49,7 @@ import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
public class LauncherAccessibilityDelegate extends AccessibilityDelegate implements DragListener {
|
||||
public class LauncherAccessibilityDelegate extends BaseAccessibilityDelegate<Launcher> {
|
||||
|
||||
private static final String TAG = "LauncherAccessibilityDelegate";
|
||||
|
||||
@@ -73,25 +65,8 @@ public class LauncherAccessibilityDelegate extends AccessibilityDelegate impleme
|
||||
public static final int DEEP_SHORTCUTS = R.id.action_deep_shortcuts;
|
||||
public static final int SHORTCUTS_AND_NOTIFICATIONS = R.id.action_shortcuts_and_notifications;
|
||||
|
||||
public enum DragType {
|
||||
ICON,
|
||||
FOLDER,
|
||||
WIDGET
|
||||
}
|
||||
|
||||
public static class DragInfo {
|
||||
public DragType dragType;
|
||||
public ItemInfo info;
|
||||
public View item;
|
||||
}
|
||||
|
||||
protected final SparseArray<LauncherAction> mActions = new SparseArray<>();
|
||||
protected final Launcher mLauncher;
|
||||
|
||||
private DragInfo mDragInfo = null;
|
||||
|
||||
public LauncherAccessibilityDelegate(Launcher launcher) {
|
||||
mLauncher = launcher;
|
||||
super(launcher);
|
||||
|
||||
mActions.put(REMOVE, new LauncherAction(
|
||||
REMOVE, R.string.remove_drop_target_label, KeyEvent.KEYCODE_X));
|
||||
@@ -116,25 +91,6 @@ public class LauncherAccessibilityDelegate extends AccessibilityDelegate impleme
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfo info) {
|
||||
super.onInitializeAccessibilityNodeInfo(host, info);
|
||||
if (host.getTag() instanceof ItemInfo) {
|
||||
ItemInfo item = (ItemInfo) host.getTag();
|
||||
|
||||
List<LauncherAction> actions = new ArrayList<>();
|
||||
getSupportedActions(host, item, actions);
|
||||
actions.forEach(la -> info.addAction(la.accessibilityAction));
|
||||
|
||||
if (!itemSupportsLongClick(host, item)) {
|
||||
info.setLongClickable(false);
|
||||
info.removeAction(AccessibilityAction.ACTION_LONG_CLICK);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds all the accessibility actions that can be handled.
|
||||
*/
|
||||
protected void getSupportedActions(View host, ItemInfo item, List<LauncherAction> out) {
|
||||
// If the request came from keyboard, do not add custom shortcuts as that is already
|
||||
// exposed as a direct shortcut
|
||||
@@ -143,7 +99,7 @@ public class LauncherAccessibilityDelegate extends AccessibilityDelegate impleme
|
||||
? SHORTCUTS_AND_NOTIFICATIONS : DEEP_SHORTCUTS));
|
||||
}
|
||||
|
||||
for (ButtonDropTarget target : mLauncher.getDropTargetBar().getDropTargets()) {
|
||||
for (ButtonDropTarget target : mContext.getDropTargetBar().getDropTargets()) {
|
||||
if (target.supportsAccessibilityDrop(item, host)) {
|
||||
out.add(mActions.get(target.getAccessibilityAction()));
|
||||
}
|
||||
@@ -183,31 +139,7 @@ public class LauncherAccessibilityDelegate extends AccessibilityDelegate impleme
|
||||
return result;
|
||||
}
|
||||
|
||||
private boolean itemSupportsLongClick(View host, ItemInfo info) {
|
||||
return PopupContainerWithArrow.canShow(host, info);
|
||||
}
|
||||
|
||||
private boolean itemSupportsAccessibleDrag(ItemInfo item) {
|
||||
if (item instanceof WorkspaceItemInfo) {
|
||||
// Support the action unless the item is in a context menu.
|
||||
return item.screenId >= 0 && item.container != Favorites.CONTAINER_HOTSEAT_PREDICTION;
|
||||
}
|
||||
return (item instanceof LauncherAppWidgetInfo)
|
||||
|| (item instanceof FolderInfo);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean performAccessibilityAction(View host, int action, Bundle args) {
|
||||
if ((host.getTag() instanceof ItemInfo)
|
||||
&& performAction(host, (ItemInfo) host.getTag(), action, false)) {
|
||||
return true;
|
||||
}
|
||||
return super.performAccessibilityAction(host, action, args);
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs the provided action on the host
|
||||
*/
|
||||
protected boolean performAction(final View host, final ItemInfo item, int action,
|
||||
boolean fromKeyboard) {
|
||||
if (action == ACTION_LONG_CLICK) {
|
||||
@@ -226,36 +158,36 @@ public class LauncherAccessibilityDelegate extends AccessibilityDelegate impleme
|
||||
if (screenId == -1) {
|
||||
return false;
|
||||
}
|
||||
mLauncher.getStateManager().goToState(NORMAL, true, forSuccessCallback(() -> {
|
||||
mContext.getStateManager().goToState(NORMAL, true, forSuccessCallback(() -> {
|
||||
if (item instanceof AppInfo) {
|
||||
WorkspaceItemInfo info = ((AppInfo) item).makeWorkspaceItem();
|
||||
mLauncher.getModelWriter().addItemToDatabase(info,
|
||||
mContext.getModelWriter().addItemToDatabase(info,
|
||||
Favorites.CONTAINER_DESKTOP,
|
||||
screenId, coordinates[0], coordinates[1]);
|
||||
|
||||
mLauncher.bindItems(
|
||||
mContext.bindItems(
|
||||
Collections.singletonList(info),
|
||||
/* forceAnimateIcons= */ true,
|
||||
/* focusFirstItemForAccessibility= */ true);
|
||||
announceConfirmation(R.string.item_added_to_workspace);
|
||||
} else if (item instanceof PendingAddItemInfo) {
|
||||
PendingAddItemInfo info = (PendingAddItemInfo) item;
|
||||
Workspace workspace = mLauncher.getWorkspace();
|
||||
Workspace workspace = mContext.getWorkspace();
|
||||
workspace.snapToPage(workspace.getPageIndexForScreenId(screenId));
|
||||
mLauncher.addPendingItem(info, Favorites.CONTAINER_DESKTOP,
|
||||
mContext.addPendingItem(info, Favorites.CONTAINER_DESKTOP,
|
||||
screenId, coordinates, info.spanX, info.spanY);
|
||||
}
|
||||
else if (item instanceof WorkspaceItemInfo) {
|
||||
WorkspaceItemInfo info = ((WorkspaceItemInfo) item).clone();
|
||||
mLauncher.getModelWriter().addItemToDatabase(info,
|
||||
mContext.getModelWriter().addItemToDatabase(info,
|
||||
Favorites.CONTAINER_DESKTOP,
|
||||
screenId, coordinates[0], coordinates[1]);
|
||||
mLauncher.bindItems(Collections.singletonList(info), true, true);
|
||||
mContext.bindItems(Collections.singletonList(info), true, true);
|
||||
}
|
||||
}));
|
||||
return true;
|
||||
} else if (action == MOVE_TO_WORKSPACE) {
|
||||
Folder folder = Folder.getOpen(mLauncher);
|
||||
Folder folder = Folder.getOpen(mContext);
|
||||
folder.close(true);
|
||||
WorkspaceItemInfo info = (WorkspaceItemInfo) item;
|
||||
folder.getInfo().remove(info, false);
|
||||
@@ -265,14 +197,14 @@ public class LauncherAccessibilityDelegate extends AccessibilityDelegate impleme
|
||||
if (screenId == -1) {
|
||||
return false;
|
||||
}
|
||||
mLauncher.getModelWriter().moveItemInDatabase(info,
|
||||
mContext.getModelWriter().moveItemInDatabase(info,
|
||||
Favorites.CONTAINER_DESKTOP,
|
||||
screenId, coordinates[0], coordinates[1]);
|
||||
|
||||
// Bind the item in next frame so that if a new workspace page was created,
|
||||
// it will get laid out.
|
||||
new Handler().post(() -> {
|
||||
mLauncher.bindItems(Collections.singletonList(item), true);
|
||||
mContext.bindItems(Collections.singletonList(item), true);
|
||||
announceConfirmation(R.string.item_moved);
|
||||
});
|
||||
return true;
|
||||
@@ -280,15 +212,15 @@ public class LauncherAccessibilityDelegate extends AccessibilityDelegate impleme
|
||||
final LauncherAppWidgetInfo info = (LauncherAppWidgetInfo) item;
|
||||
List<OptionItem> actions = getSupportedResizeActions(host, info);
|
||||
Rect pos = new Rect();
|
||||
mLauncher.getDragLayer().getDescendantRectRelativeToSelf(host, pos);
|
||||
ArrowPopup popup = OptionsPopupView.show(mLauncher, new RectF(pos), actions, false);
|
||||
mContext.getDragLayer().getDescendantRectRelativeToSelf(host, pos);
|
||||
ArrowPopup popup = OptionsPopupView.show(mContext, new RectF(pos), actions, false);
|
||||
popup.requestFocus();
|
||||
popup.setOnCloseCallback(host::requestFocus);
|
||||
return true;
|
||||
} else if (action == DEEP_SHORTCUTS || action == SHORTCUTS_AND_NOTIFICATIONS) {
|
||||
return PopupContainerWithArrow.showForIcon((BubbleTextView) host) != null;
|
||||
} else {
|
||||
for (ButtonDropTarget dropTarget : mLauncher.getDropTargetBar().getDropTargets()) {
|
||||
for (ButtonDropTarget dropTarget : mContext.getDropTargetBar().getDropTargets()) {
|
||||
if (dropTarget.supportsAccessibilityDrop(item, host)
|
||||
&& action == dropTarget.getAccessibilityAction()) {
|
||||
dropTarget.onAccessibilityDrop(host, item);
|
||||
@@ -315,7 +247,7 @@ public class LauncherAccessibilityDelegate extends AccessibilityDelegate impleme
|
||||
if ((providerInfo.resizeMode & AppWidgetProviderInfo.RESIZE_HORIZONTAL) != 0) {
|
||||
if (layout.isRegionVacant(info.cellX + info.spanX, info.cellY, 1, info.spanY) ||
|
||||
layout.isRegionVacant(info.cellX - 1, info.cellY, 1, info.spanY)) {
|
||||
actions.add(new OptionItem(mLauncher,
|
||||
actions.add(new OptionItem(mContext,
|
||||
R.string.action_increase_width,
|
||||
R.drawable.ic_widget_width_increase,
|
||||
IGNORE,
|
||||
@@ -323,7 +255,7 @@ public class LauncherAccessibilityDelegate extends AccessibilityDelegate impleme
|
||||
}
|
||||
|
||||
if (info.spanX > info.minSpanX && info.spanX > 1) {
|
||||
actions.add(new OptionItem(mLauncher,
|
||||
actions.add(new OptionItem(mContext,
|
||||
R.string.action_decrease_width,
|
||||
R.drawable.ic_widget_width_decrease,
|
||||
IGNORE,
|
||||
@@ -334,7 +266,7 @@ public class LauncherAccessibilityDelegate extends AccessibilityDelegate impleme
|
||||
if ((providerInfo.resizeMode & AppWidgetProviderInfo.RESIZE_VERTICAL) != 0) {
|
||||
if (layout.isRegionVacant(info.cellX, info.cellY + info.spanY, info.spanX, 1) ||
|
||||
layout.isRegionVacant(info.cellX, info.cellY - 1, info.spanX, 1)) {
|
||||
actions.add(new OptionItem(mLauncher,
|
||||
actions.add(new OptionItem(mContext,
|
||||
R.string.action_increase_height,
|
||||
R.drawable.ic_widget_height_increase,
|
||||
IGNORE,
|
||||
@@ -342,7 +274,7 @@ public class LauncherAccessibilityDelegate extends AccessibilityDelegate impleme
|
||||
}
|
||||
|
||||
if (info.spanY > info.minSpanY && info.spanY > 1) {
|
||||
actions.add(new OptionItem(mLauncher,
|
||||
actions.add(new OptionItem(mContext,
|
||||
R.string.action_decrease_height,
|
||||
R.drawable.ic_widget_height_decrease,
|
||||
IGNORE,
|
||||
@@ -382,58 +314,20 @@ public class LauncherAccessibilityDelegate extends AccessibilityDelegate impleme
|
||||
}
|
||||
|
||||
layout.markCellsAsOccupiedForView(host);
|
||||
WidgetSizes.updateWidgetSizeRanges(((LauncherAppWidgetHostView) host), mLauncher,
|
||||
WidgetSizes.updateWidgetSizeRanges(((LauncherAppWidgetHostView) host), mContext,
|
||||
info.spanX, info.spanY);
|
||||
host.requestLayout();
|
||||
mLauncher.getModelWriter().updateItemInDatabase(info);
|
||||
announceConfirmation(mLauncher.getString(R.string.widget_resized, info.spanX, info.spanY));
|
||||
mContext.getModelWriter().updateItemInDatabase(info);
|
||||
announceConfirmation(mContext.getString(R.string.widget_resized, info.spanX, info.spanY));
|
||||
return true;
|
||||
}
|
||||
|
||||
@Thunk void announceConfirmation(int resId) {
|
||||
announceConfirmation(mLauncher.getResources().getString(resId));
|
||||
announceConfirmation(mContext.getResources().getString(resId));
|
||||
}
|
||||
|
||||
@Thunk void announceConfirmation(String confirmation) {
|
||||
mLauncher.getDragLayer().announceForAccessibility(confirmation);
|
||||
|
||||
}
|
||||
|
||||
public boolean isInAccessibleDrag() {
|
||||
return mDragInfo != null;
|
||||
}
|
||||
|
||||
public DragInfo getDragInfo() {
|
||||
return mDragInfo;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param clickedTarget the actual view that was clicked
|
||||
* @param dropLocation relative to {@param clickedTarget}. If provided, its center is used
|
||||
* as the actual drop location otherwise the views center is used.
|
||||
*/
|
||||
public void handleAccessibleDrop(View clickedTarget, Rect dropLocation,
|
||||
String confirmation) {
|
||||
if (!isInAccessibleDrag()) return;
|
||||
|
||||
int[] loc = new int[2];
|
||||
if (dropLocation == null) {
|
||||
loc[0] = clickedTarget.getWidth() / 2;
|
||||
loc[1] = clickedTarget.getHeight() / 2;
|
||||
} else {
|
||||
loc[0] = dropLocation.centerX();
|
||||
loc[1] = dropLocation.centerY();
|
||||
}
|
||||
|
||||
mLauncher.getDragLayer().getDescendantCoordRelativeToSelf(clickedTarget, loc);
|
||||
mLauncher.getDragController().completeAccessibleDrag(loc);
|
||||
|
||||
if (!TextUtils.isEmpty(confirmation)) {
|
||||
announceConfirmation(confirmation);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean beginAccessibleDrag(View item, ItemInfo info, boolean fromKeyboard) {
|
||||
@Override
|
||||
protected boolean beginAccessibleDrag(View item, ItemInfo info, boolean fromKeyboard) {
|
||||
if (!itemSupportsAccessibleDrag(info)) {
|
||||
return false;
|
||||
}
|
||||
@@ -449,8 +343,8 @@ public class LauncherAccessibilityDelegate extends AccessibilityDelegate impleme
|
||||
}
|
||||
|
||||
Rect pos = new Rect();
|
||||
mLauncher.getDragLayer().getDescendantRectRelativeToSelf(item, pos);
|
||||
mLauncher.getDragController().addDragListener(this);
|
||||
mContext.getDragLayer().getDescendantRectRelativeToSelf(item, pos);
|
||||
mContext.getDragController().addDragListener(this);
|
||||
|
||||
DragOptions options = new DragOptions();
|
||||
options.isAccessibleDrag = true;
|
||||
@@ -458,31 +352,20 @@ public class LauncherAccessibilityDelegate extends AccessibilityDelegate impleme
|
||||
options.simulatedDndStartPoint = new Point(pos.centerX(), pos.centerY());
|
||||
|
||||
if (fromKeyboard) {
|
||||
KeyboardDragAndDropView popup = (KeyboardDragAndDropView) mLauncher.getLayoutInflater()
|
||||
.inflate(R.layout.keyboard_drag_and_drop, mLauncher.getDragLayer(), false);
|
||||
KeyboardDragAndDropView popup = (KeyboardDragAndDropView) mContext.getLayoutInflater()
|
||||
.inflate(R.layout.keyboard_drag_and_drop, mContext.getDragLayer(), false);
|
||||
popup.showForIcon(item, info, options);
|
||||
} else {
|
||||
ItemLongClickListener.beginDrag(item, mLauncher, info, options);
|
||||
ItemLongClickListener.beginDrag(item, mContext, info, options);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDragStart(DragObject dragObject, DragOptions options) {
|
||||
// No-op
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDragEnd() {
|
||||
mLauncher.getDragController().removeDragListener(this);
|
||||
mDragInfo = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find empty space on the workspace and returns the screenId.
|
||||
*/
|
||||
protected int findSpaceOnWorkspace(ItemInfo info, int[] outCoordinates) {
|
||||
Workspace workspace = mLauncher.getWorkspace();
|
||||
Workspace workspace = mContext.getWorkspace();
|
||||
IntArray workspaceScreens = workspace.getScreenOrder();
|
||||
int screenId;
|
||||
|
||||
@@ -520,29 +403,4 @@ public class LauncherAccessibilityDelegate extends AccessibilityDelegate impleme
|
||||
}
|
||||
return screenId;
|
||||
}
|
||||
|
||||
public class LauncherAction {
|
||||
public final int keyCode;
|
||||
public final AccessibilityAction accessibilityAction;
|
||||
|
||||
private final LauncherAccessibilityDelegate mDelegate;
|
||||
|
||||
public LauncherAction(int id, int labelRes, int keyCode) {
|
||||
this.keyCode = keyCode;
|
||||
accessibilityAction = new AccessibilityAction(id, mLauncher.getString(labelRes));
|
||||
mDelegate = LauncherAccessibilityDelegate.this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Invokes the action for the provided host
|
||||
*/
|
||||
public boolean invokeFromKeyboard(View host) {
|
||||
if (host != null && host.getTag() instanceof ItemInfo) {
|
||||
return mDelegate.performAction(
|
||||
host, (ItemInfo) host.getTag(), accessibilityAction.getId(), true);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -71,12 +71,12 @@ public class ShortcutMenuAccessibilityDelegate extends LauncherAccessibilityDele
|
||||
if (screenId == -1) {
|
||||
return false;
|
||||
}
|
||||
mLauncher.getStateManager().goToState(NORMAL, true, forSuccessCallback(() -> {
|
||||
mLauncher.getModelWriter().addItemToDatabase(info,
|
||||
mContext.getStateManager().goToState(NORMAL, true, forSuccessCallback(() -> {
|
||||
mContext.getModelWriter().addItemToDatabase(info,
|
||||
LauncherSettings.Favorites.CONTAINER_DESKTOP,
|
||||
screenId, coordinates[0], coordinates[1]);
|
||||
mLauncher.bindItems(Collections.singletonList(info), true);
|
||||
AbstractFloatingView.closeAllOpenViews(mLauncher);
|
||||
mContext.bindItems(Collections.singletonList(info), true);
|
||||
AbstractFloatingView.closeAllOpenViews(mContext);
|
||||
announceConfirmation(R.string.item_added_to_workspace);
|
||||
}));
|
||||
return true;
|
||||
|
||||
@@ -22,7 +22,7 @@ import android.view.View;
|
||||
|
||||
import com.android.launcher3.CellLayout;
|
||||
import com.android.launcher3.R;
|
||||
import com.android.launcher3.accessibility.LauncherAccessibilityDelegate.DragType;
|
||||
import com.android.launcher3.accessibility.BaseAccessibilityDelegate.DragType;
|
||||
import com.android.launcher3.model.data.AppInfo;
|
||||
import com.android.launcher3.model.data.FolderInfo;
|
||||
import com.android.launcher3.model.data.ItemInfo;
|
||||
|
||||
@@ -18,6 +18,7 @@ package com.android.launcher3.popup;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import com.android.launcher3.BubbleTextView;
|
||||
import com.android.launcher3.Launcher;
|
||||
import com.android.launcher3.R;
|
||||
import com.android.launcher3.model.data.ItemInfo;
|
||||
@@ -86,4 +87,9 @@ public class LauncherPopupLiveUpdateHandler extends PopupLiveUpdateHandler<Launc
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void showPopupContainerForIcon(BubbleTextView originalIcon) {
|
||||
PopupContainerWithArrow.showForIcon(originalIcon);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -152,6 +152,10 @@ public class PopupContainerWithArrow<T extends Context & ActivityContext>
|
||||
};
|
||||
}
|
||||
|
||||
public void setPopupItemDragHandler(PopupItemDragHandler popupItemDragHandler) {
|
||||
mPopupItemDragHandler = popupItemDragHandler;
|
||||
}
|
||||
|
||||
public PopupItemDragHandler getItemDragHandler() {
|
||||
return mPopupItemDragHandler;
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@ import static android.view.View.GONE;
|
||||
import android.content.Context;
|
||||
import android.view.View;
|
||||
|
||||
import com.android.launcher3.BubbleTextView;
|
||||
import com.android.launcher3.dot.DotInfo;
|
||||
import com.android.launcher3.model.data.ItemInfo;
|
||||
import com.android.launcher3.notification.NotificationContainer;
|
||||
@@ -36,7 +37,7 @@ import java.util.function.Predicate;
|
||||
*
|
||||
* @param <T> The activity on which the popup shows
|
||||
*/
|
||||
public class PopupLiveUpdateHandler<T extends Context & ActivityContext> implements
|
||||
public abstract class PopupLiveUpdateHandler<T extends Context & ActivityContext> implements
|
||||
PopupDataProvider.PopupDataChangeListener, View.OnAttachStateChangeListener {
|
||||
|
||||
protected final T mContext;
|
||||
@@ -103,6 +104,8 @@ public class PopupLiveUpdateHandler<T extends Context & ActivityContext> impleme
|
||||
@Override
|
||||
public void onSystemShortcutsUpdated() {
|
||||
mPopupContainerWithArrow.close(true);
|
||||
PopupContainerWithArrow.showForIcon(mPopupContainerWithArrow.getOriginalIcon());
|
||||
showPopupContainerForIcon(mPopupContainerWithArrow.getOriginalIcon());
|
||||
}
|
||||
|
||||
protected abstract void showPopupContainerForIcon(BubbleTextView originalIcon);
|
||||
}
|
||||
|
||||
@@ -23,12 +23,12 @@ import android.graphics.Rect;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.view.View;
|
||||
|
||||
import com.android.launcher3.Launcher;
|
||||
import com.android.launcher3.Utilities;
|
||||
import com.android.launcher3.config.FeatureFlags;
|
||||
import com.android.launcher3.graphics.DragPreviewProvider;
|
||||
import com.android.launcher3.icons.BitmapRenderer;
|
||||
import com.android.launcher3.icons.FastBitmapDrawable;
|
||||
import com.android.launcher3.views.ActivityContext;
|
||||
|
||||
/**
|
||||
* Extension of {@link DragPreviewProvider} which generates bitmaps scaled to the default icon size.
|
||||
@@ -45,7 +45,8 @@ public class ShortcutDragPreviewProvider extends DragPreviewProvider {
|
||||
@Override
|
||||
public Drawable createDrawable() {
|
||||
if (FeatureFlags.ENABLE_DEEP_SHORTCUT_ICON_CACHE.get()) {
|
||||
int size = Launcher.getLauncher(mView.getContext()).getDeviceProfile().iconSizePx;
|
||||
int size = ActivityContext.lookupContext(mView.getContext())
|
||||
.getDeviceProfile().iconSizePx;
|
||||
return new FastBitmapDrawable(
|
||||
BitmapRenderer.createHardwareBitmap(
|
||||
size + blurSizeOutline,
|
||||
@@ -59,7 +60,7 @@ public class ShortcutDragPreviewProvider extends DragPreviewProvider {
|
||||
private Bitmap createDragBitmapLegacy() {
|
||||
Drawable d = mView.getBackground();
|
||||
Rect bounds = getDrawableBounds(d);
|
||||
int size = Launcher.getLauncher(mView.getContext()).getDeviceProfile().iconSizePx;
|
||||
int size = ActivityContext.lookupContext(mView.getContext()).getDeviceProfile().iconSizePx;
|
||||
final Bitmap b = Bitmap.createBitmap(
|
||||
size + blurSizeOutline,
|
||||
size + blurSizeOutline,
|
||||
@@ -84,9 +85,9 @@ public class ShortcutDragPreviewProvider extends DragPreviewProvider {
|
||||
|
||||
@Override
|
||||
public float getScaleAndPosition(Drawable preview, int[] outPos) {
|
||||
Launcher launcher = Launcher.getLauncher(mView.getContext());
|
||||
ActivityContext context = ActivityContext.lookupContext(mView.getContext());
|
||||
int iconSize = getDrawableBounds(mView.getBackground()).width();
|
||||
float scale = launcher.getDragLayer().getLocationInDragLayer(mView, outPos);
|
||||
float scale = context.getDragLayer().getLocationInDragLayer(mView, outPos);
|
||||
|
||||
int iconLeft = mView.getPaddingStart();
|
||||
if (Utilities.isRtl(mView.getResources())) {
|
||||
@@ -98,7 +99,7 @@ public class ShortcutDragPreviewProvider extends DragPreviewProvider {
|
||||
+ mPositionShift.x);
|
||||
outPos[1] += Math.round((scale * mView.getHeight() - preview.getIntrinsicHeight()) / 2
|
||||
+ mPositionShift.y);
|
||||
float size = launcher.getDeviceProfile().iconSizePx;
|
||||
float size = context.getDeviceProfile().iconSizePx;
|
||||
return scale * iconSize / size;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,8 +28,9 @@ import android.view.Gravity;
|
||||
import android.view.MotionEvent;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.android.launcher3.AbstractFloatingView;
|
||||
import com.android.launcher3.BaseDraggingActivity;
|
||||
import com.android.launcher3.R;
|
||||
import com.android.launcher3.anim.Interpolators;
|
||||
import com.android.launcher3.compat.AccessibilityManagerCompat;
|
||||
@@ -44,7 +45,7 @@ public class Snackbar extends AbstractFloatingView {
|
||||
private static final long HIDE_DURATION_MS = 180;
|
||||
private static final int TIMEOUT_DURATION_MS = 4000;
|
||||
|
||||
private final BaseDraggingActivity mActivity;
|
||||
private final ActivityContext mActivity;
|
||||
private Runnable mOnDismissed;
|
||||
|
||||
public Snackbar(Context context, AttributeSet attrs) {
|
||||
@@ -53,12 +54,19 @@ public class Snackbar extends AbstractFloatingView {
|
||||
|
||||
public Snackbar(Context context, AttributeSet attrs, int defStyleAttr) {
|
||||
super(context, attrs, defStyleAttr);
|
||||
mActivity = BaseDraggingActivity.fromContext(context);
|
||||
mActivity = ActivityContext.lookupContext(context);
|
||||
inflate(context, R.layout.snackbar, this);
|
||||
}
|
||||
|
||||
public static void show(BaseDraggingActivity activity, int labelStringResId,
|
||||
int actionStringResId, Runnable onDismissed, Runnable onActionClicked) {
|
||||
/** Show a snackbar with just a label. */
|
||||
public static <T extends Context & ActivityContext> void show(T activity, int labelStringRedId,
|
||||
Runnable onDismissed) {
|
||||
show(activity, labelStringRedId, NO_ID, onDismissed, null);
|
||||
}
|
||||
|
||||
/** Show a snackbar with a label and action. */
|
||||
public static <T extends Context & ActivityContext> void show(T activity, int labelStringResId,
|
||||
int actionStringResId, Runnable onDismissed, @Nullable Runnable onActionClicked) {
|
||||
closeOpenViews(activity, true, TYPE_SNACKBAR);
|
||||
Snackbar snackbar = new Snackbar(activity, null);
|
||||
// Set some properties here since inflated xml only contains the children.
|
||||
@@ -87,13 +95,30 @@ public class Snackbar extends AbstractFloatingView {
|
||||
params.setMargins(0, 0, 0, marginBottom + insets.bottom);
|
||||
|
||||
TextView labelView = snackbar.findViewById(R.id.label);
|
||||
TextView actionView = snackbar.findViewById(R.id.action);
|
||||
String labelText = res.getString(labelStringResId);
|
||||
String actionText = res.getString(actionStringResId);
|
||||
int totalContentWidth = (int) (labelView.getPaint().measureText(labelText)
|
||||
+ actionView.getPaint().measureText(actionText))
|
||||
labelView.setText(labelText);
|
||||
|
||||
TextView actionView = snackbar.findViewById(R.id.action);
|
||||
float actionWidth;
|
||||
if (actionStringResId != NO_ID) {
|
||||
String actionText = res.getString(actionStringResId);
|
||||
actionWidth = actionView.getPaint().measureText(actionText)
|
||||
+ actionView.getPaddingRight() + actionView.getPaddingLeft();
|
||||
actionView.setText(actionText);
|
||||
actionView.setOnClickListener(v -> {
|
||||
if (onActionClicked != null) {
|
||||
onActionClicked.run();
|
||||
}
|
||||
snackbar.mOnDismissed = null;
|
||||
snackbar.close(true);
|
||||
});
|
||||
} else {
|
||||
actionWidth = 0;
|
||||
actionView.setVisibility(GONE);
|
||||
}
|
||||
|
||||
int totalContentWidth = (int) (labelView.getPaint().measureText(labelText) + actionWidth)
|
||||
+ labelView.getPaddingRight() + labelView.getPaddingLeft()
|
||||
+ actionView.getPaddingRight() + actionView.getPaddingLeft()
|
||||
+ padding * 2;
|
||||
if (totalContentWidth > params.width) {
|
||||
// The text doesn't fit in our standard width so update width to accommodate.
|
||||
@@ -113,17 +138,8 @@ public class Snackbar extends AbstractFloatingView {
|
||||
params.width = maxWidth;
|
||||
}
|
||||
}
|
||||
labelView.setText(labelText);
|
||||
actionView.setText(actionText);
|
||||
actionView.setOnClickListener(v -> {
|
||||
if (onActionClicked != null) {
|
||||
onActionClicked.run();
|
||||
}
|
||||
snackbar.mOnDismissed = null;
|
||||
snackbar.close(true);
|
||||
});
|
||||
snackbar.mOnDismissed = onDismissed;
|
||||
|
||||
snackbar.mOnDismissed = onDismissed;
|
||||
snackbar.setAlpha(0);
|
||||
snackbar.setScaleX(0.8f);
|
||||
snackbar.setScaleY(0.8f);
|
||||
|
||||
@@ -77,7 +77,8 @@ public final class Widgets extends LauncherInstrumentation.VisibleContainer {
|
||||
mLauncher.scroll(
|
||||
widgetsContainer,
|
||||
Direction.UP,
|
||||
new Rect(0, 0, mLauncher.getVisibleBounds(widgetsContainer).width(), 0),
|
||||
new Rect(0, 0, mLauncher.getRightGestureMarginInContainer(widgetsContainer) + 1,
|
||||
0),
|
||||
FLING_STEPS, false);
|
||||
try (LauncherInstrumentation.Closable c1 = mLauncher.addContextLayer("flung back")) {
|
||||
verifyActiveContainer();
|
||||
|
||||
Reference in New Issue
Block a user