Jan 9, 2014

Sony SmartWatch App Development 4

Standard
In previous article, we looked at Notification API with Hello SmartWatch demo. In this article, we will focus on Control API.



Control API is responsible for 

  • Extension lifecycle: It allows you to start and stop extension using Intent. Extension means SmartWatch Host application.
    • Control.Intents.CONTROL_START_INTENT
    • Control.Intents.CONTROL_STOP_INTENT.
  • Controlling the display: Normally we don't require to control the display. Default screen state is "Auto", for the specific need you can change screen state by using Intent.
    • Control.Intents.SCREEN_STATE_OFF
    • Control.Intents.SCREEN_STATE_AUTO
    • Control.Intents.SCREEN_STATE_DIM
    • Control.Intents.SCREEN_STATE_ON
  • Controlling the LEDs: It used to switch on /off LEDs based on notification.
    • Control.Intents.CONTROL_LED_INTENT
  • Controlling the vibrator: It allows you to vibrate SmartWatch.
    • Control.Intents.CONTROL_VIBRATE_INTENT
  • Key event: It is used to capture hardware key i.e. home, back and menu key, which is available in SmartWatch2.
    • Control.Intents.CONTROL_KEY_EVENT_INTENT
  • Touch event: It is used to capture touch events.
    • Control.Intents.CONTROL_TOUCH_EVENT_INTENT
    • Control.Intents.CONTROL_SWIPE_EVENT_INTENT
    • Control.Intents.CONTROL_OBJECT_CLICK_EVENT_INTENT 
Sony's Add-on SDK version 2.0 supports ListView and GridView but this article focuses on basic controls only. For the demo I will create a simple layout with ImageView and TextView. I will animate ImageView with thread and also capture onClick event of ImageView.

Screen Shots


Open Accessory emulator and select SmartWatch 2

Select Control API

Select Hello SmartWatch Extension

Enjoy dancing android

Click on Android will fire onClick event from host app to extension app
Click on menu hardware button to make menu item visible. 
Let's extend example which was previously developed.

Step 1:  Create java class named "HelloControlSmartWatch2.java" and extend with ControlExtension.
package com.kpbird.hellosmartwatch;
import android.content.Context;
import android.location.Address;
import android.os.Bundle;
import android.os.Handler;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.Toast;

import com.sonyericsson.extras.liveware.aef.control.Control;
import com.sonyericsson.extras.liveware.extension.util.control.ControlExtension;
import com.sonyericsson.extras.liveware.extension.util.control.ControlObjectClickEvent;
import com.sonyericsson.extras.liveware.extension.util.control.ControlTouchEvent;
import com.sonyericsson.extras.liveware.extension.util.control.ControlView;
import com.sonyericsson.extras.liveware.extension.util.control.ControlView.OnClickListener;
import com.sonyericsson.extras.liveware.extension.util.control.ControlViewGroup;

public class HelloControlSmartWatch2 extends ControlExtension {

    private static final int ANIMATION_DELTA_MS = 500;
    private static final int MENU_ITEM_0 = 0;
    private static final int MENU_ITEM_1 = 1;
    private static final int MENU_ITEM_2 = 2;

    
    private boolean mIsShowingAnimation = false;   
    private Animation mAnimation = null;    
 private Handler mHandler;
    private ControlViewGroup mLayout = null;
    private String TAG = this.getClass().getSimpleName();
    private Bundle[] mMenuItemsText = new Bundle[3];


 public HelloControlSmartWatch2(Context context, String hostAppPackageName,Handler handler) {
  super(context, hostAppPackageName);
   if (handler == null) {
             throw new IllegalArgumentException("handler == null");
      }
      mHandler = handler;
      LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
      View layout = inflater.inflate(R.layout.hello_control_2, null);
      mLayout = parseLayout(layout);
      if(mLayout !=null){
       ControlView androidAnim = mLayout.findViewById(R.id.imgHelloControl);
       androidAnim.setOnClickListener(new OnClickListener() {
    
    @Override
    public void onClick() {
      Toast.makeText(mContext, "Button Clicked", Toast.LENGTH_LONG).show();
      Log.i(TAG, "Android Button Clicked");
      toggleAnimation();
    }
   });  
      
      }
      
      
         mMenuItemsText[0] = new Bundle();
         mMenuItemsText[0].putInt(Control.Intents.EXTRA_MENU_ITEM_ID, MENU_ITEM_0);
         mMenuItemsText[0].putString(Control.Intents.EXTRA_MENU_ITEM_TEXT, "Item 1");
         
         mMenuItemsText[1] = new Bundle();
         mMenuItemsText[1].putInt(Control.Intents.EXTRA_MENU_ITEM_ID, MENU_ITEM_1);
         mMenuItemsText[1].putString(Control.Intents.EXTRA_MENU_ITEM_TEXT, "Item 2");
         
         mMenuItemsText[2] = new Bundle();
         mMenuItemsText[2].putInt(Control.Intents.EXTRA_MENU_ITEM_ID, MENU_ITEM_2);
         mMenuItemsText[2].putString(Control.Intents.EXTRA_MENU_ITEM_TEXT, "Item 3");
         
 }
 
 @Override
 public void onResume() {
  super.onResume();
   showLayout(R.layout.hello_control_2, null);
   startAnimation();
 }
 
 @Override
 public void onPause() {
  super.onPause();
        stopAnimation();
 }
 
    @Override
    public void onDestroy() {   
     stopAnimation();
        mHandler = null;
    };
 

    public static int getSupportedControlWidth(Context context) {
        return context.getResources().getDimensionPixelSize(R.dimen.smart_watch_2_control_width);
    }

    public static int getSupportedControlHeight(Context context) {
        return context.getResources().getDimensionPixelSize(R.dimen.smart_watch_2_control_height);
    }

    
    @Override
    public void onTouch(ControlTouchEvent event) {
     Log.i(TAG, "onTouch() " + event.getAction());
    }
    @Override
    public void onObjectClick(ControlObjectClickEvent event) {
      Log.i(TAG, "onObjectClick() " + event.getClickType());
      if (event.getLayoutReference() != -1) {
             mLayout.onClick(event.getLayoutReference());
         }
    }
    @Override
    public void onKey(int action, int keyCode, long timeStamp) {
        Log.i(TAG, "onKey() " + action + "\t" + keyCode + "\t" + timeStamp);
        if (action == Control.Intents.KEY_ACTION_RELEASE
                && keyCode == Control.KeyCodes.KEYCODE_OPTIONS) {
           showMenu(mMenuItemsText);
        }
    }
    
    @Override
    public void onMenuItemSelected(int menuItem) {
        Log.d(TAG, "onMenuItemSelected() - menu item " + menuItem);
        if (menuItem == MENU_ITEM_0) {
            clearDisplay();
            mHandler.postDelayed(new Runnable() {
                @Override
                public void run() {
                    onResume();
                }
            }, 1000);
        }

    }
    
    private void toggleAnimation() {
        if (mIsShowingAnimation) {
            stopAnimation();
        }
        else {
            startAnimation();
        }
    }
    
    private void startAnimation() {
        if (!mIsShowingAnimation) {
            mIsShowingAnimation = true;
            mAnimation = new Animation();
            mAnimation.run();
        }
    }
    private void stopAnimation() {
        if (mIsShowingAnimation) {
            // Stop animation on accessory
            if (mAnimation != null) {
                mAnimation.stop();
                mHandler.removeCallbacks(mAnimation);
                mAnimation = null;
            }
            mIsShowingAnimation = false;
        }
    }

    private class Animation implements Runnable {

        private int mIndex = 1;
        private boolean mIsStopped = false;
        
        Animation() {
            mIndex = 1;
        }

        public void stop() {
            mIsStopped = true;
        }

        @Override
        public void run() {
            int resourceId;
            switch (mIndex) {
                case 1:
                    resourceId = R.drawable.dancing_android_0;
                    break;
                case 2:
                    resourceId = R.drawable.dancing_android_1;
                    break;

                default:
                    Log.e(TAG, "mIndex out of bounds: " + mIndex);
                    resourceId = R.drawable.dancing_android_0;
                    break;
            }
            mIndex++;
            if (mIndex > 2) {
                mIndex = 1;
            }

            if (!mIsStopped) {
                updateAnimation(resourceId);
            }
            if (mHandler != null && !mIsStopped) {
                mHandler.postDelayed(this, ANIMATION_DELTA_MS);
            }
        }
        private void updateAnimation(int resourceId) {
            sendImage(R.id.imgHelloControl, resourceId);
        }
    };
}


Step 2: Open HelloExtensionService.java and override "createExtensionControl" method. In this method we need to identify accessory type before creating object of HelloControlSmartWatch2 class. To identify accessory type ExtensionUtils class has method named "DeviceInfoHealper".


  
@Override
   public ControlExtension createControlExtension(String hostAppPackageName) {
        // First we check if the API level and screen size required for
        // SampleControlSmartWatch2 is supported
        boolean advancedFeaturesSupported = DeviceInfoHelper.isSmartWatch2ApiAndScreenDetected(this, hostAppPackageName);
        if (advancedFeaturesSupported) {
            return new HelloControlSmartWatch2(this,hostAppPackageName, new Handler());
        } else {
         return null;
        }
    }
Step 3: Open HelloRegistrationInformation.java and change return value from 0 to 2 of "getRequiredControlApiVersion" method, 0 means our example doesn't require Control API, 1 and 2 represent minimum version of API required. We also need to override method named "isDisplaySizeSupported" for the confirmation of Display Size.


...
...
@Override
 public int getRequiredControlApiVersion() {
  return 2;
 }
....

@Override
    public boolean isDisplaySizeSupported(int width, int height) {
        return ((width == HelloControlSmartWatch2.getSupportedControlWidth(mContext)
                && height == HelloControlSmartWatch2
                        .getSupportedControlHeight(mContext) || width == HelloControlSmartWatch2
                .getSupportedControlWidth(mContext) && height == HelloControlSmartWatch2
                .getSupportedControlHeight(mContext)) );
    }
...
...

Step 4: Finally, Open AndroidManiFest.xml and add following action in HelloExtensionReceiver. It will require to capture onClick, onTouch and onObject click event.
...
...
                <action android:name="com.sonyericsson.extras.aef.control.TOUCH_EVENT" />
                <action android:name="com.sonyericsson.extras.aef.control.SWIPE_EVENT" />
                <action android:name="com.sonyericsson.extras.aef.control.OBJECT_CLICK_EVENT" />
                <action android:name="com.sonyericsson.extras.aef.control.MENU_ITEM_SELECTED" />
...
...

Step 5: Compile & Execute HelloSmartWatch example



Note: 1. Import project in eclipse 2. Make sure that you change SmartExtensionUtils and SmartExtensionAPI path from project properties.

1 comment:

  1. amark1:19 PM

    Hi Ketan,
    I'm not sure what I am missing here?
    When I download the code and go through the steps of the tutorial, the notification application seems to be running, not the Control API app.
    best regards,
    Anders

    ReplyDelete