Loading packages/SystemUI/src/com/android/systemui/controls/ui/ControlActionCoordinator.kt +32 −0 Original line number Diff line number Diff line Loading @@ -18,28 +18,46 @@ package com.android.systemui.controls.ui import android.app.Dialog import android.content.Intent import android.os.Vibrator import android.os.VibrationEffect import android.service.controls.Control import android.service.controls.actions.BooleanAction import android.service.controls.actions.CommandAction import android.view.HapticFeedbackConstants import com.android.systemui.controls.controller.ControlsController import com.android.systemui.util.concurrency.DelayableExecutor object ControlActionCoordinator { const val MIN_LEVEL = 0 const val MAX_LEVEL = 10000 private var dialog: Dialog? = null private var vibrator: Vibrator? = null lateinit var bgExecutor: DelayableExecutor fun closeDialog() { dialog?.dismiss() dialog = null } /** * Create custom vibrations, all intended to create very subtle feedback while interacting * with the controls. */ fun initialize(vibrator: Vibrator, bgExecutor: DelayableExecutor) { this.vibrator = vibrator this.bgExecutor = bgExecutor } fun toggle(cvh: ControlViewHolder, templateId: String, isChecked: Boolean) { val effect = if (isChecked) Vibrations.toggleOnEffect else Vibrations.toggleOffEffect vibrate(effect) cvh.action(BooleanAction(templateId, !isChecked)) } fun touch(cvh: ControlViewHolder, templateId: String, control: Control) { vibrate(Vibrations.toggleOnEffect) if (cvh.usePanel()) { showDialog(cvh, control.getAppIntent().getIntent()) } else { Loading @@ -47,6 +65,14 @@ object ControlActionCoordinator { } } fun drag(isEdge: Boolean) { if (isEdge) { vibrate(Vibrations.rangeEdgeEffect) } else { vibrate(Vibrations.rangeMiddleEffect) } } /** * All long presses will be shown in a 3/4 height bottomsheet panel, in order for the user to * retain context with their favorited controls in the power menu. Loading @@ -59,6 +85,12 @@ object ControlActionCoordinator { } } private fun vibrate(effect: VibrationEffect) { vibrator?.let { bgExecutor.execute { it.vibrate(effect) } } } private fun showDialog(cvh: ControlViewHolder, intent: Intent) { dialog = DetailDialog(cvh, intent).also { it.setOnDismissListener { _ -> dialog = null } Loading packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt +6 −0 Original line number Diff line number Diff line Loading @@ -30,6 +30,7 @@ import android.content.res.Configuration import android.graphics.drawable.Drawable import android.graphics.drawable.LayerDrawable import android.os.Process import android.os.Vibrator import android.service.controls.Control import android.service.controls.actions.ControlAction import android.util.Log Loading Loading @@ -109,6 +110,11 @@ class ControlsUiControllerImpl @Inject constructor ( private lateinit var listingCallback: ControlsListingController.ControlsListingCallback init { val vibratorService = context.getSystemService(Context.VIBRATOR_SERVICE) as Vibrator ControlActionCoordinator.initialize(vibratorService, bgExecutor) } private fun createCallback( onResult: (List<SelectionItem>) -> Unit ): ControlsListingController.ControlsListingCallback { Loading packages/SystemUI/src/com/android/systemui/controls/ui/ToggleRangeBehavior.kt +4 −0 Original line number Diff line number Diff line Loading @@ -181,9 +181,13 @@ class ToggleRangeBehavior : Behavior { fun updateRange(level: Int, checked: Boolean, isDragging: Boolean) { val newLevel = if (checked) Math.max(MIN_LEVEL, Math.min(MAX_LEVEL, level)) else MIN_LEVEL if (newLevel == clipLayer.level) return rangeAnimator?.cancel() if (isDragging) { clipLayer.level = newLevel val isEdge = newLevel == MIN_LEVEL || newLevel == MAX_LEVEL ControlActionCoordinator.drag(isEdge) } else { rangeAnimator = ValueAnimator.ofInt(cvh.clipLayer.level, newLevel).apply { addUpdateListener { Loading packages/SystemUI/src/com/android/systemui/controls/ui/Vibrations.kt 0 → 100644 +62 −0 Original line number Diff line number Diff line /* * Copyright (C) 2020 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.systemui.controls.ui import android.os.VibrationEffect import android.os.VibrationEffect.Composition.PRIMITIVE_TICK object Vibrations { private const val TOGGLE_TICK_COUNT = 12 val toggleOnEffect = initToggleOnEffect() val toggleOffEffect = initToggleOffEffect() val rangeEdgeEffect = initRangeEdgeEffect() val rangeMiddleEffect = initRangeMiddleEffect() private fun initToggleOnEffect(): VibrationEffect { val composition = VibrationEffect.startComposition() var i = 0 while (i++ < TOGGLE_TICK_COUNT) { composition.addPrimitive(PRIMITIVE_TICK, 0.05f, 0) } composition.addPrimitive(PRIMITIVE_TICK, 0.5f, 100) return composition.compose() } private fun initToggleOffEffect(): VibrationEffect { val composition = VibrationEffect.startComposition() composition.addPrimitive(PRIMITIVE_TICK, 0.5f, 0) composition.addPrimitive(PRIMITIVE_TICK, 0.05f, 100) var i = 0 while (i++ < TOGGLE_TICK_COUNT) { composition?.addPrimitive(PRIMITIVE_TICK, 0.05f, 0) } return composition.compose() } private fun initRangeEdgeEffect(): VibrationEffect { val composition = VibrationEffect.startComposition() composition.addPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK, 0.5f) return composition.compose() } private fun initRangeMiddleEffect(): VibrationEffect { val composition = VibrationEffect.startComposition() composition.addPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK, 0.1f) return composition.compose() } } Loading
packages/SystemUI/src/com/android/systemui/controls/ui/ControlActionCoordinator.kt +32 −0 Original line number Diff line number Diff line Loading @@ -18,28 +18,46 @@ package com.android.systemui.controls.ui import android.app.Dialog import android.content.Intent import android.os.Vibrator import android.os.VibrationEffect import android.service.controls.Control import android.service.controls.actions.BooleanAction import android.service.controls.actions.CommandAction import android.view.HapticFeedbackConstants import com.android.systemui.controls.controller.ControlsController import com.android.systemui.util.concurrency.DelayableExecutor object ControlActionCoordinator { const val MIN_LEVEL = 0 const val MAX_LEVEL = 10000 private var dialog: Dialog? = null private var vibrator: Vibrator? = null lateinit var bgExecutor: DelayableExecutor fun closeDialog() { dialog?.dismiss() dialog = null } /** * Create custom vibrations, all intended to create very subtle feedback while interacting * with the controls. */ fun initialize(vibrator: Vibrator, bgExecutor: DelayableExecutor) { this.vibrator = vibrator this.bgExecutor = bgExecutor } fun toggle(cvh: ControlViewHolder, templateId: String, isChecked: Boolean) { val effect = if (isChecked) Vibrations.toggleOnEffect else Vibrations.toggleOffEffect vibrate(effect) cvh.action(BooleanAction(templateId, !isChecked)) } fun touch(cvh: ControlViewHolder, templateId: String, control: Control) { vibrate(Vibrations.toggleOnEffect) if (cvh.usePanel()) { showDialog(cvh, control.getAppIntent().getIntent()) } else { Loading @@ -47,6 +65,14 @@ object ControlActionCoordinator { } } fun drag(isEdge: Boolean) { if (isEdge) { vibrate(Vibrations.rangeEdgeEffect) } else { vibrate(Vibrations.rangeMiddleEffect) } } /** * All long presses will be shown in a 3/4 height bottomsheet panel, in order for the user to * retain context with their favorited controls in the power menu. Loading @@ -59,6 +85,12 @@ object ControlActionCoordinator { } } private fun vibrate(effect: VibrationEffect) { vibrator?.let { bgExecutor.execute { it.vibrate(effect) } } } private fun showDialog(cvh: ControlViewHolder, intent: Intent) { dialog = DetailDialog(cvh, intent).also { it.setOnDismissListener { _ -> dialog = null } Loading
packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt +6 −0 Original line number Diff line number Diff line Loading @@ -30,6 +30,7 @@ import android.content.res.Configuration import android.graphics.drawable.Drawable import android.graphics.drawable.LayerDrawable import android.os.Process import android.os.Vibrator import android.service.controls.Control import android.service.controls.actions.ControlAction import android.util.Log Loading Loading @@ -109,6 +110,11 @@ class ControlsUiControllerImpl @Inject constructor ( private lateinit var listingCallback: ControlsListingController.ControlsListingCallback init { val vibratorService = context.getSystemService(Context.VIBRATOR_SERVICE) as Vibrator ControlActionCoordinator.initialize(vibratorService, bgExecutor) } private fun createCallback( onResult: (List<SelectionItem>) -> Unit ): ControlsListingController.ControlsListingCallback { Loading
packages/SystemUI/src/com/android/systemui/controls/ui/ToggleRangeBehavior.kt +4 −0 Original line number Diff line number Diff line Loading @@ -181,9 +181,13 @@ class ToggleRangeBehavior : Behavior { fun updateRange(level: Int, checked: Boolean, isDragging: Boolean) { val newLevel = if (checked) Math.max(MIN_LEVEL, Math.min(MAX_LEVEL, level)) else MIN_LEVEL if (newLevel == clipLayer.level) return rangeAnimator?.cancel() if (isDragging) { clipLayer.level = newLevel val isEdge = newLevel == MIN_LEVEL || newLevel == MAX_LEVEL ControlActionCoordinator.drag(isEdge) } else { rangeAnimator = ValueAnimator.ofInt(cvh.clipLayer.level, newLevel).apply { addUpdateListener { Loading
packages/SystemUI/src/com/android/systemui/controls/ui/Vibrations.kt 0 → 100644 +62 −0 Original line number Diff line number Diff line /* * Copyright (C) 2020 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.systemui.controls.ui import android.os.VibrationEffect import android.os.VibrationEffect.Composition.PRIMITIVE_TICK object Vibrations { private const val TOGGLE_TICK_COUNT = 12 val toggleOnEffect = initToggleOnEffect() val toggleOffEffect = initToggleOffEffect() val rangeEdgeEffect = initRangeEdgeEffect() val rangeMiddleEffect = initRangeMiddleEffect() private fun initToggleOnEffect(): VibrationEffect { val composition = VibrationEffect.startComposition() var i = 0 while (i++ < TOGGLE_TICK_COUNT) { composition.addPrimitive(PRIMITIVE_TICK, 0.05f, 0) } composition.addPrimitive(PRIMITIVE_TICK, 0.5f, 100) return composition.compose() } private fun initToggleOffEffect(): VibrationEffect { val composition = VibrationEffect.startComposition() composition.addPrimitive(PRIMITIVE_TICK, 0.5f, 0) composition.addPrimitive(PRIMITIVE_TICK, 0.05f, 100) var i = 0 while (i++ < TOGGLE_TICK_COUNT) { composition?.addPrimitive(PRIMITIVE_TICK, 0.05f, 0) } return composition.compose() } private fun initRangeEdgeEffect(): VibrationEffect { val composition = VibrationEffect.startComposition() composition.addPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK, 0.5f) return composition.compose() } private fun initRangeMiddleEffect(): VibrationEffect { val composition = VibrationEffect.startComposition() composition.addPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK, 0.1f) return composition.compose() } }