Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 647e5702 authored by Jeremy Sim's avatar Jeremy Sim
Browse files

Add haptics to magnetic divider

Initializes MSDLPlayer in WMShellModule as a singleton to be used in Shell. (Can be cleaned up and replaced by a global import in GlobalModule -- currently being worked on by haptics team)

Adds logic to listen to segment changes in the MotionValue controller, and plays a small haptic click when attaching or detaching from a snap target.

Bug: 383631946
Flag: com.android.wm.shell.enable_magnetic_split_divider
Test: No changes with flag off. Plays haptics with flag on.
Change-Id: If0bdc46627afdc4f1794307333f10aba96210e3f
parent fb2a0e97
Loading
Loading
Loading
Loading
+22 −0
Original line number Diff line number Diff line
@@ -55,6 +55,7 @@ import androidx.annotation.Nullable;

import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.protolog.ProtoLog;
import com.android.mechanics.spec.BreakpointKey;
import com.android.mechanics.spec.InputDirection;
import com.android.mechanics.view.DistanceGestureContext;
import com.android.mechanics.view.ViewMotionValue;
@@ -64,6 +65,8 @@ import com.android.wm.shell.protolog.ShellProtoLogGroup;
import com.android.wm.shell.shared.animation.Interpolators;
import com.android.wm.shell.shared.desktopmode.DesktopState;

import com.google.android.msdl.data.model.MSDLToken;

/**
 * Divider for multi window splits.
 */
@@ -96,6 +99,7 @@ public class DividerView extends FrameLayout implements View.OnTouchListener {
    // Calculation classes for "magnetic snap" user-controlled movement
    private DistanceGestureContext mDistanceGestureContext;
    private ViewMotionValue mViewMotionValue;
    private BreakpointKey mCurrentHapticBreakpoint;

    /**
     * This is not the visible bounds you see on screen, but the actual behind-the-scenes window
@@ -377,6 +381,18 @@ public class DividerView extends FrameLayout implements View.OnTouchListener {
                                "dividerView::pos" /* label */);
                        mViewMotionValue.addUpdateCallback(viewMotionValue -> {
                            placeDivider((int) viewMotionValue.getOutput());
                            // Each MotionValue "segment" has two "breakpoints", one on each end.
                            // We can uniquely identify each segment by just one of its breakpoints,
                            // so we arbitrarily listen for changes to the "min-side" breakpoint
                            // to determine when the user has moved the onto a new segment (i.e.
                            // moved the divider from the "free-drag" segment to the "snapped"
                            // segment, or vice versa). We play a haptic when this happens.
                            if (!viewMotionValue.getSegmentKey().getMinBreakpoint()
                                    .equals(mCurrentHapticBreakpoint)) {
                                playHapticClick();
                            }
                            mCurrentHapticBreakpoint =
                                    viewMotionValue.getSegmentKey().getMinBreakpoint();
                        });
                    }
                }
@@ -420,6 +436,11 @@ public class DividerView extends FrameLayout implements View.OnTouchListener {
        return true;
    }

    /** Plays a short haptic to indicate attaching or detaching from a divider snap point. */
    private void playHapticClick() {
        mSplitLayout.getHapticPlayer().playToken(MSDLToken.SWIPE_THRESHOLD_INDICATOR, null);
    }

    /** Updates the position of the divider. */
    private void placeDivider(int position) {
        mSplitLayout.updateDividerBounds(position, true /* shouldUseParallaxEffect */);
@@ -446,6 +467,7 @@ public class DividerView extends FrameLayout implements View.OnTouchListener {
        }
        mDistanceGestureContext = null;
        mViewMotionValue = null;
        mCurrentHapticBreakpoint = null;
    }

    private void setTouching() {
+12 −1
Original line number Diff line number Diff line
@@ -89,6 +89,8 @@ import com.android.wm.shell.shared.split.SplitScreenConstants.SplitPosition;
import com.android.wm.shell.splitscreen.SplitStatusBarHider;
import com.android.wm.shell.splitscreen.StageTaskListener;

import com.google.android.msdl.domain.MSDLPlayer;

import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
@@ -149,6 +151,9 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange
    /** Singleton source of truth for the current state of split screen on this device. */
    private final SplitState mSplitState;

    /** A haptics controller that plays haptic effects. */
    private final MSDLPlayer mMSDLPlayer;

    private int mDividerWindowWidth;
    private int mDividerInsets;
    private int mDividerSize;
@@ -215,7 +220,7 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange
            DisplayController displayController, DisplayImeController displayImeController,
            ShellTaskOrganizer taskOrganizer, int parallaxType, SplitState splitState,
            @ShellMainThread Handler handler, SplitStatusBarHider statusBarHider,
            DesktopState desktopState) {
            DesktopState desktopState, MSDLPlayer msdlPlayer) {
        mHandler = handler;
        mStatusBarHider = statusBarHider;
        mContext = context.createConfigurationContext(configuration);
@@ -234,6 +239,7 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange
        mSurfaceEffectPolicy = new ResizingEffectPolicy(parallaxType, this);
        mSplitState = splitState;
        mDesktopState = desktopState;
        mMSDLPlayer = msdlPlayer;

        final Resources res = mContext.getResources();
        mDimNonImeSide = res.getBoolean(R.bool.config_dimNonImeAttachedSide);
@@ -388,6 +394,11 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange
        return mDividerPosition;
    }

    /** Returns the haptic player used in this class. */
    public MSDLPlayer getHapticPlayer() {
        return mMSDLPlayer;
    }

    /**
     * Finds the {@link SnapPosition} nearest to the current divider position.
     */
+5 −2
Original line number Diff line number Diff line
@@ -47,6 +47,8 @@ import com.android.wm.shell.sysui.ShellController;
import com.android.wm.shell.sysui.ShellInit;
import com.android.wm.shell.transition.Transitions;

import com.google.android.msdl.domain.MSDLPlayer;

import dagger.Module;
import dagger.Provides;

@@ -99,12 +101,13 @@ public class TvWMShellModule {
            SystemWindows systemWindows,
            RootDisplayAreaOrganizer rootDisplayAreaOrganizer,
            DesktopState desktopState,
            IActivityTaskManager activityTaskManager) {
            IActivityTaskManager activityTaskManager,
            MSDLPlayer msdlPlayer) {
        return new TvSplitScreenController(context, shellInit, shellCommandHandler, shellController,
                shellTaskOrganizer, syncQueue, rootTDAOrganizer, displayController,
                displayImeController, displayInsetsController, transitions, transactionPool,
                iconProvider, recentTasks, launchAdjacentController, multiInstanceHelper,
                splitState, mainExecutor, mainHandler, systemWindows, rootDisplayAreaOrganizer,
                desktopState, activityTaskManager);
                desktopState, activityTaskManager, msdlPlayer);
    }
}
+15 −0
Original line number Diff line number Diff line
@@ -22,6 +22,7 @@ import static com.android.wm.shell.compatui.CompatUIStatusManager.COMPAT_UI_EDUC
import static com.android.wm.shell.onehanded.OneHandedController.SUPPORT_ONE_HANDED_MODE;

import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityTaskManager;
import android.content.Context;
import android.content.pm.PackageManager;
@@ -29,6 +30,7 @@ import android.hardware.display.DisplayManager;
import android.os.Handler;
import android.os.SystemProperties;
import android.os.UserManager;
import android.os.Vibrator;
import android.provider.Settings;
import android.view.IWindowManager;
import android.view.accessibility.AccessibilityManager;
@@ -147,12 +149,15 @@ import com.android.wm.shell.windowdecor.WindowDecorViewModel;
import com.android.wm.shell.windowdecor.viewholder.AppHandleNotifier;
import com.android.wm.shell.windowdecor.viewholder.AppHandles;

import com.google.android.msdl.domain.MSDLPlayer;

import dagger.BindsOptionalOf;
import dagger.Lazy;
import dagger.Module;
import dagger.Provides;

import java.util.Optional;
import java.util.concurrent.Executors;

/**
 * Provides basic dependencies from {@link com.android.wm.shell}, these dependencies are only
@@ -244,6 +249,16 @@ public abstract class WMShellBaseModule {
                context, shellInit, postureController, displayController, mainExecutor);
    }

    @WMSingleton
    @Provides
    static MSDLPlayer provideMSDLPlayer(@Nullable Vibrator vibrator) {
        return MSDLPlayer.Companion.createPlayer(
                vibrator,
                Executors.newSingleThreadExecutor(),
                null /* useHapticFeedbackForToken */
        );
    }

    @WMSingleton
    @Provides
    static ShellTaskOrganizer provideShellTaskOrganizer(
+6 −2
Original line number Diff line number Diff line
@@ -192,6 +192,8 @@ import com.android.wm.shell.windowdecor.education.DesktopWindowingEducationToolt
import com.android.wm.shell.windowdecor.tiling.DesktopTilingDecorViewModel;
import com.android.wm.shell.windowdecor.viewholder.AppHandleNotifier;

import com.google.android.msdl.domain.MSDLPlayer;

import dagger.Binds;
import dagger.Lazy;
import dagger.Module;
@@ -613,7 +615,8 @@ public abstract class WMShellModule {
            @ShellMainThread Handler mainHandler,
            RootDisplayAreaOrganizer rootDisplayAreaOrganizer,
            DesktopState desktopState,
            IActivityTaskManager activityTaskManager) {
            IActivityTaskManager activityTaskManager,
            MSDLPlayer msdlPlayer) {
        return new SplitScreenController(
                context,
                shellInit,
@@ -640,7 +643,8 @@ public abstract class WMShellModule {
                mainHandler,
                rootDisplayAreaOrganizer,
                desktopState,
                activityTaskManager);
                activityTaskManager,
                msdlPlayer);
    }

    //
Loading