original source : https://developer.android.com/training/wearables/apps/packaging.html

wear 1.0 의 경우 독립된 wearable app이 만들어 질수 없다. 반드시 mobile app이 같이 만들어져야 한다. wear 2.0 부터 완전히 독립된 app을 만들수 있다. 이경우 target api가 25이상이 되어야 한다. 1.0과 2.0을 같이 지원하는 app을 만드는 경우 target api는 23 이상이 되어야 한다. wearable app, mobile app 둘다 만드는 경우 같은 sign key를 사용해야한다. (1.0에서도 이는 똑같이 적용된다.) 

Planning for the Play Store

app을 google play에 올리는 경우. wearable app만 배포하는 경우는 평상시 보통 mobile app 배포하는 것과 같은 과정으로 하면된다. 단 wearable, mobile app을 둘다 배포하는 경우는 

Multi-APK delivery method 방법을 따라야 한다.

Distribution to Wear 2.0 watches

사용자가 만약 wear 2.0를 사용하며  mobile에서 연결된 wearable app을 가지고 있는 app을 설치하는 경우 watch app이 mobile app embedded 되었거나 따로 play console을 통해 배포된경우든 상관없이 mobile app을 설치할때 wearable app을 설치할수 있게 notification이 나오게 된다.

  • When you update a phone APK with a new embedded watch APK, the user’s watch APK automatically is updated.
  • When you upload a watch APK via the Play Console, you can update your Wear APK independently from the phone APK, and users receive updates via the watch Play Store.
  • For an embedded watch APK, the user’s watch APK is automatically updated when the APK on the phone is updated. In the case of multi-APK, the update behavior depends on the Play Store setting (Auto-update apps). It is strongly discouraged to have both an embedded APK and multi-APK for your app if they are not the same version.

Distribution to Wear 1.x and 2.0 watches

original source 링크 내용 참조

Specifying a version code

버전 코드 작성요령

  • Set the first two digits of the version code to the targetSdkVersion, e.g. 25
  • Set the next three digits to the product version, e.g. 152 for a product version of 1.5.2
  • Set the next two digits to build or release number, e.g. 01
  • Reserve the last two digits for a multi-APK variant, e.g. 00

Support in the Gradle file

wear 1.0 과 2.0을 둘다 이용하는 경우 product flavors 를 gradle 화일에 적용하는 것을 검토하라 . original source 링크를 참조할것

Migrating a Wear 1.0 APK from Embedded to Multi-APK

original source 링크를 참조할것

Setting Up Targeting for a Watch

In your Android manifest file, you must set the uses-feature element to android.hardware.type.watch. Additionally, do not set the required attribute to false. A single APK for Wear and non-Wear devices presently is not supported.

<manifest package="com.example.standalone"
   xmlns:android="http://schemas.android.com/apk/res/android">
   <uses-feature
       android:name="android.hardware.type.watch"/>
   ...
</manifest>

Specifying an App as Standalone

Wear 2.0 requires a meta-data element in the Android manifest file of watch apps, as a child of the <application> element. The name of the meta-data element is com.google.android.wearable.standalone and the value must be true or false.

If not all of your APKs (alpha, beta, and production) that currently are served to users have the above setting, your app will be unavailable when a user searches on a watch paired to an iPhone.

<application>
...
 <meta-data
   android:name="com.google.android.wearable.standalone"
   android:value="true" />
...
</application>

Note: Even if the value is false, the watch app can be installed before the phone app is installed.

If your Wear 2.0 app has an accompanying phone app, use the same package name for your Wear app and that phone app

Using the Play Console

     Uploading and publishing your APK

Embedding a Wear 1.x APK

     

Packaging a Wear 1.x app with Android Studio

      Signing the Wear 1.x app and phone app separately

     

Package a Wear 1.x app manually



Turning off asset compression

ensure that the watch app is not doubly compressed 

original source: https://developer.android.com/training/wearables/apps/voice.html


두종류의 voice action types

  • System-provided  이미 시스템상에서 지정된 voice action
  • App-provided  app에서 지정하거나 특정 app의 activity를 실행하는 경우

Declare System-provided Voice Actions

When users speak the voice action, your app can filter for the intent that is fired to start an activity. If you want to start a service to do something in the background, show an activity as a visual cue and start the service in the activity. Make sure to call finish() when you want to get rid of the visual cue.

<activity android:name="MyNoteActivity">
     <intent-filter>
         <action android:name="android.intent.action.SEND" />
         <category android:name="com.google.android.voicesearch.SELF_NOTE" />
     </intent-filter>
 </activity>

몇몇의 voice intent의 예시들

더 많은 voice intent를 참조하려명 

see Common intents.

Declare App-provided Voice Actions

you can start your apps directly with a “Start MyActivityName” voice action.

<application>
 <activity android:name="StartRunActivity" android:label="MyRunningApp">
     <intent-filter>
         <action android:name="android.intent.action.MAIN" />
         <category android:name="android.intent.category.LAUNCHER" />
     </intent-filter>
 </activity>
</application>

label에 해당하는 내용이 Start 다음에 들어갈 명령어에 해당한다.

Obtaining Free-form Speech Input(사용자로 부터 음성으로 입력값을 받는 방법)

private static final int SPEECH_REQUEST_CODE = 0;

// Create an intent that can start the Speech Recognizer activity
private void displaySpeechRecognizer() {
   Intent intent = new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH);
   intent.putExtra(RecognizerIntent.EXTRA_LANGUAGE_MODEL,
           RecognizerIntent.LANGUAGE_MODEL_FREE_FORM);
// Start the activity, the intent will be populated with the speech text
   startActivityForResult(intent, SPEECH_REQUEST_CODE);
}

// This callback is invoked when the Speech Recognizer returns.
// This is where you process the intent and extract the speech text from the intent.
@Override
protected void onActivityResult(int requestCode, int resultCode,
       Intent data) {
   if (requestCode == SPEECH_REQUEST_CODE && resultCode == RESULT_OK) {
       List<String> results = data.getStringArrayListExtra(
               RecognizerIntent.EXTRA_RESULTS);
       String spokenText = results.get(0);
       // Do something with spokenText
   }
   super.onActivityResult(requestCode, resultCode, data);
}

startActivityForResult() 를 통해 음성으로 입력값을 받을 activity를 실행한다.이때 action은 

ACTION_RECOGNIZE_SPEECH 으로 지정한다. 그리고 그 결과는 

onActivityResult() 에서 받을수 있다.

original source: https://developer.android.com/training/wearables/apps/always-on.html

Keeping Your App Visible

Android Wear devices running Android version 5.1 or higher allow apps to remain in the foreground while saving battery power.

Important: The 27.0.0 version of the Android Support Library enables a new way to support ambient mode that uses the AmbientMode(새로운방버) class rather than the WearableActivity(기존의방법) class. You can decide whether you want to use

Configure your project

기본적으로 ambient상태에서도 app이 작동하게 하기 위해서는 기본적으로 설정작업이 필요하다.

  1. Create or update your project based on the configurations on the Creating and Running a Wearable App page.
  2. Add the WAKE_LOCK permission to the Android Manifest file:
<uses-permission android:name="android.permission.WAKE_LOCK" />

Ambient Mode Using the AmbientMode Class

AmbientMode 클래스 사용의 장점

Note: The AmbientMode.attachAmbientSupport() method attaches a headless fragment to the Activity class or subclass that you provide, and subsequent calls to FragmentManager.getFragments() return a reference to this fragment (which is not intended to be used in any way)

AmbientMode class를 이용한 방법

1. Create a subclass of one of the Activity classes.

2. 

public class MainActivity extends Activity implements AmbientMode.AmbientCallbackProvider {
   …
   @Override
   public AmbientMode.AmbientCallback getAmbientCallback() {
       return new MyAmbientCallback();
   }
   …
}

3. 

This method returns anAmbientMode.AmbientController. The controller allows you to check the ambient state outside of the callbacks

public class MainActivity extends Activity implements AmbientMode.AmbientCallbackProvider {
   …
   /*
    * Declare an ambient mode controller, which will be used by
    * the activity to determine if the current mode is ambient.
    */
   private AmbientMode.AmbientController mAmbientController;
   …
   @Override
   public void onCreate(Bundle savedInstanceState) {
       super.onCreate(savedInstanceState);
       setContentView(R.layout.activity_main);
   ...
       mAmbientController = AmbientMode.attachAmbientSupport(this);
   }
   ...
}

4.

Create an inner class that extends the AmbientCallback class in order to act on ambient events

private class MyAmbientCallback extends AmbientMode.AmbientCallback {
   @Override
   public void onEnterAmbient(Bundle ambientDetails) {
            // Handle entering ambient mode
   }

   @Override
   public void onExitAmbient() {
     // Handle exiting ambient mode
    }

   @Override
   public void onUpdateAmbient() {
     // Update the content
   }
}

Ambient Mode Using the WearableActivity Class

       Create an activity that supports ambient mode

1.Create an activity that extends WearableActivity.

2. In the onCreate() method of your activity, call the setAmbientEnabled() method.

public class MainActivity extends WearableActivity {
@Override
public void onCreate(Bundle savedInstanceState) {
   super.onCreate(savedInstanceState);

   setAmbientEnabled();
   ...
}

       Handle transitions between modes

When the activity switches to ambient mode, the system calls the onEnterAmbient() method in your wearable activity.

예) 

@Override
public void onEnterAmbient(Bundle ambientDetails) {
   super.onEnterAmbient(ambientDetails);

   mStateTextView.setTextColor(Color.WHITE);
   mStateTextView.getPaint().setAntiAlias(false);
}

the activity switches from ambient mode to interactive mode.

예)

@Override
public void onExitAmbient() {
   super.onExitAmbient();

   mStateTextView.setTextColor(Color.GREEN);
   mStateTextView.getPaint().setAntiAlias(true);
}

       Update Content in Ambient Mode

You should strongly consider only overriding the onUpdateAmbient() method to update the screen once a minute in ambient mode. If your app requires more frequent updates, take into consideration that there is a trade-off between battery life and the frequency of updates. To realize battery savings, updates should be no more than once every 10 seconds.

                  Update once a minute (일분에 한번만 업데이트하는 경우)

onUpdateAmbient(), that allows you to update the screen

예)

@Override
public void onUpdateAmbient() {
   super.onUpdateAmbient();
   // Update the content
}

                 

Update more frequently (좀더 자주 업데이트해야 하는 경우)

use an AlarmManager object to wake the processor

사용방법

  1. Prepare the alarm manager.
  2. Set the frequency of the updates.
  3. Schedule the next update when the activity switches to ambient mode or is currently in ambient mode.
  4. Cancel the alarm when the activity switches to interactive mode or the activity is stopped

Note: The alarm manager may create new instances of your activity as they are triggered. To prevent this situation, ensure that your activity is declared with the android:launchMode="singleInstance" parameter in the manifest.

                            1. Prepare the alarm manager

// Action for updating the display in ambient mode, per our custom refresh cycle.
private static final String AMBIENT_UPDATE_ACTION = "com.your.package.action.AMBIENT_UPDATE";

private AlarmManager mAmbientUpdateAlarmManager;
private PendingIntent mAmbientUpdatePendingIntent;
private BroadcastReceiver mAmbientUpdateBroadcastReceiver;

@Override
public void onCreate(Bundle savedInstanceState) {
   super.onCreate(savedInstanceState);

   setAmbientEnabled();

   mAmbientUpdateAlarmManager =
       (AlarmManager) getSystemService(Context.ALARM_SERVICE);

   Intent ambientUpdateIntent = new Intent(AMBIENT_UPDATE_ACTION);

   mAmbientUpdatePendingIntent = PendingIntent.getBroadcast(
       this, 0, ambientUpdateIntent, PendingIntent.FLAG_UPDATE_CURRENT);

   mAmbientUpdateBroadcastReceiver = new BroadcastReceiver() {
       @Override
       public void onReceive(Context context, Intent intent) {
           refreshDisplayAndSetNextUpdate();
       }
   };
   ...
}
@Override
public void onResume() {
   super.onResume();
   IntentFilter filter = new IntentFilter(AMBIENT_UPDATE_ACTION);
   registerReceiver(mAmbientUpdateBroadcastReceiver, filter);
       ...
}

@Override
public void onPause() {
   super.onPause();
   unregisterReceiver(mAmbientUpdateBroadcastReceiver);
   mAmbientUpdateAlarmManager.cancel(mAmbientUpdatePendingIntent);
   ...
}

                             2.

Update screen and schedule data updates

// Milliseconds between waking processor/screen for updates
private static final long AMBIENT_INTERVAL_MS = TimeUnit.SECONDS.toMillis(20);
private void refreshDisplayAndSetNextUpdate() {
   if (isAmbient()) {
       // Implement data retrieval and update the screen for ambient mode
   } else {
       // Implement data retrieval and update the screen for interactive mode
   }
   long timeMs = System.currentTimeMillis();
   // Schedule a new alarm
   if (isAmbient()) {
       // Calculate the next trigger time
       long delayMs = AMBIENT_INTERVAL_MS - (timeMs % AMBIENT_INTERVAL_MS);
       long triggerTimeMs = timeMs + delayMs;
       mAmbientStateAlarmManager.setExact(
           AlarmManager.RTC_WAKEUP,
           triggerTimeMs,
           mAmbientStatePendingIntent);
   } else {
       // Calculate the next trigger time for interactive mode
   }
}

                               3.

Schedule the next alarm

@Override
public void onEnterAmbient(Bundle ambientDetails) {
   super.onEnterAmbient(ambientDetails);
   refreshDisplayAndSetNextUpdate();
}

@Override
public void onUpdateAmbient() {
   super.onUpdateAmbient();
   refreshDisplayAndSetNextUpdate();
}

                                 4.

Cancel the alarm

@Override
public void onExitAmbient() {
   super.onExitAmbient();
   mAmbientStateAlarmManager.cancel(mAmbientUpdatePendingIntent);
}
@Override
public void onDestroy() {
   mAmbientStateAlarmManager.cancel(mAmbientUpdatePendingIntent);
   super.onDestroy();
}

Maintain Backward-compatibility

Android versions prior to 5.1 에서는 위의 방법이 적용되지 않으며 보통 activity처럼 행동한다.

ambient mode 모드가 되는 순간 작동하는 순간 activity는 exit하게 되고 home screen 으로 이동한다.

Android versions prior to 5.1 을 개발하는 앱이 지원하지 않는 경우 

<uses-library android:name="com.google.android.wearable" android:required="true" />

를 manifest에 작성해서 설치를 막는다.

original source:  https://developer.android.com/training/wearables/apps/layouts.html

Create Custom Notifications

ou should create notifications on the phone and let them automatically sync to the wearable.

Note: When creating custom notifications on the watch, you can use the standard notification APIs (API Level 20) instead of the Support Library.

1.Create a layout and set it as the content view for the activity that you want to display.

public void onCreate(Bundle bundle){
   ...
   setContentView(R.layout.notification_activity);
}

 2.in the Android manifest 

For example:

<activity android:name="com.example.MyDisplayActivity"
   android:exported="true"
   android:allowEmbedded="true"
   android:taskAffinity=""
   android:theme="@android:style/Theme.DeviceDefault.Light" />

3.Create a PendingIntent for the activity that you want to display. 

Intent notificationIntent = new Intent(this, NotificationActivity.class);
PendingIntent notificationPendingIntent = PendingIntent.getActivity(
       this, 0, notificationIntent, PendingIntent.FLAG_UPDATE_CURRENT);

4.Build a Notification and call setDisplayIntent() providing the PendingIntent. The system uses this PendingIntent to launch the activity when users view your notification.

5.Issue the notification using the notify() method.

Create Layouts with the Wear UI Library

사용자 정의 layout을 만드는데 유용한 wear 에 특화된 ui library element

  • BoxInsetLayout  A FrameLayout object that’s aware of screen shape and can box its children in the center square of a round screen.
  • ConfirmationActivity  An activity that displays confirmation animations after the user completes an action.
  • AnimationSet  A group of animations that should be played together.
  • CircularProgressLayout  A layout that provides a circular countdown timer around a child view. Typically used as an automatic timer to confirm an operation after a short delay has elapsed.
  • PagerSnapHelper  Implementation of the SnapHelper instance supporting pager style snapping in either vertical or horizontal orientation.
  • ProgressBar  Displays a bar to the user representing how far the operation has progressed; the application can change the amount of progress (modifying the length of the bar) as it moves forward.

original source : https://developer.android.com/training/wearables/apps/standalone-apps.html

Planning Your Apps

Generally, the minimum and target API level for a standalone app, and for Wear 2.0, is level 25. The minimum SDK level can be 23 only if you are using the same APK for Wear 1.0 and 2.0

1.0의 경우는 wearable apk가 mobile apk에 포함되어있다. 2.0의 경우에는 분리되어야 하며 각각 따로 google play에 올려져야한다.

Identifying an app as standalone

watch app이 완전 또는 semi 완전하게 독립된 app인경우. watch app의 manifest 에 아래사항을 기입한다.

<application>
...
 <meta-data
   android:name="com.google.android.wearable.standalone"
   android:value="true" />
...
</application>

Shared Code and Data Storage

  • Code can be shared between a Wear app and a phone app.
  • You can use standard Android storage APIs to store data locally. For example, you can use the SharedPreferences APIs, SQLite, or internal storage

Detecting Your App on Another Device

Your phone app or watch app can use the CapabilityApi to advertise the app’s presence to a paired device. It can do so statically and dynamically. When an app is on a node in a user’s Wear network (i.e., on a phone, paired watch, or in the cloud)

Specifying capability names for detecting your apps

For the app corresponding to each device type (watch or phone), specify a unique string for the capability name in the res/values/wear.xml file(mobile쪽에서 또 watch 쪽에서도 양쪽다).

App detection and opening a URL from a watch

  1. Use the CapabilityApi to check if your phone app is installed on the paired phone. For more information, see the sample.
  2. If your phone app isn’t installed on the phone, use the PhoneDeviceType.getPhoneDeviceType() method to check the type of the phone.
  3. If PhoneDeviceType.DEVICE_TYPE_ANDROID is returned, the phone is an Android phone. Call RemoteIntent.startRemoteActivity() on the Wear device to open the app store on the phone. Use the market URI for your phone app (which may be different from your phone URI). For example, use a market URI such as: market://details?id=com.example.android.wearable.wear.finddevices
  4. If PhoneDeviceType.DEVICE_TYPE_IOS is returned, it means the phone is an iOS phone (with no Play Store available). Open the App Store on the iPhone by calling RemoteIntent.startRemoteActivity() on the Wear device. You can specify your app’s iTunes URL, e.g., https://itunes.apple.com/us/app/yourappname. On an iPhone, from Android Wear, you cannot programmatically determine if your phone app is installed. As a best practice, provide a mechanism to the user (e.g., a button) to manually trigger the opening of the App Store.

Details for detecting the type of paired phone

int phoneDeviceType =
PhoneDeviceType.getPhoneDeviceType(context);

App detection starting from an Android phone

  1. Using the NodeApi, find all watches connected to the user’s phone. For more information, see the sample.
  2. Using the CapabilityApi, check which of the user’s watches have your app installed.
  3. If your app isn’t installed on all of the user’s watches (compare the results from Step 1 with the results from Step 2), allow the user to open the Play Store on the remaining Wear devices from the phone via the RemoteIntent.startRemoteActivity() method. Specifically, use the market URI for the Wear app (which may be different from your phone app’s URI). For example, use a market URI such as: market://details?id=com.example.android.wearable.wear.finddevices