Skip to content

Commit

Permalink
1. Implimented haptic primitives
Browse files Browse the repository at this point in the history
2. Removed descriptions of Android R
3. Updated readme
  • Loading branch information
PBBB committed Mar 10, 2022
1 parent f802f24 commit a6dafa7
Show file tree
Hide file tree
Showing 10 changed files with 44 additions and 68 deletions.
33 changes: 22 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,21 +1,32 @@
# AndroidHapticFeedbackTest
An app to test haptic feedback on Android. If you need to compose haptics feedbacks or vibration effects, this app is a good start.
An app to test haptic feedbacks on Android. If you need to compose haptic feedbacks or vibration effects, this app is a good start.

Note that haptics feedbacks and vibration effects behave dramatically differently on devices from different vendors with Pixel series showing the best behaviors. Be sure to test on the devices that your products will support and this is the main reason why I started this app.
Note that haptic feedbacks and vibration effects behave dramatically differently on devices from different vendors with Pixel series showing the best behaviors. Be sure to test on the devices that your products will support and this is the main reason why I started this app.

## Read First
There are official Android documents talking about haptics in detail: [Haptics](https://source.android.com/devices/input/haptics). You should read them first to know the basics about haptics and what you can do with those APIs, before using this app to get hands on the actual feeling of the haptics.

There are three ways to orchestrate haptic patterns:
- **Haptic Feedback**: suitable for input events (like long press, or swipe), or UI elements (like keyboard).
- **OneShot & Waveform**: suitable for more refined vibration patterns that last over time.
- **Composition**: According to the documents, compositions enable stringing together sequences of more nuanced haptics or vibarations. However, none of the devices I tested supports this feature including Pixel 5, so in this app you can only try the primitive haptics instead of composing them.

## Haptics
- Uses [View#performHapticFeedback](https://developer.android.com/reference/android/view/View#performHapticFeedback(int)).
- You can test each one of [HapticFeedbackConstants](https://developer.android.com/reference/android/view/HapticFeedbackConstants).
- You can test hidden haptics using HapticFeedback ID (can be found in the HapticFeedbackConstants.java, such as [this one](https://cs.android.com/android/platform/superproject/+/master:frameworks/base/core/java/android/view/HapticFeedbackConstants.java)).

1. Uses [View#performHapticFeedback](https://developer.android.com/reference/android/view/View#performHapticFeedback(int))
2. You can test each one of [HapticFeedbackConstants](https://developer.android.com/reference/android/view/HapticFeedbackConstants)
3. You can test hidden haptics using HapticFeedback ID (can be found in the HapticFeedbackConstants.java, such as [this one](https://cs.android.com/android/platform/superproject/+/master:frameworks/base/core/java/android/view/HapticFeedbackConstants.java))
## OneShot & Waveform
- Uses [VibrationEffect](https://developer.android.com/reference/android/os/VibrationEffect).
- Supports OneShot, Predefined and Waveform effects.

## Vibrator
1. Uses [Vibrator](https://developer.android.com/reference/android/os/Vibrator)
2. Supports OneShot, Predefined and Waveform [vibration effects](https://developer.android.com/reference/android/os/VibrationEffect).
## Composition
- Uses [VibrationEffect.Composition](https://developer.android.com/reference/kotlin/android/os/VibrationEffect.Composition).
- You can test all haptic primitives *which I failed to feel on all the devices available to me*.
- If your devices support any haptic primitives, feel free to contact me.

## Some Notes
This app doesn't have any data validation, so the app just crashes if the data can't be parsed. I'm very new to Android development and Kotlin, and this app is just for testing purposes, so this won't be optimized in the near future.

This app doesn't have any data validation, so the app just crashes if the data can't be parsed. And the code is really a mess as a result of my unfamiliarity with Android development and Kotlin.

## Screenshots
<img src="https://raw.githubusercontent.com/PBBB/AndroidHapticFeedbackTest/master/readme/Haptics.png" width="360"> <img src="https://raw.githubusercontent.com/PBBB/AndroidHapticFeedbackTest/master/readme/Vibrator.png" width="360">
<img src="https://raw.githubusercontent.com/PBBB/AndroidHapticFeedbackTest/master/readme/Haptics.png" width="360"> <img src="https://raw.githubusercontent.com/PBBB/AndroidHapticFeedbackTest/master/readme/OneShotWaveform.png" width="360"> <img src="https://raw.githubusercontent.com/PBBB/AndroidHapticFeedbackTest/master/readme/Composition.png" width="360">
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.os.VibrationEffect
import android.os.Vibrator
import android.view.HapticFeedbackConstants
import android.view.View
import android.widget.Button
import kotlinx.android.synthetic.main.activity_composition.*
Expand All @@ -29,14 +28,14 @@ class CompositionActivity : AppCompatActivity() {
var compositon = VibrationEffect.startComposition()

when (view.text.toString()) {
"PRIMITIVE_CLICK (1)" -> compositon = compositon.addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK)
"PRIMITIVE_THUD (2, Android S)" -> compositon = compositon.addPrimitive(VibrationEffect.Composition.PRIMITIVE_THUD)
"PRIMITIVE_SPIN (3, Android S)" -> compositon = compositon.addPrimitive(VibrationEffect.Composition.PRIMITIVE_SPIN)
"PRIMITIVE_QUICK_RISE (4)" -> compositon = compositon.addPrimitive(VibrationEffect.Composition.PRIMITIVE_QUICK_RISE)
"PRIMITIVE_SLOW_RISE (5)" -> compositon = compositon.addPrimitive(VibrationEffect.Composition.PRIMITIVE_SLOW_RISE)
"PRIMITIVE_QUICK_FALL (6)" -> compositon = compositon.addPrimitive(VibrationEffect.Composition.PRIMITIVE_QUICK_FALL)
"PRIMITIVE_TICK (7)" -> compositon = compositon.addPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK)
"PRIMITIVE_LOW_TICK (8, Android S)" -> compositon = compositon.addPrimitive(VibrationEffect.Composition.PRIMITIVE_LOW_TICK)
"PRIMITIVE_CLICK (1)" -> compositon.addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK)
"PRIMITIVE_THUD (2, Android S)" -> compositon.addPrimitive(VibrationEffect.Composition.PRIMITIVE_THUD)
"PRIMITIVE_SPIN (3, Android S)" -> compositon.addPrimitive(VibrationEffect.Composition.PRIMITIVE_SPIN)
"PRIMITIVE_QUICK_RISE (4)" -> compositon.addPrimitive(VibrationEffect.Composition.PRIMITIVE_QUICK_RISE)
"PRIMITIVE_SLOW_RISE (5)" -> compositon.addPrimitive(VibrationEffect.Composition.PRIMITIVE_SLOW_RISE)
"PRIMITIVE_QUICK_FALL (6)" -> compositon.addPrimitive(VibrationEffect.Composition.PRIMITIVE_QUICK_FALL)
"PRIMITIVE_TICK (7)" -> compositon.addPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK)
"PRIMITIVE_LOW_TICK (8, Android S)" -> compositon.addPrimitive(VibrationEffect.Composition.PRIMITIVE_LOW_TICK)
}

val viborationEffect = compositon.compose()
Expand Down
20 changes: 5 additions & 15 deletions app/src/main/java/com/pbb/study/hapticfeedback/MainActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,7 @@ package com.pbb.study.hapticfeedback
import android.content.DialogInterface
import android.content.Intent
import android.os.Bundle
import android.view.HapticFeedbackConstants
import android.view.Menu
import android.view.MenuItem
import android.view.View
import android.view.*
import android.widget.Button
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity
Expand Down Expand Up @@ -57,17 +54,10 @@ class MainActivity : AppCompatActivity() {
"KEYBOARD_RELEASE (7)" -> view.performHapticFeedback(HapticFeedbackConstants.KEYBOARD_RELEASE)
"VIRTUAL_KEY_RELEASE (8)" -> view.performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY_RELEASE)
"TEXT_HANDLE_MOVE (9)" -> view.performHapticFeedback(HapticFeedbackConstants.TEXT_HANDLE_MOVE)
}
}
}

fun performAndroidRHaptics (view: View) {
if (view is Button) {
when (view.text.toString()) {
"GESTURE_START (12, Android R)" -> view.performHapticFeedback(12)
"GESTURE_END (13, Android R)" -> view.performHapticFeedback(13)
"CONFIRM (16, Android R)" -> view.performHapticFeedback(16)
"REJECT (17, Android R)" -> view.performHapticFeedback(17)
"GESTURE_START (12)" -> view.performHapticFeedback(HapticFeedbackConstants.GESTURE_START)
"GESTURE_END (13)" -> view.performHapticFeedback(HapticFeedbackConstants.GESTURE_END)
"CONFIRM (16)" -> view.performHapticFeedback(HapticFeedbackConstants.CONFIRM)
"REJECT (17)" -> view.performHapticFeedback(HapticFeedbackConstants.REJECT)
}
}
}
Expand Down
24 changes: 0 additions & 24 deletions app/src/main/res/layout/activity_composition.xml
Original file line number Diff line number Diff line change
Expand Up @@ -166,30 +166,6 @@
android:text="This very short low frequency effect should produce a light crisp sensation intended to be used repetitively for dynamic feedback."
android:textAlignment="viewStart" />

<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal">

<EditText
android:id="@+id/hapticsID"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:autofillHints="hapticsID"
android:ems="10"
android:hint="Custom ID"
android:inputType="number"
android:minHeight="48dp" />

<Button
android:id="@+id/button14"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="performPrimitives"
android:text="Play Haptics" />

</LinearLayout>
</LinearLayout>
</ScrollView>
</androidx.constraintlayout.widget.ConstraintLayout>
16 changes: 8 additions & 8 deletions app/src/main/res/layout/activity_main.xml
Original file line number Diff line number Diff line change
Expand Up @@ -79,29 +79,29 @@
android:id="@+id/button15"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="performAndroidRHaptics"
android:text="GESTURE_START (12, Android R)" />
android:onClick="performHaptics"
android:text="GESTURE_START (12)" />

<Button
android:id="@+id/button16"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="performAndroidRHaptics"
android:text="GESTURE_END (13, Android R)" />
android:onClick="performHaptics"
android:text="GESTURE_END (13)" />

<Button
android:id="@+id/button3"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="performAndroidRHaptics"
android:text="CONFIRM (16, Android R)" />
android:onClick="performHaptics"
android:text="CONFIRM (16)" />

<Button
android:id="@+id/button10"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="performAndroidRHaptics"
android:text="REJECT (17, Android R)" />
android:onClick="performHaptics"
android:text="REJECT (17)" />

<LinearLayout
android:layout_width="wrap_content"
Expand Down
2 changes: 1 addition & 1 deletion app/src/main/res/menu/menus.xml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

<item
android:id="@+id/composition"
android:title="Composition (Android R)" />
android:title="Composition" />
<item
android:id="@+id/vibrator"
android:title="OneShot &amp; Waveform"
Expand Down
Binary file added readme/Composition.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified readme/Haptics.png
100644 → 100755
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added readme/OneShotWaveform.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file removed readme/Vibrator.png
Binary file not shown.

0 comments on commit a6dafa7

Please sign in to comment.