GoogleAppsSriptAPI+Android/NFCで出席をとる

今更感はありますが、AppsScriptAPIとNFC搭載のAndroidを使って、出席確認？アプリ的な物を作ってみたいと思います。
ちなみにですが、AppsScriptもAndroidもNFCも少しかじった程度の知識しかございません！
行きます。

#GASでAPIを作る
まずはGASで関数を書きます。
とりあえず、ただの文字列をreturnするだけの関数です。

```
function getIcCardData() {
  var message = 'おつかれさまです';
  return message;
}
```

作った関数をAPIにし、外部から叩けるようにせねばならない。
以下を参考にさせていただきました。

- GASのExecution APIを使ってGASを外部からぶっ叩く
  - https://qiita.com/soundTricker/items/1bcfc5c9e80d29a7ae4b
- 【GAS】Execution APIを使ってJavaScriptからGASにアクセスする
  - https://qiita.com/ShishidoToru/items/67a7cad6b997e316cb20
- Android Quickstart(公式)
  -  https://developers.google.com/apps-script/api/quickstart/android

認証情報の作成についてですが、
今回の呼び出し元はAndroidなので、クライアントIDの作成時に、アプリケーションの種類でAndroidを選びます。
OAuthクライアントIDで。


![clientID.png](https://qiita-image-store.s3.amazonaws.com/0/67496/5b3b497e-ec53-9cc7-3545-be737ca2d9f0.png)

パッケージ名とSHA-1 署名証明書のフィンガープリントを登録する必要があります。
先に、Androidプロジェクトを作成してしまったほうが良さそうです。

AndroidStudioで、新規Androidプロジェクトを作成します。
上記画像に記載されている通り、作成後のAndroidManifest.xml内のpackage名をコピー、認証情報に貼り付けます。

SHA-1 署名証明書のフィンガープリントですが、こちらも同様に上記の画像の、
コマンドを実行することで、取得可能です。

keytool -exportcert -keystore **path-to-debug-or-production-keystore** -list -v

太字の部分でkeystoreファイルの場所を指定します。
今回はデバッグ用のkeystoreを使用します。
私の場合(Windows環境)、**c:\Users\ユーザー名\\.android\debug.keystore** でした。
正しくコマンドを入力すると、パスワードを求められます。
debug.keystoreのパスワードはデフォルトだと"Android"となっています。表示されたフィンガープリントのSHA1をコピーし、認証情報に貼り付けて、保存します。

ここまでで、Android QuickstartのSTEP3までが完了しています。

#Androidからアクセスしてみる
Android QuickstartのSTEP4~STEP6の対象ファイルを書き換えます。

- Android Quickstart(公式)
  -  https://developers.google.com/apps-script/api/quickstart/android
<br/>

- 対象ファイル
  - build.gradle
  - AndroidManifest.xml
  - MainActivity.java
  
build.gradleファイルですが、
gradleのバージョンが3.0であったため、以下のように変更しています。
dependencies部分のみの変更で大丈夫そうです。

```
dependencies {
    configurations.all {
        resolutionStrategy.force 'com.google.code.findbugs:jsr305:1.3.9'
    }
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    implementation 'com.android.support:appcompat-v7:26.1.0'
    implementation 'com.android.support.constraint:constraint-layout:1.0.2'
    implementation 'com.android.support:design:26.1.0'
    testImplementation 'junit:junit:4.12'
    androidTestImplementation 'com.android.support.test:runner:1.0.1'
    androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.1'
    implementation 'com.google.android.gms:play-services-auth:11.8.0'
    implementation 'pub.devrel:easypermissions:0.3.0'
    implementation('com.google.api-client:google-api-client-android:1.23.0') {
        exclude group: 'org.apache.httpcomponents'
    }
    implementation('com.google.apis:google-api-services-script:v1-rev166-1.23.0') {
        exclude group: 'org.apache.httpcomponents'
    }
}
```

configurations.all {
        resolutionStrategy.force 'com.google.code.findbugs:jsr305:1.3.9'
}
と後ろ4つのimplementationを追加しました。


AndroidManifest.xmlですが、
以下の追加のみでokです。

    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
    <uses-permission android:name="android.permission.GET_ACCOUNTS" />


MainActivity.javaに関してはAndroidQuickStartのコードを全コピーでいけます。
嘘を付きました。
getDataFromApiを少し変更します。

```
 private List<String> getDataFromApi()
                throws IOException, GoogleAuthException {
            // ID of the script to call. Acquire this from the Apps Script editor,
            // under Publish > Deploy as API executable.
            String scriptId = "APIIDを入力！";

            List<String> folderList = new ArrayList<String>();

            // Create an execution request object.
            ExecutionRequest request = new ExecutionRequest()
                    .setFunction("API関数名を入力！");

            // Make the request.
            Operation op =
                    mService.scripts().run(scriptId, request).execute();

            // Print results of request.
            if (op.getError() != null) {
                throw new IOException(getScriptError(op));
            }
            if (op.getResponse() != null &&
                    op.getResponse().get("result") != null) {
                //文字列を返すのでStringへキャストする
                 String data = (String)(op.getResponse().get("result"));
                 folderList.add(data);
            }

            return folderList;
        }
```

APIID、呼び出す関数の指定、加えて今回は文字列を受け取るので、戻り値をStringへキャストしています。

以上で準備完了です。
まだでした。今回のスクリプトはspreadsheetにアクセスして操作しようと思っているので、
spreadsheetに紐づくAppsScriptプロジェクトになっています。
スコープの設定が必要です。
クラス変数のSCOPESを以下に書き換えます。
"https://www.googleapis.com/auth/spreadsheets"

追加が必要なスコープはAppsScriptのスクリプトエディタ→ファイル→プロジェクトのプロパティにて確認可能です。

おそらく完了です。
APKを作成し、とりあえず動かしてみます。

<img src="https://qiita-image-store.s3.amazonaws.com/0/67496/fc855408-7e13-9404-525e-9647abec3389.png" width=50%>

起動した。
CALL GOOGLE APPS SCRIPT APIボタンを押下。

<img src="https://qiita-image-store.s3.amazonaws.com/0/67496/989a68b5-6895-63f7-55f7-4d8b6616c778.png" width=50%>

許可&許可。
認証アカウントを選択。(今回のAppsScriptを作成したアカウント)

<img src="https://qiita-image-store.s3.amazonaws.com/0/67496/fd8b96d5-6d95-6d86-7bbc-4335b78b5138.png" width=50%>

読み込み中。

<img src="https://qiita-image-store.s3.amazonaws.com/0/67496/fe6b07de-bc92-3c09-ba3a-ba4bd1091854.png" width=50%>

文字列が返ってきました。
おつかれさまです。


#NFCでの読み取り部分を実装する
ここを参考にします。

- NFC Basics
  - https://developer.android.com/guide/topics/connectivity/nfc/nfc.html

AndroidManiFest.xmlにNFC関連のパーミッションを追加する。

    <uses-permission android:name="android.permission.NFC" />

実装部分は後々書きます...

動かすまではできました。

カードをタッチして読み込み(結果が既に見えてしまっている...)

 <img src="https://qiita-image-store.s3.amazonaws.com/0/67496/ea31fc0b-2daa-1829-c63e-6fd7fbb78713.png" width=50%>

結果

 <img src="https://qiita-image-store.s3.amazonaws.com/0/67496/50e964b8-f2e9-4edc-fe6f-03f27a1ead93.png" width=50%>
 

スプレッドシートにも書き込み。

![ss.png](https://qiita-image-store.s3.amazonaws.com/0/67496/7cb5c920-aba4-29a7-8284-2b1b0adbc451.png)

照会用データはこんな感じ。

![aaa.png](https://qiita-image-store.s3.amazonaws.com/0/67496/80f93be2-d3ff-c2d2-414c-619248291752.png)

A列にIdmを持っている。

AppsScript側はこんな感じ。

```
var icCardSheetUrl = 'シートURL';

function getIcCardData(idm) {
    var spreadSheet = SpreadsheetApp.openByUrl(icCardSheetUrl);
    var activeSheet = spreadSheet.getSheetByName("icdata");
    var lastRowNum = activeSheet.getLastRow();
    var valueList = activeSheet.getRange(1,1,lastRowNum,2).getValues();
    var map = {};
    for(var i = 0; i < valueList.length; i++){
        var childList = valueList[i];
        map[childList[0]] = childList[1];
    }
    var name = map[String(idm)];
    Logger.log(name);
    writeAttendanceData(name);
    return name;
}

function writeAttendanceData(name){
    var sheet = SpreadsheetApp.openByUrl(icCardSheetUrl);
    var activeSheet = sheet.getSheetByName('attendance');
    var lastRowNum = activeSheet.getLastRow();
    var range = activeSheet.getRange(lastRowNum+1,1,1,2);
    var today = new Date();
    var timeString = today.getHours()+":"+ today.getMinutes()+":"+ today.getSeconds();
    var values = [[name,timeString]];
    range.setValues(values);
}
```

Androidの画面のデザインを勉強すること。