Get started with Amazon Stylus SDK
- Prerequisites
- Implementing the Amazon Stylus SDK
- Overview
- Step 1: Check for Amazon Predictive Touch availability
- Step 2: Initialize the AmazonPredictiveTouch
- Step 3: Check if PredictiveTouches is registered
- Step 4: Register for predictions
- Step 5: Set the view for which you will use predictions
- Step 6: Identify if it's a MOVE event using TouchEvent and note its timestamp and coordinates
- Step 7: Get the predictions and draw
- Step 8: Deregister predictions
- Troubleshooting
Prerequisites
Install Android Studio
We recommend you download the latest version of Android Studio, but it isn't required to implement the Predictive Touch API. If you're using another IDE, make sure you have the Android SDK (30 or above) installed and meet all other system requirements necessary for Android development. This documentation will assume you're using Android Studio.
Download the Amazon Stylus SDK for Fire Tablets
This download allows you to implement Predictive Touch in your Stylus enabled Apps. Extract the SDK contents to a location of your choice.
Add the SDK to Android Studio
- Download the SDK, save it to a preferred folder, and extract the zip file.
- Open your existing project or make a new project in Android Studio.
-
Change the folder structure from Android to Project.
-
If not already created, inside of the app (old Android Studio) or Application (newer Android Studio), create a libs folder.
- Copy the StylusSDKSupportLibrary-1.0.aar extracted in step 1 to the libs folder you just created.
- In your app's
build.gradle
file, reference the AAR file in thedependencies
object: Add the implementation files (libs/StylusSDKSupportLibrary-1.0.aar) in thebuild.gradle
file's dependency section.
dependencies {
implementation files('libs/StylusSDKSupportLibrary-1.0.aar')
Now, you can use the Amazon Stylus SDK APIs in your project.
API specifications
You will be working with these classes and APIs for predictive touch:
- com.amazon.stylussupportsdk.predictedtouches.AmazonPredictiveTouch
- com.amazon.stylussupportsdk.predictedtouches.AmazonPredictedEvents
AmazonPredictiveTouch
This class is to carry out all the tasks like registering/deregistering for Predictive Touches. It also helps in getting the predicted points, so that apps can ask for them. Always call isFeatureRuntimeAvailable(Context)
to know if the Predictive Touches are supported at runtime on device. If not supported, apps can refrain from calling other API's as those will be non-useful. The App then needs to initialize AmazonPredictiveTouch
using init(Context)
in order to use predictions.
AmazonPredictiveTouch
This class contains the predicted coordinates. An instance of this is supplied to the app it is using AmazonPredictiveTouch#getLatestPredictions(long)
.
/**
* To initialize the Predictive Touch engine.
* An app/process should call it only once, unless they deregistered explicitly earlier.
* Calling it multiple times will not be useful and may result in non-delivery of
* predictions to the app due to duplicate registrations from same process.
* @param context the context of the application.
*/
public static void init(Context ctx);
/**
* Always call this method to know if the Predictive Touches are supported at runtime
* on device. If not supported, apps can refrain from calling other
* API's as those will be non-useful
* @param context the context of the application.
* @return true if feature is supported in this device, else false
*/
public static boolean isFeatureRuntimeAvailable(Context ctx);
/**
* To manually register for getting Predictive Touches in case app deregistered earlier
* and want to re-register
* @return true if feature supported and either we are already registered or register
* is successful, else false
*/
public static boolean registerPredictiveTouches();
/**
* To manually deregister with Predictive Touches in case app doesn't want predictions
* @return true if feature supported and either we are not registered or deregister
* is successful, else false
*/
public static boolean deregisterPredictiveTouches();
/**
* To let app check if it has registered for predictions or not
* @return true if registered else false
*/
public static boolean isPredictiveTouchesRegistered();
/**
* To let the app set the view for which it needs predictions
* @param view the view in which app will use predictions, should be non null
*/
public static void setCurrentView(View v);
/**
* To get the latest prediction stored in PredictiveTouchManager it received from algorithm
* If the feature is not supported or we don't have any predictions, this will return null
* @param t latest event timestamp the app has, to get predictions after t
* @return an {@link AmazonPredictedEvents} object containing predictions or null if
* there isn't any predictions received.
*/
public static AmazonPredictedEvents getLatestPredictions(long t);
}
/**
* To get the number of predictions in this {@link AmazonPredictedEvents} instance
* @return Number of predictions
*/
public int getNumberOfPredictions();
/**
* To get the x coordinate of predicted point at passed index. Apps should check
* if -1.0f is returned,then don't use it to draw
* @param index the index of predicted point
* @return x coordinate of the predicted point at passed index if index is valid, else -1.0f
*/
public float getXAt(int index);
/**
* To get the y coordinate of predicted point at passed index. Apps should check
* if -1.0f is returned, then don't use it to draw
* @param index the index of predicted point
* @return y coordinate of the predicted point at passed index if index is valid, else -1.0f
*/
public float getYAt(int index);
/**
* To get the timestamp of predicted point at passed index. Apps should check
* if -1L is returned, then don't use it as it indicates invalid.
* @param index the index of predicted point
* @return timestamp of the predicted point at passed index if index is valid, else -1L
*/
public long getEventTimeAt(int index);
}
Implementing the Amazon Stylus SDK
Overview
- Check for feature availability using
AmazonPredictiveTouch.isFeatureRuntimeAvailable()
. - Initialize the
AmazonPredictiveTouch
usingAmazonPredictiveTouch.init()
. - Check if PredictiveTouches is registered using public static boolean
isPredictiveTouchesRegistered()
. - Register for predictions using
AmazonPredictiveTouch.registerPredictiveTouches()
. - Set the view for which you will use predictions using
AmazonPredictiveTouch.setCurrentView()
. - Identify if it's a MOVE event in
onTouchEvent
and note its timestamp and coordinates. - Get predictions using
AmazonPredictiveTouch.getLatestPredictions()
and draw usingAmazonPredictedEvents
. - Deregister predictions using
AmazonPredictiveTouch.deregisterPredictiveTouches()
.
See the details for each of these below.
Step 1: Check for Amazon Predictive Touch availability
Use the AmazonPredictiveTouch.isFeatureRuntimeAvailable(android.content.Context context
)
method to know whether the feature is available on the device.
If the feature is not available or supported, this method will return false
. Otherwise, it will be true
.
import com.amazon.stylussupportsdk.predictedtouches.AmazonPredictiveTouch;
private boolean isPredictiveTouchesSupported() {
/* sample implementation */
return AmazonPredictiveTouch.isFeatureRuntimeAvailable(this) == true;
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
..........
..........
if (isPredictiveTouchesSupported()) {
// PredictiveTouches should be available. Code
// to init and register for predictions goes here
}
else {
// PredictiveTouches not available. Write it in a log message
Log.w(TAG, "PredictiveTouches is not supported");
}
}
import com.amazon.stylussupportsdk.predictedtouches.AmazonPredictiveTouch
private fun isPredictiveTouchesSupported(): Boolean {
/* sample implementation */
return AmazonPredictiveTouch.isFeatureRuntimeAvailable(this)
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
if (isPredictiveTouchesSupported()) {
// PredictiveTouches should be available. Code
// to init and register for predictions goes here
} else {
// PredictiveTouches not available. Write it in a log message
Log.w(TAG, "PredictiveTouches is not supported")
}
}
Step 2: Initialize the AmazonPredictiveTouch
Use the AmazonPredictiveTouch.init(android.content.Context context)
method to initialize.
This should be done using the onCreate
()
method of an Activity or during View creation (if needed only for that view).
Initialization will also register with the PredictionService
by default, so you can skip calling AmazonPredictiveTouch.registerPredictiveTouches()
after this step.
Now, predictive touch APIs can be used in all app processes.
init()
more than once in the same App process. Predictive Service is designed to be used as one instance per process, so calling init()
again will be ignored.import com.amazon.stylussupportsdk.predictedtouches.AmazonPredictiveTouch;
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
..........
..........
if (isPredictiveTouchesSupported()) {
// PredictiveTouches should be available. Code
// to init and register for predictions goes here
AmazonPredictiveTouch.init(MainActivity.this);
}
else {
// PredictiveTouches not available. Write it in a log message
Log.w(TAG, "PredictiveTouches is not supported");
}
}
}
import com.amazon.stylussupportsdk.predictedtouches.AmazonPredictiveTouch
class MainActivity : Activity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
..........
..........
if (isPredictiveTouchesSupported()) {
// PredictiveTouches should be available. Code
// to init and register for predictions goes here
AmazonPredictiveTouch.init(this@MainActivity)
} else {
// PredictiveTouches not available. Write it in a log message
Log.w(TAG, "PredictiveTouches is not supported")
}
}
}
Step 3: Check if PredictiveTouches is registered
Use the utility method isPredictiveTouchesRegistered()
to check if PredictiveTouches
is registered. This returns a boolean value.
For the App to receive predictive points, you must initialize or register the listener with either init()
or registerPredictiveTouches()
. You will only need to use registerPredictiveTouches()
in the cases where you have called deregisterPredictiveTouches()
on certain views where you do not want the prediction and then come back to the view where the prediction is needed.
Step 4: Register for predictions
This is required when you used AmazonPredictiveTouch.deregisterPredictiveTouches()
earlier to save compute/memory resources as you didn't need predictions, but now you again need predictions to be enabled.
In order to start receiving predictions again, use the AmazonPredictiveTouch.registerPredictiveTouches()
method. This is an idempotent API, so calling it multiple times is not needed and will not result in different behavior. It will return true
if registration is successful, otherwise it will return false
.
import com.amazon.stylussupportsdk.predictedtouches.AmazonPredictiveTouch;
public class MainActivity extends Activity {
@Override
protected void onResume() {
super.onResume();
// Assuming that at resume, user will be writing/drawing, etc. and we deregistered earlier,
// so need to register for predictions.
AmazonPredictiveTouch.registerPredictiveTouches();
.............
.............
}
}
import com.amazon.stylussupportsdk.predictedtouches.AmazonPredictiveTouch
class MainActivity : Activity() {
override fun onResume() {
super.onResume()
// Assuming that at resume, user will be writing/drawing, etc. and we deregistered earlier,
// so need to register for predictions.
AmazonPredictiveTouch.registerPredictiveTouches()
}
}
Step 5: Set the view for which you will use predictions
Each view has its own geometrical transform. For instance, its position on the screen, possible rotation, or scaling effects. The touch points need to be transformed accordingly.
In order to provide accurate predicted events to the current view where you will use predictions, set your current view. This can be done by switching to a new view that uses predictions. Use the method setCurrentView
(android.view.View view)
.
Call this method before drawing in onTouchEvent()
for MotionEvent.ACTION_DOWN
for each view, which gets called every time a view is touched.
import com.amazon.stylussupportsdk.predictedtouches.AmazonPredictiveTouch;
public class TouchDisplayView extends View {
@Override
public boolean onTouchEvent(MotionEvent event) {
// existing code
final int action = event.getAction();
switch (action & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN: {
AmazonPredictiveTouch.setCurrentView(this);
// existing code
}
}
}
}
import com.amazon.stylussupportsdk.predictedtouches.AmazonPredictiveTouch
class TouchDisplayView : View() {
override fun onTouchEvent(event: MotionEvent): Boolean {
// existing code
val action = event.action
when (action and MotionEvent.ACTION_MASK) {
MotionEvent.ACTION_DOWN -> {
AmazonPredictiveTouch.setCurrentView(this)
}
}
}
}
Step 6: Identify if it's a MOVE event using TouchEvent and note its timestamp and coordinates
Only use predictions for MOVE events. You can identify if it’s a MOVE event using the onTouchEvent()
method. Make note of its timestamp and coordinates. To predict points for the latest MOVE event, use the timestamp. Also, we want to draw predicted points after the latest touch points X, Y coordinates, so we need the coordinates for it.
This can be done using onTouchEvent()
with variables such as mLatestX
, mLatestY
, and mCurrentMoveTime
. See the sample code below.
import com.amazon.stylussupportsdk.predictedtouches.AmazonPredictiveTouch;
public class TouchDisplayView extends View {
private long mCurrentMoveTime = -1;
private float mLatestX, mLatestY;
private Path mCurrentStroke = new Path(); // Path to store stroke data
@Override
public boolean onTouchEvent(MotionEvent event) {
............
// Set the timestamp -1
mCurrentMoveTime = -1;
switch (event.getAction() & MotionEvent.ACTION_MASK) {
.............
case MotionEvent.ACTION_MOVE: {
// Get the timestamp, X, Y values of current move event
mCurrentMoveTime = event.getEventTime();
mLatestX = event.getX();
mLatestY = event.getY();
for (i in 0 until event.historySize) {
// add history points to current stroke
mCurrentStroke.lineTo(event.getHistoricalX(0, i), event.getHistoricalY(0, i))
}
// We are using lineTo for the sake of example, please use custom draw logic as required
mCurrentStroke.lineTo(mLatestX,mLatestY); // add point to current stroke
this.invalidate();
break;
}
.............
}
}
}
import com.amazon.stylussupportsdk.predictedtouches.AmazonPredictiveTouch
class TouchDisplayView : View() {
private var mCurrentMoveTime: Long = -1
private var mLatestX = 0f
private var mLatestY = 0f
private val mCurrentStroke: Path = Path() // Path to store stroke data
override fun onTouchEvent(event: MotionEvent): Boolean {
// Set the timestamp -1
mCurrentMoveTime = -1
when (event.action and MotionEvent.ACTION_MASK) {
MotionEvent.ACTION_MOVE -> {
// Get the timestamp, X, Y values of current move event
mCurrentMoveTime = event.eventTime
mLatestX = event.x
mLatestY = event.y
for (i in 0 until event.historySize) {
// add history points to current stroke
mCurrentStroke.lineTo(event.getHistoricalX(0, i), event.getHistoricalY(0, i))
}
// We are using lineTo for the sake of example, please use custom draw logic as required
mCurrentStroke.lineTo(mLatestX, mLatestY) // add point to current stroke
this.invalidate()
}
}
}
}
Step 7: Get the predictions and draw
Now that we know it's a MOVE event and have its timestamp and its coordinates, we need to get the predictions corresponding to this timestamp.
- Use
getLatestPredictions(long t)
to get the predictions. - This will give the
AmazonPredictedEvents
object. You can use this object to find the following:- The number of predictions using the
getNumberOfPredictions()
method. This returns an integer. - Event time using
getEventTimeAt(int index)
. This returns a timestamp of type long. - X / Y coordinates using the
getXAt(int index)
andgetYAt(int index)
methods. These return float values.
- The number of predictions using the
-
Add the predictions to the current path or separate path for predictions to draw.
Important: Clear the canvas. Otherwise the predictions from the last draw will also be visible. - Draw the path containing predictions.
import com.amazon.stylussupportsdk.predictedtouches.AmazonPredictiveTouch;
import com.amazon.stylussupportsdk.predictedtouches.AmazonPredictedEvents;
public class TouchDisplayView extends View {
@Override
protected void onDraw(Canvas canvas) {
// Get the latest predictions corresponding to mCurrentMoveTime
AmazonPredictedEvents latestPred = AmazonPredictiveTouch.getLatestPredictions(mCurrentMoveTime);
Path predicted = new Path();
// Add the predictions to Path if latest predictions are available
if (mCurrentMoveTime != -1 /*valid move*/ && latestPred != null
&& latestPred.getNumberOfPredictions() != 0) {
Log.i(TAG,
"DRAWSTART EVENTTIME: " + mCurrentMoveTime + " currentTime "
+ SystemClock.uptimeMillis() + " latestPredTime "
+ latestPred.getEventTimeAt(latestPred.getNumberOfPredictions() - 1));
predicted.moveTo(mLatestX, mLatestY);
for (int i = 0; i < latestPred.getNumberOfPredictions(); i++) {
// We are using lineTo for the sake of example, please use custom draw logic as required
if (latestPred.getXAt(i) > -1.0f && latestPred.getYAt(i) > -1.0f)
predicted.lineTo(latestPred.getXAt(i), latestPred.getYAt(i));
}
}
// Clear the canvas first, so that last predicted points get erased and we don't have unwanted artifacts
canvas.drawColor(Color.WHITE);
// Draw the actual points
canvas.drawPath(mCurrentStroke, mPaint);
// Draw the path containing predictions with another Paint object, to confirm
if (!predicted.isEmpty())
canvas.drawPath(predicted, mPaintPred);
if (mCurrentMoveTime != -1) {
Log.i(TAG, "DRAWEND EVENTTIME: " + mCurrentMoveTime + " currentTime " + SystemClock.uptimeMillis());
}
super.onDraw(canvas);
}
}
import com.amazon.stylussupportsdk.predictedtouches.AmazonPredictiveTouch
import com.amazon.stylussupportsdk.predictedtouches.AmazonPredictedEvents
class TouchDisplayView() : View() {
override fun onDraw(canvas: Canvas) {
// Get the latest predictions corresponding to mCurrentMoveTime
val latestPred: AmazonPredictedEvents =
AmazonPredictiveTouch.getLatestPredictions(mCurrentMoveTime)
val predicted = Path()
// Add the predictions to Path if latest predictions are available
if (mCurrentMoveTime !== -1 /*valid move*/ && latestPred != null && latestPred.getNumberOfPredictions() !== 0) {
Log.i(
TAG,
"DRAWSTART EVENTTIME: " + mCurrentMoveTime.toString() + " currentTime "
+ SystemClock.uptimeMillis().toString() + " latestPredTime "
+ latestPred.getEventTimeAt(latestPred.getNumberOfPredictions() - 1)
)
predicted.moveTo(mLatestX, mLatestY)
for (i in 0 until latestPred.getNumberOfPredictions()) {
// We are using lineTo for the sake of example, please use custom draw logic as required
if (latestPred.getXAt(i) > -1.0f && latestPred.getYAt(i) > -1.0f) predicted.lineTo(
latestPred.getXAt(i),
latestPred.getYAt(i)
)
}
}
// Clear the canvas first, so that last predicted points get erased and we don't have unwanted artifacts
canvas.drawColor(Color.WHITE)
// Draw the actual points
canvas.drawPath(mCurrentStroke, mPaint)
// Draw the path containing predictions with another Paint object, to confirm
if (!predicted.isEmpty()) canvas.drawPath(predicted, mPaintPred)
if (mCurrentMoveTime !== -1) {
Log.i(
TAG,
"DRAWEND EVENTTIME: " + mCurrentMoveTime.toString() + " currentTime " + SystemClock.uptimeMillis()
)
}
super.onDraw(canvas)
}
}
Step 8: Deregister predictions
Call the deregisterPredictiveTouches()
method to deregister PredictiveTouches
. You may consider doing this when the app moves to an Activity or View that does not need predictions, or your app goes into the background, to reduce CPU/Memory usage.
It is important that you link deregisterPredictiveTouches()
of PredictiveTouches
with the lifecycle of the Activity/View where it was initialized.
For example, for an activity, use init()
in the onCreate()
method and use deregisterPredictiveTouches()
in the onDestroy()
method. In scenarios where the process is not terminated but the Activity or View is destroyed, you will not be able re-initialize PredictiveTouches
in the onCreate()
method if it was not deregistered earlier. Display rotation is an example where the process is not killed (the process is still registered with the service), but the activity is re-created (which tries to create a new instance).
On a successful deregister, the method will return true
, otherwise it will return false
.
import com.amazon.stylussupportsdk.predictedtouches.AmazonPredictiveTouch;
public class MainActivity extends Activity {
// OPTIONAL: can do this when you want to stop for predictions while your app in background to save the predictive touches memory footprint.
@Override
protected void onPause() {
AmazonPredictiveTouch.deregisterPredictiveTouches();
super.onPause();
}
// This is a must for components who can die while the process is still alive (like activity, view, etc)
@Override
protected void onDestroy() {
AmazonPredictiveTouch.deregisterPredictiveTouches();
super.onDestroy();
}
}
import com.amazon.stylussupportsdk.predictedtouches.AmazonPredictiveTouch
class MainActivity : Activity() {
// OPTIONAL: can do this when you want to stop for predictions while your app in background to save the predictive touches memory footprint.
override fun onPause() {
AmazonPredictiveTouch.deregisterPredictiveTouches()
super.onPause()
}
// This is a must for components who can die while the process is still alive (like activity, view, etc)
override fun onDestroy() {
AmazonPredictiveTouch.deregisterPredictiveTouches()
super.onDestroy()
}
}
Troubleshooting
If you are having trouble getting or drawing predictions, check the following:
- Is the feature available on the device?
- Solution: Update Fire OS to latest version and call
AmazonPredictiveTouch.isFeatureRuntimeAvailable()
to check if your device supports this feature. Refer to Step 1 for details. - Do I need to maintain two APKs if my app will also be available on devices that don’t support Predictive Touch?
- Solution: No, there is no need to maintain two different APKs. Use
AmazonPredictiveTouch.isFeatureRuntimeAvailable()
to determine if the device supports Predictive Touch before initializingAmazonPredictiveTouch
. - Can I draw the predictions with a separate paint object (like a different color) and see that the predictions are being drawn?
- Solution: Use the code sample in Step 7.
- Registering predictions (Step 4) or setting the view for predictions (Step 5) fails:
- Currently, for a process we only support one channel for receiving predictions, and so
init()
should only be called once in a process. - It’s not uncommon to have multiple activities or a rotation case where the same activity is recreated and
init()
is called again. - Solutions:
- Use
deregisterPredictiveTouches()
when exiting an activity/view whereinit()
was called. - Use the
isPredictiveTouchesRegistered()
API to check ifinit()
needs to be called. If it returnstrue
, there is no need to callinit()
again.
- Use
Last updated: Nov 10, 2023