diff --git a/misc/src/main/java/com/example/snippets/ui/haptics/CustomVibrationCompositions.kt b/misc/src/main/java/com/example/snippets/ui/haptics/CustomVibrationCompositions.kt new file mode 100644 index 000000000..e6b39f11a --- /dev/null +++ b/misc/src/main/java/com/example/snippets/ui/haptics/CustomVibrationCompositions.kt @@ -0,0 +1,82 @@ +/* + * Copyright 2025 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 + * + * https://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.example.snippets.ui.haptics + +import android.Manifest +import android.app.Activity +import android.os.Build +import android.os.VibrationEffect +import android.os.Vibrator +import androidx.annotation.RequiresApi +import androidx.annotation.RequiresPermission + +@RequiresApi(api = Build.VERSION_CODES.S) +class CustomVibrationCompositions : Activity() { + var vibrator: Vibrator = getApplicationContext().getSystemService(Vibrator::class.java) + + @RequiresPermission(Manifest.permission.VIBRATE) + // [START android_ui_haptics_composed_vibration_effect] + fun createComposedVibrationEffect() { + vibrator.vibrate( + VibrationEffect.startComposition() + .addPrimitive(VibrationEffect.Composition.PRIMITIVE_SLOW_RISE) + .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK) + .compose() + ) + } + // [END android_ui_haptics_composed_vibration_effect] + + @RequiresPermission(Manifest.permission.VIBRATE) + // [START android_ui_haptics_gap_between_primitives] + fun gapBetweenPrimitives() { + val delayMs: Int = 100 + vibrator.vibrate( + VibrationEffect.startComposition() + .addPrimitive(VibrationEffect.Composition.PRIMITIVE_SPIN, 0.8f) + .addPrimitive(VibrationEffect.Composition.PRIMITIVE_SPIN, 0.6f) + .addPrimitive( + VibrationEffect.Composition.PRIMITIVE_THUD, 1.0f, delayMs + ) + .compose() + ) + } + // [END android_ui_haptics_gap_between_primitives] + + @RequiresPermission(Manifest.permission.VIBRATE) + private fun checkPrimitivesSupport() { + // [START android_ui_haptics_check_single_primitive_support] + val primitive: Int = VibrationEffect.Composition.PRIMITIVE_LOW_TICK + + if (vibrator.areAllPrimitivesSupported(primitive)) { + vibrator.vibrate( + VibrationEffect.startComposition() + .addPrimitive(primitive).compose() + ) + } else { + // Play a predefined effect or custom pattern as a fallback. + } + // [END android_ui_haptics_check_single_primitive_support] + + // [START android_ui_haptics_check_multiple_primitives_support] + val supported: BooleanArray = vibrator.arePrimitivesSupported( + VibrationEffect.Composition.PRIMITIVE_LOW_TICK, + VibrationEffect.Composition.PRIMITIVE_TICK, + VibrationEffect.Composition.PRIMITIVE_CLICK + ) + // [END android_ui_haptics_check_multiple_primitives_support] + } +} diff --git a/misc/src/main/java/com/example/snippets/ui/haptics/CustomVibrationCompositionsJava.java b/misc/src/main/java/com/example/snippets/ui/haptics/CustomVibrationCompositionsJava.java new file mode 100644 index 000000000..f4b21d7b0 --- /dev/null +++ b/misc/src/main/java/com/example/snippets/ui/haptics/CustomVibrationCompositionsJava.java @@ -0,0 +1,61 @@ +package com.example.snippets.ui.haptics; + +import android.Manifest; +import android.app.Activity; +import android.os.Build; +import android.os.VibrationEffect; +import android.os.Vibrator; + +import androidx.annotation.RequiresApi; +import androidx.annotation.RequiresPermission; + +@RequiresApi(Build.VERSION_CODES.S) +public class CustomVibrationCompositionsJava extends Activity { + Vibrator vibrator = getApplicationContext().getSystemService(Vibrator.class); + + @RequiresPermission(Manifest.permission.VIBRATE) + // [START android_ui_haptics_composed_vibration_effect] + private void createComposedVibrationEffect() { + vibrator.vibrate( + VibrationEffect.startComposition() + .addPrimitive(VibrationEffect.Composition.PRIMITIVE_SLOW_RISE) + .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK) + .compose()); + } + // [END android_ui_haptics_composed_vibration_effect] + + @RequiresPermission(Manifest.permission.VIBRATE) + // [START android_ui_haptics_gap_between_primitives] + private void gapBetweenPrimitives() { + int delayMs = 100; + vibrator.vibrate( + VibrationEffect.startComposition() + .addPrimitive(VibrationEffect.Composition.PRIMITIVE_SPIN, 0.8f) + .addPrimitive(VibrationEffect.Composition.PRIMITIVE_SPIN, 0.6f) + .addPrimitive( + VibrationEffect.Composition.PRIMITIVE_THUD, 1.0f, delayMs) + .compose()); + } + // [END android_ui_haptics_gap_between_primitives] + + @RequiresPermission(Manifest.permission.VIBRATE) + private void checkPrimitivesSupport() { + // [START android_ui_haptics_check_single_primitive_support] + int primitive = VibrationEffect.Composition.PRIMITIVE_LOW_TICK; + + if (vibrator.areAllPrimitivesSupported(primitive)) { + vibrator.vibrate(VibrationEffect.startComposition() + .addPrimitive(primitive).compose()); + } else { + // Play a predefined effect or custom pattern as a fallback. + } + // [END android_ui_haptics_check_single_primitive_support] + + // [START android_ui_haptics_check_multiple_primitives_support] + boolean[] supported = vibrator.arePrimitivesSupported( + VibrationEffect.Composition.PRIMITIVE_LOW_TICK, + VibrationEffect.Composition.PRIMITIVE_TICK, + VibrationEffect.Composition.PRIMITIVE_CLICK); + // [END android_ui_haptics_check_multiple_primitives_support] + } +} diff --git a/misc/src/main/java/com/example/snippets/ui/haptics/CustomVibrationPatterns.kt b/misc/src/main/java/com/example/snippets/ui/haptics/CustomVibrationPatterns.kt new file mode 100644 index 000000000..a5909e5ef --- /dev/null +++ b/misc/src/main/java/com/example/snippets/ui/haptics/CustomVibrationPatterns.kt @@ -0,0 +1,99 @@ +/* + * Copyright 2025 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 + * + * https://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.example.snippets.ui.haptics + +import android.Manifest +import android.app.Activity +import android.os.Build +import android.os.VibrationEffect +import android.os.Vibrator +import androidx.annotation.RequiresApi +import androidx.annotation.RequiresPermission + +class CustomVibrationPatterns : Activity() { + @RequiresApi(Build.VERSION_CODES.M) + val vibrator = applicationContext.getSystemService(Vibrator::class.java) as Vibrator + + // [START android_ui_haptics_ramp_up] + @RequiresPermission(Manifest.permission.VIBRATE) + @RequiresApi(Build.VERSION_CODES.O) + fun rampUpPattern(vibrator: Vibrator) { + val timings: LongArray = longArrayOf( + 50, 50, 50, 50, 50, 100, 350, 25, 25, 25, 25, 200 + ) + val amplitudes: IntArray = intArrayOf( + 33, 51, 75, 113, 170, 255, 0, 38, 62, 100, 160, 255 + ) + val repeatIndex = -1 // Don't repeat. + + vibrator.vibrate( + VibrationEffect.createWaveform( + timings, amplitudes, repeatIndex + ) + ) + } + // [END android_ui_haptics_ramp_up] + + @RequiresPermission(Manifest.permission.VIBRATE) + @RequiresApi(Build.VERSION_CODES.O) + // [START android_ui_haptics_repeat] + fun startRepeatVibration() { + val timings: LongArray = longArrayOf(50, 50, 100, 50, 50) + val amplitudes: IntArray = intArrayOf(64, 128, 255, 128, 64) + val repeat = 1 // Repeat from the second entry, index = 1. + val repeatingEffect = VibrationEffect.createWaveform( + timings, amplitudes, repeat + ) + // repeatingEffect can be used in multiple places. + + vibrator.vibrate(repeatingEffect) + } + + // [START_EXCLUDE] + @RequiresPermission(Manifest.permission.VIBRATE) + @RequiresApi(Build.VERSION_CODES.M) + // [END_EXCLUDE] + fun stopRepeatVibrator() { + vibrator.cancel() + } + // [END android_ui_haptics_repeat] + + @RequiresApi(api = Build.VERSION_CODES.O) + @RequiresPermission(Manifest.permission.VIBRATE) + // [START android_ui_haptics_fallback] + private fun patternWithFallback() { + val smoothTimings = longArrayOf(50, 50, 100, 50, 50) + val onOffTimings = longArrayOf(50, 100) + val amplitudes = intArrayOf(64, 128, 255, 128, 64) + val repeatIndex = -1 // Don't repeat. + + if (vibrator.hasAmplitudeControl()) { + vibrator.vibrate( + VibrationEffect.createWaveform( + smoothTimings, amplitudes, repeatIndex + ) + ) + } else { + vibrator.vibrate( + VibrationEffect.createWaveform( + onOffTimings, repeatIndex + ) + ) + } + } + // [END android_ui_haptics_fallback] +} diff --git a/misc/src/main/java/com/example/snippets/ui/haptics/CustomVibrationPatternsJava.java b/misc/src/main/java/com/example/snippets/ui/haptics/CustomVibrationPatternsJava.java new file mode 100644 index 000000000..b0db0506b --- /dev/null +++ b/misc/src/main/java/com/example/snippets/ui/haptics/CustomVibrationPatternsJava.java @@ -0,0 +1,72 @@ +package com.example.snippets.ui.haptics; + +import android.Manifest; +import android.app.Activity; +import android.os.Build; +import android.os.VibrationEffect; +import android.os.Vibrator; + +import androidx.annotation.RequiresApi; +import androidx.annotation.RequiresPermission; + +@RequiresApi(api = Build.VERSION_CODES.M) +public class CustomVibrationPatternsJava extends Activity { + Vibrator vibrator = getApplicationContext().getSystemService(Vibrator.class); + + // [START android_ui_haptics_ramp_up] + @RequiresApi(Build.VERSION_CODES.O) + @RequiresPermission(Manifest.permission.VIBRATE) + private void rampUpPattern(Vibrator vibrator) { + long[] timings = new long[] { + 50, 50, 50, 50, 50, 100, 350, 25, 25, 25, 25, 200 }; + int[] amplitudes = new int[] { + 33, 51, 75, 113, 170, 255, 0, 38, 62, 100, 160, 255 }; + int repeatIndex = -1; // Don't repeat. + + vibrator.vibrate(VibrationEffect.createWaveform( + timings, amplitudes, repeatIndex)); + } + // [END android_ui_haptics_ramp_up] + + @RequiresApi(api = Build.VERSION_CODES.O) + @RequiresPermission(Manifest.permission.VIBRATE) + // [START android_ui_haptics_repeat] + private void startVibrating() { + long[] timings = new long[] { 50, 50, 100, 50, 50 }; + int[] amplitudes = new int[] { 64, 128, 255, 128, 64 }; + int repeat = 1; // Repeat from the second entry, index = 1. + VibrationEffect repeatingEffect = VibrationEffect.createWaveform( + timings, amplitudes, repeat); + // repeatingEffect can be used in multiple places. + + vibrator.vibrate(repeatingEffect); + } + + // [START_EXCLUDE] + @RequiresPermission(Manifest.permission.VIBRATE) + // [END_EXCLUDE] + private void stopVibrating() { + vibrator.cancel(); + } + // [END android_ui_haptics_repeat] + + + // [START android_ui_haptics_fallback] + @RequiresApi(api = Build.VERSION_CODES.O) + @RequiresPermission(Manifest.permission.VIBRATE) + private void patternWithFallback() { + long[] smoothTimings = new long[] { 50, 50, 100, 50, 50 }; + long[] onOffTimings = new long[] { 50, 100 }; + int[] amplitudes = new int[] { 64, 128, 255, 128, 64 }; + int repeatIndex = -1; // Don't repeat. + + if (vibrator.hasAmplitudeControl()) { + vibrator.vibrate(VibrationEffect.createWaveform( + smoothTimings, amplitudes, repeatIndex)); + } else { + vibrator.vibrate(VibrationEffect.createWaveform( + onOffTimings, repeatIndex)); + } + } + // [END android_ui_haptics_fallback] +}