Sony SmartWatchおよびSmartWatch 2のアプリケヌションを䜜成したす

Sony SmartWatchは圓時のかなり興味深いデバむスであり、䜕らかの理由でハブで開発がバむパスされたした もしそうなら-それを修正したしょう 䟋ずしお、オヌディオプレヌダヌを制埡するシンプルなアプリケヌションを開発したす。

この蚘事は、Androidの開発ツヌルを保持する偎を少なくずも最䜎限知っおいる人、および同じ時蚈を芋たり、それらに぀いおのレビュヌを読んだりしお、その機胜を提瀺する人を察象ずしおいたす。 SmartWatchの最初のバヌゞョンず2番目のバヌゞョン甚にすぐに開発したす。



必芁なラむブラリをむンストヌルする


Android SDK Managerを起動し、 [ツヌル]-> [アドオンサむトの管理]に移動したす



[ナヌザヌ定矩サむト ]タブで、SDKから時蚈の䞋にアドレスを远加したす。
dl-developer.sonymobile.com/sdk_manager/Sony-Add-on-SDK.xml

実際、このSDKは時蚈だけでなく、たずえばSmart Headsetなど、Sonyの他のトリッキヌなデバむスもサポヌトしおいたす。しかし、これたでのずころ、私たちは時蚈だけに興味がありたす。



次に、リストに衚瀺される新しいパッケヌゞを遞択しおむンストヌルしたす。



必芁なラむブラリに加えお、むンストヌル埌、必ず[Android SDKディレクトリ] / sdk /アドオン/ addon-sony_add-on_sdk_2_1-sony-16 / samplesフォルダヌを確認しおください 。 絶察にすべおの時蚈機胜を䜿甚する䟋がありたすが、゚リヌトに぀いおのみ説明したす。

゚ミュレヌタヌを芋る


原則ずしお、実際のクロック甚に開発する方がはるかに簡単で䟿利ですが、それでも゚ミュレヌタにはSDKが付属しおいたす。 それを䜿甚するには、 AVD Managerに移動しお、リストに衚瀺されたSonyの新しいデバむスの1぀、たずえばXperia Tを䜜成したす。 䞻なこずは、 SonyアドオンSDKが Targetパラメヌタヌずしお遞択されるこずです。



さお、゚ミュレヌトするためにそのようなデバむスを実行するず、゚ミュレヌトされたデバむス䞊のアプリケヌションのリストで、 アクセサリ゚ミュレヌタを芋぀けるこずができたす



これは必芁な時蚈を゚ミュレヌトしたす䞊蚘のようにだけではありたせん。



プロゞェクト蚈画


さお、今、正確に䜕を開発するのでしょうか あらゆる皮類の挚拶をするのは退屈だず思うので、プレヌダヌを制埡するアプリケヌションを䜜成したしょう。 電話のプレヌダヌ。 これはアクションの適切なスケヌルです。 ;



ラむブラリをIntelliJ IDEAのプロゞェクトに接続したす


私はIntelliJ IDEAを䜿甚しおいるので、その䟋を玹介できたす。 たず、プロゞェクトを䜜成したす。SDKのバヌゞョンずしお、Sonyからオプションを遞択したす。



さらに、䜜業のために、同じサンプルフォルダヌのいく぀かのモゞュヌル、特にSmartExtensions / SmartExtensionAPIおよびSmartExtensions / SmartExtensionUtilsをプロゞェクトに接続したす 。 第二に、理論的には、すべおのコンテンツを最初から接続しお曞き蟌むこずはできたせんが、私たちは力の暗黒面、䟡倀効率、利䟿性、そしおれロから曞きたいずいう願望はすでに私たちにずっお異質です。 すべおが簡単であるため、スポむラヌの䞋の接続自䜓の指瀺を削陀したした。

ラむブラリを接続したす。
[ファむル]-> [プロゞェクト構造 ]に移動し、[ モゞュヌル ]タブで[プラス蚘号]をクリックしお、[ モゞュヌルのむンポヌト]を遞択したす。



SmartExtensionAPIフォルダヌを芋぀けたす。



OKをクリックし、次ぞ->次ぞ->次ぞ、最埌たで、叀き良き時代のように。
次に、远加したモゞュヌルをメむンプロゞェクトに接続したす。





同じ方法でSmartExtensionUtilsを接続したす 。


基本クラスずパラメヌタヌを構成したす


マニフェストから始めたしょう。

AndroidManifest.xml
 <?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.smartwatch_habra_demo"> <uses-sdk android:minSdkVersion="9" android:targetSdkVersion="16"/> <uses-permission android:name="com.sonyericsson.extras.liveware.aef.EXTENSION_PERMISSION" /> <application android:label="-    " android:icon="@drawable/icon"> <activity android:name="DemoConfigActivity" android:label="  " > <intent-filter> <action android:name="android.intent.action.MAIN" /> </intent-filter> </activity> <service android:name="DemoReceiverService" /> <receiver android:name="DemoExtensionReceiver" 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" /> <action android:name="com.sonyericsson.extras.aef.control.OBJECT_CLICK_EVENT" /> <action android:name="com.sonyericsson.extras.aef.control.MENU_ITEM_SELECTED" /> </intent-filter> </receiver> </application> </manifest> 


䜕が起こっおいるかの本質はこれですアプリケヌションから、クロックからむベントを受け取り、それらを凊理サヌビスに枡すクラスを䜜成したす。これにより、意味のあるアクションが生成されたす。 蚭定りィンドりに必芁な唯䞀のアクティビティです。必芁ない堎合は、完党に砎棄できたす。

レシヌバヌクラスは非垞に単玔です。
DemoExtensionReceiver.java
 public class DemoExtensionReceiver extends BroadcastReceiver { @Override public void onReceive(final Context context, final Intent intent) { intent.setClass(context, DemoReceiverService.class); context.startService(intent); } } 


それでは、サヌビス自䜓に移りたしょう。
DemoReceiverService.java
 public class DemoReceiverService extends ExtensionService { public static final String EXTENSION_KEY = "com.smartwatch_habra_demo"; //todo        ,         "." public DemoReceiverService() { super(EXTENSION_KEY); } @Override protected RegistrationInformation getRegistrationInformation() { return new DemoRegistrationInformation(this); } @Override protected boolean keepRunningWhenConnected() {//       return false; } @Override public WidgetExtension createWidgetExtension(String hostAppPackageName) { //   return new DemoWidget(this,hostAppPackageName); } @Override public ControlExtension createControlExtension(String hostAppPackageName) {//    boolean IsSmartWatch2= DeviceInfoHelper.isSmartWatch2ApiAndScreenDetected( this, hostAppPackageName); if (IsSmartWatch2){ return new DemoControl2(this,hostAppPackageName); }else{ return new DemoControl(this,hostAppPackageName); } } } 


楜芳的ですよね キヌポむントはコメントで説明されおいたすが、質問は発生しないようです。 時蚈のメむンアプリケヌションであるWidgetExtensionを凊理しお描画するには、 ControlExtensionが必芁です。これは同じ目的ですが、りィゞェットの堎合です。

ただし、 RegistrationInformationは、いわば時蚈管理プログラムに拡匵機胜を登録するための情報です。
DemoRegistrationInformation.java
 public class DemoRegistrationInformation extends RegistrationInformation { public static final int WIDGET_WIDTH_SMARTWATCH = 128; public static final int WIDGET_HEIGHT_SMARTWATCH = 110; public static final int CONTROL_WIDTH_SMARTWATCH = 128; public static final int CONTROL_HEIGHT_SMARTWATCH = 128; public static final int CONTROL_WIDTH_SMARTWATCH_2 = 220; public static final int CONTROL_HEIGHT_SMARTWATCH_2 = 176; Context mContext; protected DemoRegistrationInformation(Context context) { if (context == null) { throw new IllegalArgumentException("context == null"); } mContext = context; } @Override public ContentValues getExtensionRegistrationConfiguration() { String iconHostapp = ExtensionUtils.getUriString(mContext, R.drawable.icon); ContentValues values = new ContentValues(); values.put(Registration.ExtensionColumns.CONFIGURATION_ACTIVITY,DemoConfigActivity.class.getName()); //,      " ".      -   . values.put(Registration.ExtensionColumns.CONFIGURATION_TEXT," -");//  ,        .      -   . values.put(Registration.ExtensionColumns.NAME, "--");//,         values.put(Registration.ExtensionColumns.EXTENSION_KEY,DemoReceiverService.EXTENSION_KEY); //   values.put(Registration.ExtensionColumns.HOST_APP_ICON_URI, iconHostapp); //      values.put(Registration.ExtensionColumns.EXTENSION_ICON_URI, iconHostapp); //      ,   48x48 values.put(Registration.ExtensionColumns.NOTIFICATION_API_VERSION,getRequiredNotificationApiVersion());//    values.put(Registration.ExtensionColumns.PACKAGE_NAME, mContext.getPackageName()); return values; } @Override public int getRequiredNotificationApiVersion() { //     return 0; } @Override public int getRequiredSensorApiVersion() { //        return 0; } //--------------------------------------------- //      //--------------------------------------------- @Override public boolean isWidgetSizeSupported(final int width, final int height) { return (width == WIDGET_WIDTH_SMARTWATCH && height == WIDGET_HEIGHT_SMARTWATCH); } @Override public int getRequiredWidgetApiVersion() { //    return 1; } //--------------------------------------------- //      //--------------------------------------------- @Override public int getRequiredControlApiVersion() { //    return 1; } @Override public int getTargetControlApiVersion() { //     return 2; } @Override public boolean isDisplaySizeSupported(int width, int height) { return (width == CONTROL_WIDTH_SMARTWATCH_2 && height == CONTROL_HEIGHT_SMARTWATCH_2) || (width == CONTROL_WIDTH_SMARTWATCH && height == CONTROL_HEIGHT_SMARTWATCH); } } 


詳现に滞圚する䟡倀がありたす。 実際、ダりンロヌドしたSony APIは、倚くのSonyデバむス党䜓に共通しおおり、これらのデバむスすべおで同時に実行できるアプリケヌション拡匵機胜を䜜成するこずを誰も気にしたせん。 たたは、遞択したもののみ。

そのため、センサヌ、りィゞェットなどの画面サむズずAPIバヌゞョンを通知する必芁がありたす。 サポヌトする必芁がありたす。 以䞋を指定する必芁がありたす。

さらに、 getExtensionRegistrationConfigurationの䞀連のパラメヌタヌがありたすが、コメントからはすべおが明確です。

メむンプログラムりィンドり


次の点を理解するこずが重芁です。 時蚈の最初のバヌゞョンでのみ、時蚈に画像を送信できたす。 写真 それだけです これ以䞊。 他の方法で描くこずはできたせん。 2番目のバヌゞョンでは、高床なコントロヌラヌが登堎したしたが、最初は䞡方のバヌゞョンをサポヌトするように蚘述しおいるため、むメヌゞのみです。

コンポヌネントのレンダリングなど、レンダリングにレむアりト機胜を䜿甚する堎合-問題ありたせんが、クリックの座暙およびその他の盞互䜜甚は手動で凊理する必芁がありたす。 暗い芋通し...しかし、それにもかかわらず。 これは私たちの写真がどのように芋えるかです



そしお、すべおに答えるコヌドは次のずおりです。
DemoControl.java
 public class DemoControl extends ControlExtension { static final Rect buttonStopPlaySmartWatch = new Rect(43, 42, 85, 88); public DemoControl(Context context, String hostAppPackageName) { super(context, hostAppPackageName); } @Override public void onTouch(final ControlTouchEvent event) {//   if (event.getAction() == Control.Intents.CLICK_TYPE_SHORT) { if (buttonStopPlaySmartWatch.contains(event.getX(), event.getY())){ MusicBackgroundControlWrapper.TogglePausePlay(mContext); } } } @Override public void onSwipe(int direction) {//   if (direction== Control.Intents.SWIPE_DIRECTION_UP){ MusicBackgroundControlWrapper.VolumeUp(mContext); } if (direction==Control.Intents.SWIPE_DIRECTION_DOWN){ MusicBackgroundControlWrapper.VolumeDown(mContext); } if (direction==Control.Intents.SWIPE_DIRECTION_LEFT){ MusicBackgroundControlWrapper.Next(mContext); } if (direction==Control.Intents.SWIPE_DIRECTION_RIGHT){ MusicBackgroundControlWrapper.Prev(mContext); } } @Override public void onResume() {//  Bitmap mPicture = BitmapFactory.decodeResource(mContext.getResources(), R.drawable.control_picture); showBitmap(mPicture); } } 


onSwipeずonTouchのむベントの目的は、プログラムが衚瀺されるたびにonResumeが呌び出されるこずです。たずえば、クロックが䌑止状態から倖れたり、アプリケヌションアむコンが遞択されたりしたす。 原則ずしお、これはアプリケヌションずのほずんどの察話に十分です。

MusicBackgroundControlWrapperは、マルチメディアキヌのキヌストロヌクの゚ミュレヌションを䜿甚しおプレヌダヌを制埡するために蚭蚈された小さな自己蚘述型クラスです。 すべおのプレヌダヌず携垯電話で正垞に機胜するわけではありたせんが、機胜する堎合は倧䞈倫です。 最善の方法を知っおいる堎合Android 2.3以降をサポヌトしおいる-コメントで共有しおください。

MusicBackgroundControlWrapper.java
 public class MusicBackgroundControlWrapper { public static void KeyPressDownAndUp(int key,Context context){ long eventtime = SystemClock.uptimeMillis() - 1; Intent downIntent = new Intent(Intent.ACTION_MEDIA_BUTTON, null); KeyEvent downEvent = new KeyEvent(eventtime, eventtime, KeyEvent.ACTION_DOWN, key, 0); downIntent.putExtra(Intent.EXTRA_KEY_EVENT, downEvent); context.sendOrderedBroadcast(downIntent, null); eventtime++; Intent upIntent = new Intent(Intent.ACTION_MEDIA_BUTTON, null); KeyEvent upEvent = new KeyEvent(eventtime, eventtime, KeyEvent.ACTION_UP, key, 0); upIntent.putExtra(Intent.EXTRA_KEY_EVENT, upEvent); context.sendOrderedBroadcast(upIntent, null); } public static void VolumeUp(Context context){ AudioManager audioManager =(AudioManager)context.getSystemService(Context.AUDIO_SERVICE); int max=audioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC); int current=audioManager.getStreamVolume(AudioManager.STREAM_MUSIC); if (current<max){ current++; } audioManager.setStreamVolume(AudioManager.STREAM_MUSIC, current,0); } public static void VolumeDown(Context context){ AudioManager audioManager =(AudioManager)context.getSystemService(Context.AUDIO_SERVICE); int current=audioManager.getStreamVolume(AudioManager.STREAM_MUSIC); if (current>0){ current--; } audioManager.setStreamVolume(AudioManager.STREAM_MUSIC, current,0); } public static void TogglePausePlay(Context context){ KeyPressDownAndUp(KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE,context); } public static void Next(Context context){ KeyPressDownAndUp(KeyEvent.KEYCODE_MEDIA_NEXT, context); } public static void Prev(Context context){ KeyPressDownAndUp(KeyEvent.KEYCODE_MEDIA_PREVIOUS, context); } } 


時蚈の2番目のバヌゞョンをサポヌトするために、いく぀かの倉曎を加えおDemoControlからDemoControl2を継承し、別の画像をonResumeに転送し、onTouchで他の座暙を確認したす。

DemoControl2.java
 public class DemoControl2 extends DemoControl { static final Rect buttonStopPlaySmartWatch2 = new Rect(59, 52, 167, 122); public DemoControl2(Context context, String hostAppPackageName) { super(context, hostAppPackageName); } @Override public void onTouch(final ControlTouchEvent event) {//   if (event.getAction() == Control.Intents.CLICK_TYPE_SHORT) { if (buttonStopPlaySmartWatch2.contains(event.getX(), event.getY())){ MusicBackgroundControlWrapper.TogglePausePlay(mContext); } } } @Override public void onResume() {//  Bitmap mPicture = BitmapFactory.decodeResource(mContext.getResources(), R.drawable.control_picture2); showBitmap(mPicture); } } 


りィゞェット


りィゞェット。 正芏のりィゞェットは、時蚈の最初のバヌゞョンで92x92ピクセルの解像床を持ち 、原則ずしお2番目のバヌゞョンではサポヌトされおいたせん。 それをより高い解像床 128x110たで に匕き䌞ばすこずはできたすが、スタむルが倱われ、暙準のコントロヌルずディスプレむが閉じたす。

必芁なアクションは1぀だけです。クリックするこずで、メむンアプリケヌションをクロックで起動したす。 これを担圓するクラスも非垞にシンプルで、すべおの詳现はコメントに蚘茉されおいたす。

DemoWidget.java
 public class DemoWidget extends WidgetExtension { public DemoWidget(Context context, String hostAppPackageName) { super(context, hostAppPackageName); } @Override public void onStartRefresh() { //    / . showBitmap(new DemoWidgetImage(mContext).getBitmap()); } @Override public void onStopRefresh() { //    .     ,         . } @Override public void onTouch(final int type, final int x, final int y) { if (!SmartWatchConst.ACTIVE_WIDGET_TOUCH_AREA.contains(x, y)) { //     -    return; } //  (  )     if (type == Widget.Intents.EVENT_TYPE_SHORT_TAP || type==Widget.Intents.EVENT_TYPE_LONG_TAP) { Intent intent = new Intent(Control.Intents.CONTROL_START_REQUEST_INTENT); intent.putExtra(Control.Intents.EXTRA_AEA_PACKAGE_NAME, mContext.getPackageName()); intent.setPackage(mHostAppPackageName); mContext.sendBroadcast(intent, Registration.HOSTAPP_PERMISSION); } } } 


興味深い点がありたすが。 APIに含たれるナヌティリティの䞭には、 レむアりトを個別に画像にレンダリングするりィゞェット専甚のクラスがありたす。 教育目的のためだけに、この機䌚を利甚しないのは眪です。 DemoWidgetImageクラスを䜿甚しおレンダリングしたす。

DemoWidgetImage.java
 public class DemoWidgetImage extends SmartWatchWidgetImage { public DemoWidgetImage(Context context) { super(context); setInnerLayoutResourceId(R.layout.music_widget_image); } @Override protected void applyInnerLayout(LinearLayout innerLayout) { //       -  . . } } 


蚭定りィンドり


さお、ここで最䜎限必芁です。 DemoRegistrationInformationクラスにアクティビティの名前をすでに登録しおいるので、今床は少なくずも䜕かをうたく入力するだけです。 私もコメントしたせん。 ただのコヌド。

DemoConfigActivity.java
 public class DemoConfigActivity extends Activity { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.config); } } 

config.xml
 <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:textAppearance="?android:attr/textAppearanceMedium" android:text=" " android:id="@+id/textView" android:layout_gravity="center_horizontal"/> </LinearLayout> 


Google Playでアプリケヌションを公開する方法


アプリケヌションをアプリケヌションストアの時蚈管理ナヌティリティにするには、Google Playのプログラムの説明テキストに远加する必芁がありたす。

通垞、時蚈を持っおいない人でもアプリケヌションをむンストヌルできたす。 はい、開始し、最小マヌクを蚭定しないでください。 それに慣れおください、これはGoogle Playの䞖界です しかし、評䟡は私たちにずっお重芁ではありたせん。䞖界が少し良くなっおいるこずは私たちにずっお重芁ですよね...

サンプルアプリケヌションで他にできるこず




私たちの仕事の結果






蚘事の䟋の゜ヌスコヌド


github.com/Newbilius/smartwatch_habra_demo

Sonyサむトに盎面した゜ヌス


developer.sonymobile.com/knowledge-base/sony-add-on-sdk

繰り返したすが、時蚈の他の機胜に぀いお質問がある堎合は、 examplesフォルダヌ䞊蚘のフルパスを参照を参照しおください。絶察にすべおのセンサヌず機胜の䜿甚䟋がありたす。 この蚘事の目的は、「クむックスタヌト」ず興味を持たせる機䌚をあなたに䞎えるこずです。

PSこの蚘事で説明されおいる既補のアプリケヌションが必芁であるが、開発する必芁がない堎合、Google Playには既に1぀、さらにはより機胜的なものがありたすが、有料のもの、たったく同じではない、たたは同様の欠点がありたす

PPS 2番目のバヌゞョンの時蚈は手元にないので、それに぀いお曞かれおいるのは、䟋やドキュメントからの情報に加えお、゚ミュレヌタのチェックず、実際にチェックしおいない2番目のバヌゞョンの時蚈だけです。 時蚈の最初のバヌゞョンがあり、すべおが正確で怜蚌されおいたす。

Source: https://habr.com/ru/post/J210898/


All Articles