【THETAプラグイン開発】THETAプラグインでHello World

## はじめに

こんにちは、メロンパン大好きリコーの@meronpanです:melon:
THETA Vのプラグイン開発のハローワールド的なことを[SDK](https://github.com/ricohapi/theta-plugin-sdk)を使って紹介したいと思います。
SDKをインストールする手順は[こちら](https://qiita.com/yokazuya/items/cda20420ee0b1b58e03f)です。
最終的にピカピカ光るTHETAを作ります。
[![](https://img.youtube.com/vi/8iqS-FFAIYg/0.jpg)](https://www.youtube.com/watch?v=8iqS-FFAIYg)
THETAプラグインをご存じない人は[こちら](https://qiita.com/mrtakada/items/8f3bbf8d283c26899473)の記事をご覧ください。
興味を持たれた方は[twitter](https://twitter.com/theta_plugin)のフォローと
		[THETAプラグイン開発コミュニティ(slack)](https://join.slack.com/t/thetadeveloper/shared_invite/enQtNzcxODI2MTk2ODg0LWY5YmU2ZTIxNGYwNzlmNWZjMWMyMWFlMzY3ZmQ3NGU3ZjZiZjI0Njk4MGIxMDU5NDJiMzQ4Y2FkNDEzYmFlNGU)への参加もよろしくおねがいします｡


プラグインとは言いつつも、感覚的にはAndroidアプリを開発するイメージです。
ただし、通常のAndroidスマホと比べて以下のような点が異なり、若干の工夫が必要です。
- 360度の全天球カメラである（２つのカメラが同時に動く）
- 画面が無い
- いくつかのボタンとランプがある
- 4chのマイクがついている  

これらのことから、主にCameraとAudio部分のAPIが追加変更となっています。
SDKにはpluginlibraryが含まれています。こちらは基本的に変更せずそのまま利用して、それ以外の部分を変更していきます。



## 目次

* THETA Vのプラグインについて以下を説明したいと思います。

  * THETAのボタンの基本的な使い方
  * ランプの基本的な使い方
  * 無線ランプの色が変化するプラグインの作成
  * meronpanのつぶやき


## ボタンの基本的な使い方
ここでは、ボタンの基礎学習と使い方の確認をします。


### ボタンの基礎学習
ボタンを利用する上に覚えておきたいことが２つあります。

* ボタンの種類
* ボタン操作の種類

利用する際は、ご確認ください。

#### ボタンの種類

以下のようなボタンの種類があり、対応するキーコード定数を確認しましょう。

|no|ボタン|キーコード定数|
|---|---|---|
|4|シャッターボタン|KeyReceiver.KEYCODE_CAMERA|
|12|無線ボタン|KeyReceiver.KEYCODE_WLAN_ON_OFF|
|13|モードボタン|KeyReceiver.KEYCODE_MEDIA_RECORD|

<img width="317" alt="nameOfCameraParts.png" src="https://qiita-image-store.s3.amazonaws.com/0/265189/4a5a2239-994b-e45d-d792-9c617d882a62.png">




#### ボタン操作の種類

ボタン操作に対し、以下のようなコールバック関数が用意されています。

|ボタン操作|コールバック関数|
|---|---|
|ボタンを押す|onKeyDown|
|ボタンから離す|onKeyUp|
|モードボタンを長押しする|onKeyLongPress|



### ボタンの使い方

基本的には、ボタンの基礎学習で登場したボタンとボタン操作の種類を掛け合わせて利用するイメージです。
では、[SDK](https://github.com/ricohapi/theta-plugin-sdk)を用いて使い方を確認しましょう。
SDKを実行すると最初に呼ばれるのが`MainActivity`ですが、以下ボタンの説明に絞って部分的に紹介します。
メロンパンでも召し上がりながら、ご覧ください:melon:


```java
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
```
`onCreate`は`MainActivity`が実行されると最初の一回だけ呼び出されます。



```java
            @Override
            public void onKeyDown(int keyCode, KeyEvent event) {
                if (keyCode == KeyReceiver.KEYCODE_CAMERA) {
                    /*
                     * To take a static picture, use the takePicture method.
                     * You can receive a fileUrl of the static picture in the callback.
                     */
                    new TakePictureTask(mTakePictureTaskCallback).execute();
                }
            }
```

`onKeyDown`はボタンが押された時に実行されます。
中では押したボタンが`KEYCODE_CAMERA`であればと場合分けをしており、`new TakePictureTask`はシャッターボタンを押した時に実行されます。

```java
            @Override
            public void onKeyUp(int keyCode, KeyEvent event) {
                /**
                 * You can control the LED of the camera.
                 * It is possible to change the way of lighting, the cycle of blinking, the color of light emission.
                 * Light emitting color can be changed only LED3.
                 */
                notificationLedBlink(LedTarget.LED3, LedColor.BLUE, 1000);
            }

            @Override
            public void onKeyLongPress(int keyCode, KeyEvent event) {
                notificationError("");
            }
```
同様に`onKeyUp`はボタンを離した時に実行、 `onKeyLongPress`はボタンを長押しした時に実行されます。
`onKeyDown`の時のように場合分けしていないのでどのボタンを押しても実行されます。


## ランプの基本的な使い方

ここでは、ランプの使い方を確認します。  
結論から言うと以下3点を覚えてください。

* オン、オフ、点滅の３種類の制御ができる。
* 下部表のLED3〜8のランプを制御できる。
* 下部表のLED3のランプは色を変更できる。

|no|各部名称|LED No|
|---|---|---|
|6|無線ランプ|LED3|
|7|撮影モードランプ|(カメラ)LED4, (動画)LED5, (Live)LED6|
|8|動画記録ランプ|LED7|
|9|メモリー警告ランプ|LED8|

<img width="317" alt="nameOfCameraParts.png" src="https://qiita-image-store.s3.amazonaws.com/0/265189/bd1ecf79-6e6f-aec4-1936-d0d433a27473.png">




SDKを使って使い方を確認しましょう。
SDKは[こちら](https://github.com/ricohapi/theta-plugin-sdk)より  
`pluginlibrary`ではランプの制御について以下の関数が用意されています。

色を指定してLED3ランプをオンにする。

```java
    public void notificationLed3Show(@NonNull LedColor ledColor)
```
LED3〜8ランプを選択してランプをオンにする。

```java
    public void notificationLedShow(@NonNull LedTarget ledTarget)
```
ランプを点滅させる。

```java
    public void notificationLedBlink(@NonNull LedTarget ledTarget, LedColor ledColor, int period)
```
ランプをオフにする。

```java

    public void notificationLedHide(@NonNull LedTarget ledTarget)
```


* LedTarget  
    enumで、LED3, LED4, ..., LED8のいずれかを指定します。
 
* LedColor 
    enumで、RED, GREEN, BLUE, CYAN, MAGENTA, YELLOW, WHITEのいずれかを指定します。
    デフォルトは BLUEです。

* period
    点滅制御を利用した時、１サイクルの時間（ms）を250-2000の範囲で指定します。
    250以下の値は250にセットされ、2000以上の値は2000にセットされます。


## 無線ランプの色が変化するプラグインの作成

以上学習してきたことを活かし、無線ランプの色がランダムに変化し、シャッターボタンで停止するプラグインを作りました。
記事の冒頭で紹介した動画です。
以下の２つについて変更を加えています。

* `MainActivity`
* `AndroidManifest.xml`  

### MainActivityの変更

`MainActivity`を以下のように変更しました。

```java
package com.theta360.pluginapplication;

import android.os.Bundle;
import android.view.KeyEvent;
import com.theta360.pluginapplication.task.TakePictureTask;
import com.theta360.pluginapplication.task.TakePictureTask.Callback;
import com.theta360.pluginlibrary.activity.PluginActivity;
import com.theta360.pluginlibrary.callback.KeyCallback;
import com.theta360.pluginlibrary.receiver.KeyReceiver;
import com.theta360.pluginlibrary.values.LedColor;
import com.theta360.pluginlibrary.values.LedTarget;

import java.util.Random;
import java.util.Timer;
import java.util.TimerTask;

public class MainActivity extends PluginActivity {

    //スケジュール時間間隔を定義
    private static final int TIMER_INTERVAL_PERIOD = 250;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        //一定時間間隔でランプの色を更新するために、Timerをセット
        Timer timer = new Timer();

        Random ledColorRand = new Random();
        Random ledTargetRand = new Random();
        Random ledHideRand = new Random();

        //250msごとにrunが実行されるように設定
        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                //ランプの色をランダムで選択
                int ledColorNum = ledColorRand.nextInt(LedColor.values().length);
                //点灯させるランプをランダムで選択
                int ledTargetNum = ledTargetRand.nextInt(LedTarget.values().length);
                //消灯させるランプをランダムで選択
                int ledHideNum = ledHideRand.nextInt(LedTarget.values().length);

                //ランダムで選んだ色で、LED3ランプを点灯させる
                notificationLed3Show(LedColor.values()[ledColorNum]);
                //ランダムで選んだランプを点灯させる
                notificationLedShow(LedTarget.values()[ledTargetNum]);
                //ランダムで選んだランプを消灯させる
                notificationLedHide(LedTarget.values()[ledHideNum]);
            }
        }, 0, TIMER_INTERVAL_PERIOD);


        // Set a callback when a button operation event is acquired.
        setKeyCallback(new KeyCallback() {
            @Override
            public void onKeyDown(int keyCode, KeyEvent event) {
                if (keyCode == KeyReceiver.KEYCODE_CAMERA) {
                    //Timerを終了させることで、一定間隔でランダムに色を点滅させる処理を終了
                    timer.cancel();
                    //ランプを消灯させる
                    notificationLedHide(LedTarget.LED3);
                    notificationLedHide(LedTarget.LED4);
                    notificationLedHide(LedTarget.LED5);
                    notificationLedHide(LedTarget.LED6);
                    notificationLedHide(LedTarget.LED7);
                    notificationLedHide(LedTarget.LED8);
                }
            }

            @Override
            public void onKeyUp(int keyCode, KeyEvent event) {
            }

            @Override
            public void onKeyLongPress(int keyCode, KeyEvent event) {
                //Timerを終了させることで、一定間隔でランダムに色を点滅させる処理を終了
                timer.cancel();
                //ランプを消灯させる
                notificationLedHide(LedTarget.LED3);
                notificationLedHide(LedTarget.LED4);
                notificationLedHide(LedTarget.LED5);
                notificationLedHide(LedTarget.LED6);
                notificationLedHide(LedTarget.LED7);
                notificationLedHide(LedTarget.LED8);
                //正常にプラグインが終了した時の通知
                notificationSuccess();
            }
        });
    }
}

```
細かいところを見ていくと、、、

```java
        //250msごとにrunが実行されるように設定
        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                //ランプの色をランダムで選択
                int ledColorNum = ledColorRand.nextInt(LedColor.values().length);
                //点灯させるランプをランダムで選択
                int ledTargetNum = ledTargetRand.nextInt(LedTarget.values().length);
                //消灯させるランプをランダムで選択
                int ledHideNum = ledHideRand.nextInt(LedTarget.values().length);

                //ランダムで選んだ色で、LED3ランプを点灯させる
                notificationLed3Show(LedColor.values()[ledColorNum]);
                //ランダムで選んだランプを点灯させる
                notificationLedShow(LedTarget.values()[ledTargetNum]);
                //ランダムで選んだランプを消灯させる
                notificationLedHide(LedTarget.values()[ledHideNum]);
            }
        }, 0, TIMER_INTERVAL_PERIOD);
```
`onCreate`内に記載したため、`timer.schedule`は実行後１度だけ呼び出されます。
しかし、中身の`run`は定期的に呼ばれるようになります。
`timer.schedule`で`run`が250ms毎に実行されるようにしました。

```java
            @Override
            public void onKeyDown(int keyCode, KeyEvent event) {
                if (keyCode == KeyReceiver.KEYCODE_CAMERA) {
                    //Timerを終了させることで、一定間隔でランダムに色を点滅させる処理を終了
                    timer.cancel();
                    //ランプを消灯させる
                    notificationLedHide(LedTarget.LED3);
                    notificationLedHide(LedTarget.LED4);
                    notificationLedHide(LedTarget.LED5);
                    notificationLedHide(LedTarget.LED6);
                    notificationLedHide(LedTarget.LED7);
                    notificationLedHide(LedTarget.LED8);
                }
            }
```
もともとは`onKeyDown`の中でカメラ撮影するコードが書かれていましたが、ランプを消灯する処理に書き換えました。
`timer.cancel`で定期的に実行されていた`run`が停止します。
これにより、ランプの更新が止まります。
最後に`notificationLedHide`よりランプをオフ！

```java
            @Override
            public void onKeyLongPress(int keyCode, KeyEvent event) {
                //Timerを終了させることで、一定間隔でランダムに色を点滅させる処理を終了
                timer.cancel();
                //ランプを消灯させる
                notificationLedHide(LedTarget.LED3);
                notificationLedHide(LedTarget.LED4);
                notificationLedHide(LedTarget.LED5);
                notificationLedHide(LedTarget.LED6);
                notificationLedHide(LedTarget.LED7);
                notificationLedHide(LedTarget.LED8);
                //正常にプラグインが終了した時の通知
                notificationSuccess();
            }
```
`onKeyLongPress`はモードボタンを長押しした時に呼ばれます。
`onKeyLongPress`内ではプラグインを終了する処理が実行されます。
`timer.cancel`と`notificationLedHide`は`onKeyDown`と同じ目的の処理です。
最後に正常にプラグインが終了したことを知らせるために`notificationSuccess`を実行します。


### AndroidManifest.xmlの変更

画面を持たないTHETAにおいて以下の工夫をしました。

```xml:AndroidManifest.xml
        <activity android:name=".MainActivity"
            android:screenOrientation="portrait"
            >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
```

画面を固定させるための`android:screenOrientation="portrait"`の記載を加えました。
このコードがないと、THETAを傾ける度に画面が回転したと認識され`onCreate`が実行されてしまいます。 
Androidのライフサイクル上、画面が回転するとアプリが初期化されるためです。

## meronpanのつぶやき

撮影ができず、ランプの点滅を楽しむカメラ、、、いかがでしょうか。
紹介したコードを使って、ボタンの種類、ボタンアクションの種類を変更して、オリジナルのLED点滅プラグインを作るだけでも結構面白いと思います。  
これであなたもTHETAプラグインにメロメロン:melon:


THETAプラグイン開発に興味を持たれた方はぜひパートナープログラムにご登録ください！
なお、登録時に申請したシリアルナンバーのTHETAについてはメーカーサポート対象外になりますので、ご注意ください。 

- パートナープログラムの詳細は[こちら](https://api.ricoh/products/theta-plugin/)
- 登録フォームは[こちら](https://www8.webcas.net/db/pub/ricoh/thetaplugin/create/input)