Introducing the MSDLPlayer in Launcher via a wrapper singleton.
The player is the main API to the MSDL library (also used in SysUI) that
implements the Multi-sensory Design Language in the system UI. This CL
also includes an example of the API usage, when the user swipes up to
reveal AllApps, or taps on QSB.
Test: manual. Verified that the MSDL haptics play when swiping to reveal
AllApps and tapping on QSB.
Flag: com.android.launcher3.msdl_feedback
Bug: 371322466
Bug: 371250001
Change-Id: Ie13fd5494efc9fc80cdb94a7bdd6e20b2e4633a8
diff --git a/Android.bp b/Android.bp
index 223e2c2..73d0fce 100644
--- a/Android.bp
+++ b/Android.bp
@@ -387,6 +387,7 @@
"//frameworks/libs/systemui:view_capture",
"//frameworks/libs/systemui:animationlib",
"//frameworks/libs/systemui:contextualeducationlib",
+ "//frameworks/libs/systemui:msdl",
"SystemUI-statsd",
"launcher-testing-shared",
"androidx.lifecycle_lifecycle-common-java8",
diff --git a/src/com/android/launcher3/allapps/AllAppsTransitionController.java b/src/com/android/launcher3/allapps/AllAppsTransitionController.java
index c6852e0..1766e6e 100644
--- a/src/com/android/launcher3/allapps/AllAppsTransitionController.java
+++ b/src/com/android/launcher3/allapps/AllAppsTransitionController.java
@@ -48,6 +48,7 @@
import com.android.app.animation.Interpolators;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.DeviceProfile.OnDeviceProfileChangeListener;
+import com.android.launcher3.Flags;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherState;
import com.android.launcher3.R;
@@ -57,14 +58,16 @@
import com.android.launcher3.statemanager.StateManager.StateHandler;
import com.android.launcher3.states.StateAnimationConfig;
import com.android.launcher3.touch.AllAppsSwipeController;
+import com.android.launcher3.util.MSDLPlayerWrapper;
import com.android.launcher3.util.MultiPropertyFactory;
import com.android.launcher3.util.MultiPropertyFactory.MultiProperty;
import com.android.launcher3.util.MultiValueAlpha;
import com.android.launcher3.util.ScrollableLayoutManager;
import com.android.launcher3.util.Themes;
-import com.android.launcher3.util.VibratorWrapper;
import com.android.launcher3.views.ScrimView;
+import com.google.android.msdl.data.model.MSDLToken;
+
/**
* Handles AllApps view transition.
* 1) Slides all apps view using direct manipulation
@@ -186,7 +189,7 @@
private boolean mIsTablet;
private boolean mHasScaleEffect;
- private final VibratorWrapper mVibratorWrapper;
+ private final MSDLPlayerWrapper mMSDLPlayerWrapper;
public AllAppsTransitionController(Launcher l) {
mLauncher = l;
@@ -200,7 +203,7 @@
setShiftRange(dp.allAppsShiftRange);
mAllAppScale.value = 1;
mLauncher.addOnDeviceProfileChangeListener(this);
- mVibratorWrapper = VibratorWrapper.INSTANCE.get(mLauncher.getApplicationContext());
+ mMSDLPlayerWrapper = MSDLPlayerWrapper.INSTANCE.get(mLauncher.getApplicationContext());
}
public float getShiftRange() {
@@ -373,8 +376,16 @@
setAlphas(toState, config, builder);
// This controls both haptics for tapping on QSB and going to all apps.
if (ALL_APPS.equals(toState) && mLauncher.isInState(NORMAL)) {
- mLauncher.getAppsView().performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY,
- HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING);
+ if (Flags.msdlFeedback()) {
+ if (config.isUserControlled()) {
+ mMSDLPlayerWrapper.playToken(MSDLToken.SWIPE_THRESHOLD_INDICATOR);
+ } else {
+ mMSDLPlayerWrapper.playToken(MSDLToken.TAP_HIGH_EMPHASIS);
+ }
+ } else {
+ mLauncher.getAppsView().performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY,
+ HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING);
+ }
}
}
diff --git a/src/com/android/launcher3/dagger/LauncherBaseAppComponent.java b/src/com/android/launcher3/dagger/LauncherBaseAppComponent.java
index 0e20f75..fb486f7 100644
--- a/src/com/android/launcher3/dagger/LauncherBaseAppComponent.java
+++ b/src/com/android/launcher3/dagger/LauncherBaseAppComponent.java
@@ -24,6 +24,7 @@
import com.android.launcher3.pm.InstallSessionHelper;
import com.android.launcher3.util.ApiWrapper;
import com.android.launcher3.util.DaggerSingletonTracker;
+import com.android.launcher3.util.MSDLPlayerWrapper;
import com.android.launcher3.util.PackageManagerHelper;
import com.android.launcher3.util.PluginManagerWrapper;
import com.android.launcher3.util.ScreenOnTracker;
@@ -56,6 +57,7 @@
PackageManagerHelper getPackageManagerHelper();
PluginManagerWrapper getPluginManagerWrapper();
VibratorWrapper getVibratorWrapper();
+ MSDLPlayerWrapper getMSDLPlayerWrapper();
/** Builder for LauncherBaseAppComponent. */
interface Builder {
diff --git a/src/com/android/launcher3/util/MSDLPlayerWrapper.java b/src/com/android/launcher3/util/MSDLPlayerWrapper.java
new file mode 100644
index 0000000..1e53ac1
--- /dev/null
+++ b/src/com/android/launcher3/util/MSDLPlayerWrapper.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2024 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.util;
+
+import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
+
+import android.content.Context;
+import android.os.Vibrator;
+
+import com.android.launcher3.dagger.ApplicationContext;
+import com.android.launcher3.dagger.LauncherAppSingleton;
+import com.android.launcher3.dagger.LauncherBaseAppComponent;
+
+import com.google.android.msdl.data.model.MSDLToken;
+import com.google.android.msdl.domain.InteractionProperties;
+import com.google.android.msdl.domain.MSDLPlayer;
+
+import javax.inject.Inject;
+
+/**
+ * Wrapper around {@link com.google.android.msdl.domain.MSDLPlayer} to perform MSDL feedback.
+ */
+@LauncherAppSingleton
+public class MSDLPlayerWrapper {
+
+ public static final DaggerSingletonObject<MSDLPlayerWrapper> INSTANCE =
+ new DaggerSingletonObject<>(LauncherBaseAppComponent::getMSDLPlayerWrapper);
+
+ /** Internal player */
+ private final MSDLPlayer mMSDLPlayer;
+
+ @Inject
+ public MSDLPlayerWrapper(@ApplicationContext Context context) {
+ Vibrator vibrator = context.getSystemService(Vibrator.class);
+ mMSDLPlayer = MSDLPlayer.Companion.createPlayer(vibrator, UI_HELPER_EXECUTOR, null);
+ }
+
+ /** Perform MSDL feedback for a token with interaction properties */
+ public void playToken(MSDLToken token, InteractionProperties properties) {
+ mMSDLPlayer.playToken(token, properties);
+ }
+
+ /** Perform MSDL feedback for a token without properties */
+ public void playToken(MSDLToken token) {
+ mMSDLPlayer.playToken(token, null);
+ }
+}