【Smart Device Link】画面とかを作ってみる

今回は、SDLの画面をいろいろ触ってみたいと思います。
似たようなタイミングで、ASCIIさんも[SDLの連載](http://ascii.jp/elem/000/001/747/1747828/index-6.html)をされています。
エミュレータの作成など、かなり詳しく記載されてますので、
こちらもご覧頂けるといろいろ知識が得られるかなと思います。

# SDLの画面について

SDLの画面表示は、Android Autoなどとは異なり、
基本的には、テンプレートを利用することを想定しているようです。
あらかじめ決まった画面構成（画像や、ボタンなど）に対して、情報を設定し動作させます。

開発者側は、SDL端末のデザインを気にすることなく、統一感のあるデザインで機能を提供することができます。
_※良いように言い過ぎな気もします。。_

自由に画面を作成する方法（プロジェクションモード）もありますが、それは別の機会に説明します。


今回は、[Github:SDLのサンプル](https://github.com/tanaka3/SDLDisplaySample)をベースに説明していきます。

### 画面を作ってみる

###### テンプレートの設定

何も設定しない場合は、PredefinedLayout.MEDIAが設定されている状態になります。
特定のテンプレートを使用したい場合は、`SetDisplayLayout.setDisplayLayout`でテンプレートを指定し、`sdlManager.sendRPC`で登録します。

```java:SdlService.java
private void setDisplayGraphicWithTextButtons(){
	SetDisplayLayout setDisplayLayoutRequest = new SetDisplayLayout();

	//GRAPHIC_WITH_TEXT_AND_SOFTBUTTONSのテンプレートを設定
	setDisplayLayoutRequest.setDisplayLayout(PredefinedLayout.GRAPHIC_WITH_TEXTBUTTONS.toString());
	setDisplayLayoutRequest.setOnRPCResponseListener(new OnRPCResponseListener() {
		@Override
		public void onResponse(int correlationId, RPCResponse response) {
			//テンプレートの設定に成功した場合
			if(((SetDisplayLayoutResponse) response).getSuccess()){
				Log.i("SdlService", "Display layout set successfully.");
				//ここで画面の設定処理などを記載します。
			}else{
				Log.i("SdlService", "Display layout request rejected.");
			}
	});

	sdlManager.sendRPC(setDisplayLayoutRequest);
}
```

###### 利用可能なテンプレートは？？
テンプレートは、SDL端末に依存するため、テンプレートを利用する前に利用可能かチェックする必要があります。
利用可能なテンプレートは、以下の方法で取得可能です。

```java:SdlService.java
private void checkTemplateType(){
	Object result = sdlManager.getSystemCapabilityManager().getCapability(SystemCapabilityType.DISPLAY);
	if( result instanceof DisplayCapabilities){
		List<String> templates = ((DisplayCapabilities) result).getTemplatesAvailable();
		Log.i("Templete", templates.toString());
	}
}
```
ちなみに、Manticoreのエミュレータで利用可能なテンプレートは以下の通りです。
※でも、正しく動かなかったりするものもあったり・・。

> [DEFAULT, MEDIA, NON-MEDIA, ONSCREEN_PRESETS, NAV_FULLSCREEN_MAP, NAV_KEYBOARD, GRAPHIC_WITH_TEXT, TEXT_WITH_GRAPHIC, TILES_ONLY, TEXTBUTTONS_ONLY, GRAPHIC_WITH_TILES, TILES_WITH_GRAPHIC, GRAPHIC_WITH_TEXT_AND_SOFTBUTTONS, TEXT_AND_SOFTBUTTONS_WITH_GRAPHIC, GRAPHIC_WITH_TEXTBUTTONS, TEXTBUTTONS_WITH_GRAPHIC, LARGE_GRAPHIC_WITH_SOFTBUTTONS, DOUBLE_GRAPHIC_WITH_SOFTBUTTONS, LARGE_GRAPHIC_ONLY]

参考：[DisplayCapabilitiesクラスの説明](
https://www.smartdevicelink.com/en/docs/android/master/com/smartdevicelink/proxy/rpc/DisplayCapabilities/)
参考：[デザインについて](https://smartdevicelink.com/en/guides/android/displaying-information/designing-the-user-interface/)

###### 画像やテキストを設定してみる
画像や、テキストは、`sdlManager.getScreenManager().beginTransaction`から
`sdlManager.getScreenManager().commit`の間で設定します。
表示可能なテキストや、画像の位置や、サイズなどはテンプレートに依存するため指定することはできません。

画像を指定する場合、FileTypeは、画像の種類に合ったものを使用してください。
また、利用可能な画像もSDL端末に依存するため、実際に利用可能かは、テンプレートと同じくチェックする必要があります。
利用可能な画像は、`DisplayCapabilities.getGraphicSupported`で確認できます。

```java:SdlService.java
					
sdlManager.getScreenManager().beginTransaction();
//テキストを登録する場合
//sdlManager.getScreenManager().setTextField1("Hello, this is MainField1.");
//画像を登録する
SdlArtwork artwork = new SdlArtwork("sample01.png", FileType.GRAPHIC_PNG, R.drawable.sample01, true);
sdlManager.getScreenManager().setPrimaryGraphic(artwork);
sdlManager.getScreenManager().commit(new CompletionListener() {
	@Override
	public void onComplete(boolean success) {
		if (success) {
			Log.i(TAG, "welcome show successful");
		}
	}
});
```
###### ボタンを設定してみる

ボタンは、SoftButtonObjectを`getScreenManager().setSoftButtonObjects`することで設定する事ができます。
テンプレートに応じて複数個のボタンも登録可能です。

SoftButtonObjectは、SoftButtonStateを保持しており、複数個設定する事で、
トグルボタンのように表示を切り替えて利用する事ができます。
ボタンの切り替えは、`SoftButtonObject.transitionToNextState`で可能です。

```java:SdlService.java
//一つ目のボタンの設定
//一つ目のボタンステート
SoftButtonState softButton01State1 = new SoftButtonState("button01_state1", "button01_state1", null);

//Artworkを設定する事でボタンに画像を貼り付けることも可能
SdlArtwork state_artwork = new SdlArtwork("state2.png", FileType.GRAPHIC_PNG, R.drawable.ic_sdl, true);
SoftButtonState softButton01State2 = new SoftButtonState("button01_state2", "button01_state2", state_artwork);

List<SoftButtonState> softButtonStates = Arrays.asList(softButton01State1, softButton01State2);
SoftButtonObject softButtonObject1 = new SoftButtonObject("softButtonObject01", softButtonStates, softButton01State1.getName(), null);

//二つ目のボタン設定
SoftButtonState softButton02State1 = new SoftButtonState("button02_state1", "button02_state1", null);
SoftButtonObject softButtonObject2 = new SoftButtonObject("softButtonObject02",  Collections.singletonList(softButton02State1), softButton02State1.getName(), null);

//二つのボタンを登録する
List<SoftButtonObject> buttons = Arrays.asList(softButtonObject1, softButtonObject2);
sdlManager.getScreenManager().setSoftButtonObjects(buttons);
```

ボタンのイベントは、SoftButtonObjectの生成時、または`setOnEventListener`で登録する事ができます。
onPressは、ボタンが押されたとき呼び出されます。
onEventは、各動作毎に呼び出され、`OnButtonEvent.getButtonEventMode`をチェックする事で、
`BUTTONUP`または、`BUTTONDOWN`を判断する事ができます。

```java:SdlService.java
softButtonObject1.setOnEventListener(new SoftButtonObject.OnEventListener() {
	@Override
	public void onPress(SoftButtonObject softButtonObject, OnButtonPress onButtonPress) {
		softButtonObject.transitionToNextState();
	}

	@Override
	public void onEvent(SoftButtonObject softButtonObject, OnButtonEvent onButtonEvent) {
	}
});
```


今回のサンプルを実際に動かすとこんな感じになります。
button01_state1を動かすとボタンが切り替わります。
button02_state1を動かすと左の画像が切り替わります。
<img width="80%" alt="" src="https://qiita-image-store.s3.amazonaws.com/0/189829/12b8238f-b2ed-9576-882c-50d509bb3489.png">

参考：[テキスト、画像とボタンについて](https://smartdevicelink.com/en/guides/android/displaying-information/text-images-and-buttons/)

# 最後に
ざっくりですが、画面の作り方について説明してみました。
本気で作る場合、SDL依存チェック（テンプレートとか、画像フォーマットとかいろいろ・・）がたくさん入り少し面倒な感じです。
ただ、簡単なデモ用のアプリを作る分には、今回の内容がわかればいろいろ作っていけそうな気がします。

`SdlManagerListener`など基本的な部分を飛ばしていたりするところもありますが、
次回は、自動車からの情報取得をやってみたいと思います。