AndroidThings & Raspberry Pi3 で猫用扇風機を作る（終）

2017年11月18日、19日に実施された「[東北Tech道場 郡山道場 第8回](https://medium.com/@koriyamadojo/%E6%9D%B1%E5%8C%97tech%E9%81%93%E5%A0%B4-%E9%83%A1%E5%B1%B1%E9%81%93%E5%A0%B4-%E7%AC%AC%EF%BC%93%E6%9C%9F-%E7%AC%AC%EF%BC%98%E5%9B%9E%E9%96%8B%E5%82%AC-d46f5514cfa9)」の作業内容の続きをまとめます。

とりあえず、発表会用のRaspbery pi3側のプログラムは完成しましたが、スマホ側がほとんど手付かずです。最初はXamarinでAndroidとiPhoneの両端末に対応させる予定でしたが、もうそんなことは言っていられません。DorebellにAndroid端末用のソースも入っているので、それを一部修正してスマホでFirebeceの内容を確認するだけにしました。

#1.スマホ側の処理を作成
Doorbellの「companionApp」をそのまま流用しますが、以下の点を変更しました。
##(1) パッケージ名とクラス名の変更
- パッケージ名を変更
- DoorbellEntry　→　CatFansEntry に変更
- DoorbellEntryAdapter　→　CatFansEntryAdapter に変更

##(2) 注釈の表示を削除
Doorbellで行っている注釈のTextViewへの表示部分を削除する。

###(i) CatFansEntryAdapterの修正
TextViewへの表示部分を削除する。なお、同時に時刻の表示を「年/月/日 時:分:秒」形式にする。

```java:CatFansEntryAdapter.java
package xxxxxx.catfans;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.drawable.Drawable;
import android.support.v4.content.ContextCompat;
import android.support.v7.widget.RecyclerView;
import android.util.Base64;
import android.view.View;
import android.widget.ImageView;
import android.widget.TextView;

import com.firebase.ui.database.FirebaseRecyclerAdapter;
import com.google.firebase.database.DatabaseReference;

import java.text.SimpleDateFormat;

/**
 * RecyclerView adapter to populate CatFans entries from Firebase.
 */
public class CatFansEntryAdapter extends FirebaseRecyclerAdapter<CatFansEntry, CatFansEntryAdapter.CatFansEntryViewHolder> {

    /**
     * ViewHolder for each CatFans entry
     */
    public static class CatFansEntryViewHolder extends RecyclerView.ViewHolder {

        public final ImageView image;
        public final TextView time;

        public CatFansEntryViewHolder(View itemView) {
            super(itemView);

            this.image = (ImageView) itemView.findViewById(R.id.imageView1);
            this.time = (TextView) itemView.findViewById(R.id.textView1);
        }
    }

    private Context mApplicationContext;

    public CatFansEntryAdapter(Context context, DatabaseReference ref) {
        super(CatFansEntry.class, R.layout.catfans_entry, CatFansEntryViewHolder.class, ref);

        mApplicationContext = context.getApplicationContext();
    }

    @Override
    protected void populateViewHolder(CatFansEntryViewHolder viewHolder, CatFansEntry model, int position) {
        // Display the timestamp
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
        String tm = sdf.format(model.getTimestamp());
        viewHolder.time.setText(tm);

        // Display the image
        if (model.getImage() != null) {
            // Decode image data encoded by the Cloud Vision library
            byte[] imageBytes = Base64.decode(model.getImage(), Base64.NO_WRAP | Base64.URL_SAFE);
            Bitmap bitmap = BitmapFactory.decodeByteArray(imageBytes, 0, imageBytes.length);
            if (bitmap != null) {
                viewHolder.image.setImageBitmap(bitmap);
            } else {
                Drawable placeholder =
                        ContextCompat.getDrawable(mApplicationContext, R.drawable.ic_image);
                viewHolder.image.setImageDrawable(placeholder);
            }
        }
    }
}
```

###(ii) catfans_entry.xml の修正
以下のように変更して、タイムスパンと画像のみ表示するようにする。

```xml:catfans_entry.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="wrap_content"
        android:paddingTop="@dimen/activity_vertical_margin"
        android:paddingBottom="@dimen/activity_vertical_margin"
        android:gravity="center_horizontal">

    <TextView
            android:id="@+id/textView1"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginLeft="10dp"
            android:layout_weight="1"
            android:textStyle="bold" />

    <ImageView
            android:layout_width="320dp"
            android:layout_height="240dp"
            android:id="@+id/imageView1" />

</LinearLayout>
```

###(iii) 確認画面
スマホで確認すると次のようになる。
![32.png](https://qiita-image-store.s3.amazonaws.com/0/222292/d823653b-876c-0e0d-83af-a1ed68b98e40.png)

#2. ハードの状態
ハードウェアの方も組み立て終わり、出来上がってきました。
##(1) 駆動部分
![34.jpg](https://qiita-image-store.s3.amazonaws.com/0/222292/11e31aa8-42f5-b20b-8572-1c986ef0d36c.jpeg)

##(2) コルクボードで覆ったところ
![35.jpg](https://qiita-image-store.s3.amazonaws.com/0/222292/a996de15-c996-698b-7187-b697d90bbdab.jpeg)

##(3) 扇風機部分を取り付ける
![36.jpg](https://qiita-image-store.s3.amazonaws.com/0/222292/b4e2abcd-47b7-9eb9-44cb-930c329a9470.jpeg)


###(4) 完成
![33.jpg](https://qiita-image-store.s3.amazonaws.com/0/222292/3b95a9d8-c255-8fc0-014c-33a89817a294.jpeg)




#3. 不具合発生
##(1) 「カメラのパーミッションが無い」というエラーに遭遇
システムの方は機能限定ながらも動くようになり、発表会直前で稼働中の動画を撮ろうという段階になって、なんとシステムが動かなくなってしまいました。原因を探ると、「CatFansActivityクラス」の「onCreateメソッド」の以下の部分でreturnしていることがわかりました。

```java:CatFansActivity.java
        // カメラにアクセスするための許可が必要
        if (checkSelfPermission(Manifest.permission.CAMERA)
                != PackageManager.PERMISSION_GRANTED) {
            // 権限を自動付与する際に問題が発生した
            Log.d(TAG, "No permission");
            return;
        }
```

今まで動いていたわけですから、当然、AndroidManifest.xmlには、
`<uses-permission android:name="android.permission.CAMERA" />`
の記述があります。なんでパーミッションが無いと判定されるのか全く分かりません。10回実行して1回くらいは成功するという状態になってしまいました。何度も繰り返し実行して、なんとかプレゼン用の動画を撮って発表会を迎えます。

##(2) 原因究明
このエラーの原因を探るために以下のことを試しました。

- Android Things を再起動する。
- Doorbell を試す。
- Raspbery pi3 を取り換える。
- カメラを取り換える。
- 電源コードを取り換える。
- Android Thingsを最新バージョン（Android Things DP 0.6.0）にバージョンUPする。

しかし、いずれも失敗に終わりました。オリジナルのDorebellでさえパーミッションが無いとエラー落ちするのです。さっぱり原因がわかりません。

##(3) 対処法
でも、このままでは開発を続けられないので、以下のようにコマンドラインで直接Android Thingsにカメラのパーミッションを与えることにしました。

- 一旦、android studioでプロジェクトを実行する。
- 1回目はパーミッションが不許可なので、そのままエラー終了する。
- コマンドプロンプトで以下のコマンドを実行する。

　 `adb shell pm grant <プロジェクト名> android.permission.CAMERA`

- 再度、android studioでプロジェクトを実行する。すると、パーミッションが許可され、カメラが使えるようになる。(なお、プロジェクトをuninstallすると、再度コマンドの入力が必要。)

#4. 今後の作業
東北Tech道場の発表会は終わりましたが、今後、未実装だった温湿による制御と、半端に終わってしまったスマホ側の処理を実装する予定です。

#5. おわりに
初めてのIoTの開発でしたが、Android Thingsは資料も少く、手探り状態での作業となり、なかなか進まず大変でした。本当に拙い記事ではありますが、もし、誰かのお役に立てれば幸いです。