Mar 13, 2014

Use ADB over Wi-Fi without extra application or software

Image Source: http://techbeasts.com/wp-content/uploads/2013/08/Android-Developer2.png

Normally developers use USB cable to connect Android device with computer for debug and other purposes.


What are the problems with USB ?
1. You can not move around with device (some app require to move around for testing)
2. Cable damage
3. USB socket of Android device can be damage


What is solution of above problems ?
Normally  developers use ADB over USB but it is also possible to use ADB over Wi-fi. Following steps explain how to use ADB over Wifi


Prerequisites
Android device and computer should be connected in same network.  


Step 1: Connect Android device with USB cable to computer
Step 2: Use following command in terminal to make sure adb is running in USB mode.


$adb usb
restarting in USB mode
Connect to the device over USB.


Step 3: Execute following command in terminal to make sure adb identify/list gets connected with the device.


$adb devices


Step 4: Change adb mode from USB to tcpip using following command.


$adb tcpip 5555
restarting in TCP mode port: 5555


Step 5: Now, adb is running over TCP/IP mode, Let’s find IP address of Android device. Go to Settings in Android device -> About -> Status -> IP address. note down the IP address of connected Android Device.


Step 6: Use following command to connect ADB with IP address


$adb connect #.#.#.#
connected to #.#.#.#:5555


Step 7: Now adb is working over Wi-fi, You can remove USB cable from Android device.


Step 8: To confirm adb is working over Wi-fi and your device is still connect. you can use following command


$adb devices
#.#.#.#:5555 device


You’re now ready to go!, Enjoy ADB over Wi-fi.

Note: Use following command to change ADB mode to USB

$adb usb

Jan 9, 2014

Sony SmartWatch App Development 4

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.

Dec 27, 2013

Sony SmartWatch App Development 3




In previous two articles I covered development environment setup and anatomy of SmartWatch. This article will contain deep dive into coding with simple Hello Word example.


This demo will cover Notification API only. We will develop a single Android project named "Hello SmartWatch" which acts as a Host application and Extension application both. It means I will use single codebase for both things. Hello SmartWatch project has an Activity to send and clear notification to SmartWatch. 

I assume that you have already imported SmartExtensionAPI and SmartExtensionUtils libraries in workspace.

Let's jump to the code.

List of Classes and purpose
  • MainActivity.java: It is use to generate and clear notification.
  • HelloExtensionReceiver.java: It receives input event generated by SmartWatch and forward control to the Extension Service.
  • HelloExtensionService.java: core logic of Host application needs to be written in ExtensionService. It is also responsible to register a host application to SmartWatch.
  • HelloRegistrationInformation.java: It provides essential information to register host application and API requirement. 

This demo application contains only above four classes but actual application may have more classes. In above classes MainActivity acts as a Smart Extension application and rest of classes acts as a Host application.


Step 1: Create new Android project in eclipse named "HelloSmartWatch".
Step 2: Let's create UI first. Open activity_main.xml and put two EditText box for Title and Message respectively and two buttons for Send and Clear notification respectively.


<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context=".MainActivity" >

    <TextView
        android:id="@+id/textView1"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:gravity="center_horizontal"
        android:text="Send Notification"
        android:textAppearance="?android:attr/textAppearanceLarge" />

    <EditText
        android:id="@+id/etTitle"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:ems="10"
        android:hint="Title" >
    </EditText>

    <EditText
        android:id="@+id/etMessage"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:ems="10"
        android:hint="Message" >
    </EditText>

    <Button
        android:id="@+id/btnSend"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:onClick="buttonClicked"
        android:text="Send Notification" />

    <Button
        android:id="@+id/btnClearn"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:onClick="buttonClicked"
        android:text="Clear Notification" />

</LinearLayout>

Step 3: Let's make button alive by writing action in MainActivity.java. I am using onClick property of buttons, so I don't require to find object of button and setOnClickListener. I can write code in "buttonClicked" method which is defined. When a user click on "Send Notification" button I am simply firing ExtensionService with INTENT_ACTION_ADD action and other required data and same for "Clear Notification" but with INTENT_ACTION_CLEAR action.


package com.kpbird.hellosmartwatch;

import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.EditText;

public class MainActivity extends Activity {

 
 @Override
 protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_main);
  
 }
 
 public void buttonClicked(View v){
  if(v.getId() == R.id.btnSend){
   Intent serviceIntent = new Intent(this, HelloExtensionService.class);
         serviceIntent.setAction(HelloExtensionService.INTENT_ACTION_ADD);
         EditText etName = (EditText) findViewById(R.id.etTitle);
         EditText etMsg = (EditText) findViewById(R.id.etMessage);
         serviceIntent.putExtra("name", etName.getText().toString());
         serviceIntent.putExtra("message", etMsg.getText().toString());
         startService(serviceIntent);
  }
  else if(v.getId() == R.id.btnClearn){
   Intent serviceIntent = new Intent(this, HelloExtensionService.class);
         serviceIntent.setAction(HelloExtensionService.INTENT_ACTION_CLEAR);
         startService(serviceIntent);
  }
  
 }
}

Step 4: Create HelloExtensionReceiver.java, It extends BroadcastReceiver. It works as bridge between SmartWatch and Host application, It will receive event generated in SmartWatch and forward it to HelloExtensionService.java


package com.kpbird.hellosmartwatch;

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.util.Log;

public class HelloExtensionReceiver  extends BroadcastReceiver{
 
 private String TAG = this.getClass().getSimpleName();
 @Override
 public void onReceive(Context context, Intent intent) {
    Log.i(TAG, "HelloExtensionReceiver onReceiver: " + intent.getAction());
       intent.setClass(context, HelloExtensionService.class);
       context.startService(intent);
 }

}

Step 5: Create HelloRegistrationInformation.java and extends with RegistrationInformation class. As the name suggests, It will use to register host application in Smart Connect. It has six methods that we need to override, among these four methods are used to declare required APIs and two methods used for extension registration and source registration. You can have multiple sources in a host application.


package com.kpbird.hellosmartwatch;

import java.util.ArrayList;
import java.util.List;

import android.content.ContentValues;
import android.content.Context;

import com.sonyericsson.extras.liveware.aef.notification.Notification;
import com.sonyericsson.extras.liveware.aef.registration.Registration;
import com.sonyericsson.extras.liveware.extension.util.ExtensionUtils;
import com.sonyericsson.extras.liveware.extension.util.registration.RegistrationInformation;

public class HelloRegistrationInformation extends RegistrationInformation{
 private Context mContext;
 public HelloRegistrationInformation(Context ctx){
   if (ctx == null) {
             throw new IllegalArgumentException("context == null");
         }
         mContext = ctx;
 }
 @Override
 public int getRequiredNotificationApiVersion() {
  return 1;
 }
 @Override
 public int getRequiredWidgetApiVersion() {
  return 0;
 }

 @Override
 public int getRequiredControlApiVersion() {
  return 0;
 }

 @Override
 public int getRequiredSensorApiVersion() {
  return 0;
 }

 @Override
 public ContentValues getExtensionRegistrationConfiguration() {
    String extensionIcon = ExtensionUtils.getUriString(mContext,R.drawable.ic_launcher);
        String iconHostapp = ExtensionUtils.getUriString(mContext,R.drawable.ic_launcher);
         String extensionIcon48 = ExtensionUtils.getUriString(mContext,R.drawable.ic_launcher_48);

         String configurationText = "Hello SmartWatch";
         String extensionName = "Hello SmartWatch";

         ContentValues values = new ContentValues();
         values.put(Registration.ExtensionColumns.CONFIGURATION_ACTIVITY,MainActivity.class.getName());
         values.put(Registration.ExtensionColumns.CONFIGURATION_TEXT, configurationText);
         values.put(Registration.ExtensionColumns.EXTENSION_ICON_URI, extensionIcon);
         values.put(Registration.ExtensionColumns.EXTENSION_48PX_ICON_URI, extensionIcon48);

         values.put(Registration.ExtensionColumns.EXTENSION_KEY,HelloExtensionService.EXTENSION_KEY);
         values.put(Registration.ExtensionColumns.HOST_APP_ICON_URI, iconHostapp);
         values.put(Registration.ExtensionColumns.NAME, extensionName);
         values.put(Registration.ExtensionColumns.NOTIFICATION_API_VERSION,getRequiredNotificationApiVersion());
         values.put(Registration.ExtensionColumns.PACKAGE_NAME, mContext.getPackageName());
         return values;
 }

 
 
 @Override
 public ContentValues[] getSourceRegistrationConfigurations() {
  
  
   ContentValues sourceValues = null;

         String iconSource1 = ExtensionUtils.getUriString(mContext,R.drawable.ic_launcher_30);
         String iconSource2 = ExtensionUtils.getUriString(mContext,R.drawable.ic_launcher_18);
         String iconBw = ExtensionUtils.getUriString(mContext,R.drawable.ic_launcher_18_bw);
         String textToSpeech = "Notification from Hello SmartWatch Application";
         sourceValues = new ContentValues();
         sourceValues.put(Notification.SourceColumns.ENABLED, true);
         sourceValues.put(Notification.SourceColumns.ICON_URI_1, iconSource1);
         sourceValues.put(Notification.SourceColumns.ICON_URI_2, iconSource2);
         sourceValues.put(Notification.SourceColumns.ICON_URI_BLACK_WHITE, iconBw);
         sourceValues.put(Notification.SourceColumns.UPDATE_TIME, System.currentTimeMillis());
         sourceValues.put(Notification.SourceColumns.NAME, mContext.getString(R.string.app_name));
         sourceValues.put(Notification.SourceColumns.EXTENSION_SPECIFIC_ID, HelloExtensionService.EXTENSION_SPECIFIC_ID);
         sourceValues.put(Notification.SourceColumns.PACKAGE_NAME, mContext.getPackageName());
         sourceValues.put(Notification.SourceColumns.TEXT_TO_SPEECH, textToSpeech);
         sourceValues.put(Notification.SourceColumns.ACTION_1,"Hello");
         sourceValues.put(Notification.SourceColumns.ACTION_ICON_1,ExtensionUtils.getUriString(mContext, R.drawable.ic_launcher));
         
  
  
  List<ContentValues> bulkValues = new ArrayList<ContentValues>();
        bulkValues.add(sourceValues);
        return bulkValues.toArray(new ContentValues[bulkValues.size()]);
 }

}

Step 6: Create HelloExtensionService.java and extends with ExtensionService. It will contain main logic of SmartWatch host application. We need to implement two abstract methods named "getRegistrationInformation()", which sends object of HelloRegistrationInformation class and "keepRunningWhenConnected()",return true if you want to keep ExtensionService running as long as SmartWatch is connected with Smart Phone. For notification example we need to create two methods named "addData" and "clearData". It will generate and clear notification in SmartWatch respectively. We also need to implement onStartCommand to interact with Service.


package com.kpbird.hellosmartwatch;

import android.content.ContentValues;
import android.content.Intent;
import android.database.Cursor;
import android.database.SQLException;
import android.util.Log;
import android.widget.Toast;

import com.sonyericsson.extras.liveware.aef.notification.Notification;
import com.sonyericsson.extras.liveware.aef.registration.Registration;
import com.sonyericsson.extras.liveware.extension.util.ExtensionService;
import com.sonyericsson.extras.liveware.extension.util.ExtensionUtils;
import com.sonyericsson.extras.liveware.extension.util.notification.NotificationUtil;
import com.sonyericsson.extras.liveware.extension.util.registration.DeviceInfoHelper;
import com.sonyericsson.extras.liveware.extension.util.registration.RegistrationInformation;


public class HelloExtensionService extends ExtensionService   {

 public static final String EXTENSION_SPECIFIC_ID = "EXTENSION_SPECIFIC_ID_HELLO_NOTIFICATION";
 public static final String EXTENSION_KEY =  "com.kpbird.hellosmartwatch.key";
 public static final String INTENT_ACTION_ADD = "com.kpbird.hellosmartwatch.action.add";
 public static final String INTENT_ACTION_CLEAR = "com.kpbird.hellosmartwatch.action.clear";

 private String TAG = this.getClass().getSimpleName();
 public HelloExtensionService() {
  super(EXTENSION_KEY);
  Log.i(TAG, "Constructor");
 }
 
 @Override
 public void onCreate() {
  super.onCreate();
  Log.i(TAG, "onCreate()");
 }

 @Override
 public void onDestroy() {
  super.onDestroy();
  Log.i(TAG, "onDestroy()");
 }
 
 @Override
 public int onStartCommand(Intent intent, int flags, int startId) {
  int retVal = super.onStartCommand(intent, flags, startId);
  if (intent != null) {
   if (INTENT_ACTION_CLEAR.equals(intent.getAction())) {
    Log.d(TAG, "onStart action: INTENT_ACTION_CLEAR");
    clearData(intent);
    stopSelfCheck();
   } else if (INTENT_ACTION_ADD.equals(intent.getAction())) {
    Log.d(TAG, "onStart action: INTENT_ACTION_ADD");
    addData(intent);
    stopSelfCheck();
   }
  }

  return retVal;
 }

 @Override
 protected RegistrationInformation getRegistrationInformation() {
  return new HelloRegistrationInformation(this);
 }

 @Override
 protected boolean keepRunningWhenConnected() {
  return false;
 }

 private void clearData(Intent intent) {
   NotificationUtil.deleteAllEvents(this);
 }

 private void addData(Intent intent) {
  String name = "Name";
  String message = "Message";
  if (intent.getExtras().containsKey("name"))
   name = intent.getExtras().getString("name");
  if (intent.getExtras().containsKey("message"))
   message = intent.getExtras().getString("message");

  long time = System.currentTimeMillis();
  long sourceId = NotificationUtil.getSourceId(this,
    EXTENSION_SPECIFIC_ID);
  Log.i(TAG, "Source ID :" + sourceId);
  if (sourceId == NotificationUtil.INVALID_ID) {
   Log.e(TAG, "Failed to insert data");
   return;
  }
  String profileImage = ExtensionUtils.getUriString(this,R.drawable.ic_launcher);

  ContentValues eventValues = new ContentValues();
  eventValues.put(Notification.EventColumns.EVENT_READ_STATUS, false);
  eventValues.put(Notification.EventColumns.DISPLAY_NAME, name);
  eventValues.put(Notification.EventColumns.MESSAGE, message);
  eventValues.put(Notification.EventColumns.PERSONAL, 1);
  eventValues.put(Notification.EventColumns.PROFILE_IMAGE_URI,profileImage);
  eventValues.put(Notification.EventColumns.PUBLISHED_TIME, time);
  eventValues.put(Notification.EventColumns.SOURCE_ID, sourceId);

  try {
   getContentResolver().insert(Notification.Event.URI, eventValues);
  } catch (IllegalArgumentException e) {
   Log.e(TAG, "Failed to insert event", e);
  } catch (SecurityException e) {
   Log.e(TAG,
     "Failed to insert event, is Live Ware Manager installed?",
     e);
  } catch (SQLException e) {
   Log.e(TAG, "Failed to insert event", e);
  }
 }

 @Override
 protected void onViewEvent(Intent intent) {
  String action = intent.getStringExtra(Notification.Intents.EXTRA_ACTION);
  Log.i(TAG, "Action : " + action);
  String hostAppPackageName = intent.getStringExtra(Registration.Intents.EXTRA_AHA_PACKAGE_NAME);
  Log.i(TAG, "HostAppPackageName: " + hostAppPackageName);
  boolean advancedFeaturesSupported = DeviceInfoHelper.isSmartWatch2ApiAndScreenDetected(this, hostAppPackageName);
  Log.i(TAG, "Advanced Features Supported: " + advancedFeaturesSupported);
  int eventId = intent.getIntExtra(Notification.Intents.EXTRA_EVENT_ID,-1);
  try {
   Cursor cursor = getContentResolver().query(Notification.Event.URI,null, Notification.EventColumns._ID + " = " + eventId,null, null);
   if (cursor != null && cursor.moveToFirst()) {
    String name = cursor.getString(cursor.getColumnIndex(Notification.EventColumns.DISPLAY_NAME));
    String message = cursor.getString(cursor.getColumnIndex(Notification.EventColumns.MESSAGE));
    Toast.makeText(this,"Notification: Name: " + name + " Message: " + message,Toast.LENGTH_LONG).show();
    Log.i(TAG, "Name: " + name);
    Log.i(TAG, "Message: "+ message);
   }
   cursor.close();
  } catch (Exception ex) {
   ex.printStackTrace();
  }
 }
 
 @Override
 public void onRegisterResult(boolean success) {
  super.onRegisterResult(success);
   Log.d(TAG, "onRegisterResult :" + success);
 }


}

Step 7: Finally we need to register HelloExtensionReceiver and HelloExtensionService in AndroidManifest.xml like following. We also need to provide user permission "EXTENSION_PERMISSION".


<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.kpbird.hellosmartwatch"
    android:versionCode="1"
    android:versionName="1.0" >

    <uses-sdk
        android:minSdkVersion="16"
        android:targetSdkVersion="16" />
        <uses-permission android:name="com.sonyericsson.extras.liveware.aef.EXTENSION_PERMISSION" />
    

    <application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
        <activity
            android:name="com.kpbird.hellosmartwatch.MainActivity"
            android:label="@string/app_name" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
         <service android:name=".HelloExtensionService" />
        
        <receiver
            android:name=".HelloExtensionReceiver"
            android:permission="com.sonyericsson.extras.liveware.aef.HOSTAPP_PERMISSION" >
            <intent-filter>

                <!-- Generic extension intents. -->
                <action android:name="com.sonyericsson.extras.liveware.aef.registration.EXTENSION_REGISTER_REQUEST" />
                <action android:name="com.sonyericsson.extras.liveware.aef.registration.ACCESSORY_CONNECTION" />
                <action android:name="android.intent.action.LOCALE_CHANGED" />

                <!-- Notification intents -->
                <action android:name="com.sonyericsson.extras.liveware.aef.notification.VIEW_EVENT_DETAIL" />
                <action android:name="com.sonyericsson.extras.liveware.aef.notification.REFRESH_REQUEST" />

                <!-- Widget intents -->
                <action android:name="com.sonyericsson.extras.aef.widget.START_REFRESH_IMAGE_REQUEST" />
                <action android:name="com.sonyericsson.extras.aef.widget.STOP_REFRESH_IMAGE_REQUEST" />
                <action android:name="com.sonyericsson.extras.aef.widget.ONTOUCH" />
                <action android:name="com.sonyericsson.extras.liveware.extension.util.widget.scheduled.refresh" />

                <!-- Control intents -->
                <action android:name="com.sonyericsson.extras.aef.control.START" />
                <action android:name="com.sonyericsson.extras.aef.control.STOP" />
                <action android:name="com.sonyericsson.extras.aef.control.PAUSE" />
                <action android:name="com.sonyericsson.extras.aef.control.RESUME" />
                <action android:name="com.sonyericsson.extras.aef.control.ERROR" />
                <action android:name="com.sonyericsson.extras.aef.control.KEY_EVENT" />
                <action android:name="com.sonyericsson.extras.aef.control.TOUCH_EVENT" />
                <action android:name="com.sonyericsson.extras.aef.control.SWIPE_EVENT" />
            </intent-filter>
        </receiver>
    </application>
</manifest>

Step 8: Connect your SmartPhone and run this example. It will display MainActivity. Before jumping to "Send Notification", open Accessory Emulator and select Extension to verify that our HelloExtension is installed or not. Click on extension menu and it will display the registration details. If extension gets registered successfully then you can go back to Hello SmartWatch application and play with Send and Clear Notification functionality.

Screen Shots


Open Accessory emulator and select SmartWatch 2, Wait for few second if Extensions and Notification API will not enable

Select Extensions

You can see extension detail

Click on "Show Extension Settings" will open this screen because at time of extension registration we provide MainActivity.java as Setting screen

Type Notification title and message and click Send Notification

Go back to Accessory emulator and select Notification API

Select source, This will come from HelloRegistrationInformation's getSourceInformation function, It will display multiple option in the list if you have register multiple sources.

Now, you can see the notification title which we just send

click on menu button ":" will open action menu, you can add maximum 4 action, In our case we have implemented only one action

Click on action menu will call onViewEvent method in HelloExtensionService.java. You can write any action in this method. In our example we just display Toast.

When you click on "Clear Notification" from HelloActivity and come back to emulator, you will see the effect



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



Dec 20, 2013

Sony SmartWatch App Development 2


In previous article "Sony SmartWatch App Development", I explained basics of SmartWatch, configuration of Sony’s Add-On SDK, configuration of SmartWatch emulator and execute simple notification application. This article explains anatomy of SmartWatch development.

Sony’s Add-on SDK is unified SDK, which includes Smart Extension APIs, Small Apps API, IR Remote API and Camera Add-on API among these Smart Extension API is used for SmartWatch and Smart Wireless Handset Pro application development.


Smart Extension API has following subset for different purpose
  • Control API: It is use to render data on device, It supports Grid view, List view, menu, image rendering, touch event and key press. Older version of Control API was supporting only image rendering but current version (2.0) supports XML based layout.
  • Notification API: This can be most useful API among others. It is used to display notification in SmartWatch, which is generated in SmartPhone application. Notification API supports notification title,message, icon and colour.
  • Widget API: It is used to render widget on device.
  • Sensor API: It is used to fetch sensor data from SmartWatch. i.e. light and accelerometer.
  • Registration & Capabilities API: Registration is used to specify how many API subset your application requires ? Like I want to use only Notification API or Sensor API etc.. Capabilities API is used to fetch capability of Smart accessory, here capability means sensor, widget or other functionality, which accessory may have.
SmartWatch Extension API and Development
Source: Sony Smart Extension API documentation
There are three thing involved in SmartWatch application development.

  • Host Application: It handles all interaction with SmartWatch, It is installed as a part of Smart Connect (aka LiveWare). When you install host application from Google Play, you may not find it in launcher screen. It will be available in Smart Connect (LiveWare).
  • Smart Connect: It acts as mediator / manager / framework between Host Application and SmartWatch. SmartWatch connect with Smart Connection application using bluetooth.
  • Smart Extension Apps: It's our normal Android application, which extends to work with SmartWatch. For example Facebook, SMS, Twitter, etc.
Communication between Host Application and Smart Extension Application



Above figure shows, How host application communicate with smart extension application. Smart extension API works on Intent and ContentProvider. In coding sense there can be a single application or Android project which contains both Host application and Smart Extension but there isn't restriction to have single codebase. You can develop two different Android Project as per your requirement. My advice is to use single codebase or single Android project because it will be easier for user,when He/She will download from Google Play and You don't require to manage separate codebase.

Next article will deep dive in to the code.

Dec 14, 2013

Smart Watch App Development

Introduction Smart Watch or Smartwatch


"A smartwatch or smart watch, is a computerized wristwatch with functionality that is enhanced beyond timekeeping, and is often comparable to a personal digital assistant (PDA) device. While early models can perform basic tasks, such as calculations, translations, and game-playing, modern smartwatches are effectively wearable computers. Many smartwatches run mobile apps, while a smaller number of models run a mobile operating system and function as portable media players, offering playback of FM radio, audio, and video files to the user via a Bluetooth headset. Some smartphone models, (also called watch phones) feature full mobile phone capability, and can make or answer phone calls"
-- Wikipedia





SmartWatch's exist since 1980s but it wasn't popular much because of lack in hardware/software technology and wasn't supporting eco-system. Many big companies like Google, Sony, Samsung,etc. launch SmartWatch in 2013. I believe that 2014 will be the "year of the SmartWatch".

Popular SmartWatchs

  • Sony SmartWatch
  • Samsung Galaxy Gear
  • i'm Watch
  • Pebble
  • WiMM Labs (WiMM Labs was acquired by Google in 9-Jan-2013)
Following SmartWatch allows to develop apps


In this article, I will focus on Sony SmartWatch app development. I am using iMac with OSX 10.9 for the development.

Prerequisites
  • JDK
  • Android SDK
  • Sony Add-On SDK
  • Emulator / Android Phone
Setup Development Environment for Sony SmartWatch

Step 1: Install JDK [Ref: http://goo.gl/UUYvez]
Step 2: Install Android SDK [Ref: http://goo.gl/E4nRT3]
Step 3: Download Sony Add-on SDK from this link http://goo.gl/zcKlGD
Step 4: Extract zip file and copy "sony_add-on_sdk_2_1" into <Android SDK>/add-ons/

Android SDK with Sony Add On Folder installed
Setup SmartWatch Emulator

Step 1: You can use any SmartPhone or Android Emulator for SmartWatch Emulator, In my case I am using Sony Xperia P phone. You can use any Android based Smart Phone. 
Step 2: Install Sony's Smart Connect (LiveWare) Application from Google Play


Step 3: Install "acessory_emulator.apk" from <Android SDK>/add-ons/sony_add-on_sdk_2_1/apks

Accessory Emulator and Smart Connect Installed

Compile & Install Sample Application

Step 1: Open Eclipse
Step 2: Import SmartExtensions from <Android SDK>/add-ons/samples/SmartExtensions


Right Click and Select Import
Select "Sony AddOn" folder from Android SDK
Select SmartExtension Projects
SamplePreferenceActivity app and two libraries will display as project

Step 3: Eclipse will display three projects SimplePerferenceActivity,SmartExtensionAPI and SmartExtensionUtils. SimplePerferenceActivity is our main sample application, SmartExtensionAPI and SmartExtensionUtils are library which requires to develop SmartWatch application.

Step 4: Compile & Install "SimplePerferenceActivity" in your smartphone.

Step 5: Open "Accessory Emulator" application from smartphone. It will display dialogbox with different options. Select "SmartWatch"

Step 6: Accessory Emulator will display different options but only Extensions and Widget API will be active.

Step 7: Select Widget API, It will display Sony SmartWatch Emulator with Sample Widget application.


Open Accessory Emulator, Select "SmartWatch"
Enable options based on application, Select "Widget API"
Sample Widget 

References