今話題のSNS、MastodonがAPIを公開していたので叩いてみました。
Mastodonとは?
オープンソースで公開されているSNS(ソーシャル・ネットワーキング・サービス)。
見た目はTwitterに似ていますが主な違いとして、
-
ローカルタイムラインというインスタンスの公開設定の投稿が流れてくるタイムラインがある。
-
オープンソースなので誰でも自由にインスタンスが建てられる。
この2つが挙げられます。
インスタンスとはサーバーのことで、日本で有名なインスタンスは以下の3つだと思います。
- mstdn.jp
- pawoo.net
- friends.nico
はじめに
この記事はAndroidアプリ開発初心者による初心者のための解説となります。
私は他言語をいくつか触っていますが今回の開発で初めてJavaを触った程度であり、詳しくは理解していません。
関連書籍を読みつつ1日くらいAndroid Studioを触ってから始めました。
当初はREST APIについてもよくわかってませんでした。
環境
-
Android Studio 2.3.2
-
Android Emulator (Nexus6P API 25)
クライアント登録・アクセストークン取得
まずはじめにクライアントを登録してclient_idとclient_secretを払い出してもらってから、アカウント認証をしてaccess_tokenを取る必要があります。
その手順を一気にやってくれるのがAccess Token Generator for Mastodon API
Formに以下の情報を入力し、
-
Mastodon URL(インスタンスのURL)
-
Client Name(これから作るクライアントの名前)
-
Web site(クライアントのURL)
-
Scopes(権限のことです、[read write follow]がいいでしょう)
[Publish access_token]を押して、アカウント認証ページでログインすると
-
access_token
-
client_id
-
client_secret
上記3つが表示されるのでメモを取っておきましょう。
access_tokenはアカウントとクライアントを結びつけた鍵のようなもので権限が必要なアクセスの際に使います。
※私は最初APIについて全くわからなかったのでこちらを使わせて頂きました。
便利ではありますが作者様がgithubのREADMEで仰っているようにあくまで実験用のものです。
本格的にクライアントを作るのであれば自分で実装するべきでしょう。
(POSTメソッドを理解して投稿もできるようになった今なら実装できそうです)
コード(基本部分)
今回使うクラスをインポートしたり、文字列を宣言したりしましょう。
このあたりはAndroid開発の基礎だと思うのでいろいろな書籍やサイトで紹介されていると思います。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
package jp.glodia.networktest; //プロジェクト名 import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.net.Uri; import android.os.AsyncTask; import android.util.Log; import android.view.View; import android.view.View.OnClickListener; import android.widget.Button; import android.widget.EditText; import android.widget.Spinner; import android.widget.TextView; import java.io.BufferedReader; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; import java.net.HttpURLConnection; import java.net.URL; public class MainActivity extends AppCompatActivity { private final String HOST_MASTODON = "インスタンスのURL"; private final String ACCESS_TOKEN = "アクセストークン"; private final String CLIENT_ID = "クライアントID(今回は使いませんでした)"; private final String CLIENT_SECRET = "クライアントSECRET(今回は使いませんでした)"; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); //--------------------------- //ここに各動作のプログラムを書く //--------------------------- } } |
XMLファイル編集
ボタンやビューを作ります。
テストなのでシンプルに作りました。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 |
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" tools:context="jp.glodia.networktest.MainActivity" //プロジェクト名.アクティビティ android:weightSum="1"> <Button android:id="@+id/userbutton" android:layout_width="match_parent" android:layout_height="50dp" android:text="user" android:textSize="30sp" /> <Spinner android:id="@+id/spinner" android:layout_width="match_parent" android:layout_height="30dp" android:layout_weight="0.06" android:entries="@array/spinner" /> <EditText android:id="@+id/editText" android:layout_width="match_parent" android:layout_height="50dp" android:ems="10" android:hint="post" android:textSize="30sp" /> <Button android:id="@+id/postbutton" android:layout_width="match_parent" android:layout_height="50dp" android:text="post" android:textSize="30sp" /> <TextView android:id="@+id/textView" android:layout_width="match_parent" android:layout_height="200dp" android:layout_weight="0.75" android:textSize="30sp" tools:text="log" /> </LinearLayout> |
Mastodonは投稿ごとに公開設定を変更できます。
スピナーの値の参照先としてapp/res/values/strings.xmlに以下を追記します。
1 2 3 4 5 6 |
<string-array name="spinner"> <item>public</item> //公開 <item>unlisted</item> //未収載 <item>private</item> //非公開 <item>direct</item> //ダイレクト </string-array> |
また、アプリのインターネット接続を許可するためにapp/manifestsを開いて
1 |
<uses-permission android:name="android.permission.INTERNET" /> |
を追記してください。
こんな感じになります。
ユーザー情報の取得
とりあえず簡単そうなログインユーザー情報の取得からやってみました。
-
Methods
- Accounts
Getting the current user:
GET /api/v1/accounts/verify_credentials
Returns the authenticated user’s Account.
APIドキュメンテーションのこの部分です。
アクセストークンに紐付けられたユーザーの情報が返ってきます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 |
Button userbutton = (Button) this.findViewById(R.id.userbutton); userbutton.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { new AsyncTask<String, Void, String>() { //ボタンを押した時に非同期処理を開始します @Override protected String doInBackground(String... params) { final StringBuilder result = new StringBuilder(); Uri.Builder uriBuilder = new Uri.Builder(); //Uri.Builderで要素を入力していく uriBuilder.scheme("https"); uriBuilder.authority(HOST_MASTODON); uriBuilder.path("/api/v1/accounts/verify_credentials"); final String uriStr = uriBuilder.build().toString(); //URIを作成して文字列に try { URL url = new URL(uriStr); //文字列からURLに変換 HttpURLConnection con = null; //HTTP接続の設定を入力していく con = (HttpURLConnection) url.openConnection(); con.setRequestMethod("GET"); con.addRequestProperty("Authorization", "Bearer " + ACCESS_TOKEN); con.setDoInput(true); con.connect(); //HTTP接続 final InputStream in = con.getInputStream(); //情報を受け取り表示するための形式に↓ final InputStreamReader inReader = new InputStreamReader(in); final BufferedReader bufReader = new BufferedReader(inReader); String line = null; while((line = bufReader.readLine()) != null) { result.append(line); } bufReader.close(); inReader.close(); in.close(); } catch(Exception e) { //エラーの時に呼び出される Log.e("button", e.getMessage()); } return result.toString(); //onPostExecuteへreturn } @Override protected void onPostExecute(String result) { //doInBackgroundが終わると呼び出される TextView textView = (TextView) findViewById(R.id.textView); textView.setText(result); //テキストビューに出力 } }.execute(); } }); |
-
AsyncTask
-
Uri.Builder
-
HttpURLConnection
上記3つが重要です。詳しくはリファレンスなどを読むことをオススメします。
また、AndroidアプリはHTTP接続を非同期処理で行う必要があります。
AsyncTaskを継承したクラスか無名クラスの中でHTTP接続を行いましょう。
投稿してみる
大まかなアクセス方法もわかったので投稿してみましょう。
先程の情報取得はGETメソッドでしたが、投稿はPOSTメソッドです。
-
Methods
- Statuses
Posting a new status:
POST /api/v1/statuses
Form data:………
Returns the new Status.
今回のコードではstatusとvisibilityのみを指定して投稿しました。
Optionalの通り、最低限statusは必要です。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 |
Button postbutton = (Button) this.findViewById(R.id.postbutton); postbutton.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { EditText editText = (EditText) findViewById(R.id.editText); final String tootText = editText.getText().toString(); //本文 Spinner spinner = (Spinner) findViewById(R.id.spinner); final String tootvsblty = spinner.getSelectedItem().toString(); //公開設定 new AsyncTask<String, Void, String>() { @Override protected String doInBackground(String... params) { final StringBuffer result = new StringBuffer(); Uri.Builder uriBuilder = new Uri.Builder(); uriBuilder.scheme("https"); uriBuilder.authority(HOST_MASTODON); uriBuilder.path("/api/v1/statuses"); final String uriStr = uriBuilder.build().toString(); try { URL url = new URL(uriStr); HttpURLConnection con = null; con = (HttpURLConnection) url.openConnection(); con.setRequestMethod("POST"); con.addRequestProperty("Authorization", "Bearer " + ACCESS_TOKEN); con.addRequestProperty("Content-Type", "application/json; charset=UTF-8"); //データの形式を指定 con.setDoOutput(true); //defaultではfalseなので必ずtrueにすること con.connect(); final String input = "{\"status\":\"" +tootText+ "\",\"visibility\":\"" +tootvsblty+ "\"}"; //無理やりJSONデータ作成 OutputStream os = con.getOutputStream(); os.write(input.getBytes()); //文字列をバイトに変換 os.flush(); //データ送信 os.close(); final int status = con.getResponseCode(); //レスポンスコードを受け取る if(status == HttpURLConnection.HTTP_OK) { //通信成功 final InputStream in = con.getInputStream(); String encoding = con.getContentEncoding(); if(null == encoding){ encoding = "UTF-8"; } final InputStreamReader inReader = new InputStreamReader(in, encoding); final BufferedReader bufReader = new BufferedReader(inReader); String line = null; while((line = bufReader.readLine()) != null) { result.append(line); } bufReader.close(); inReader.close(); in.close(); } else { //通信失敗 System.out.println(status); } } catch(Exception e) { //エラーの時 Log.e("button", e.getMessage()); } System.out.println("result=" + result.toString()); return result.toString(); } @Override protected void onPostExecute(String result) { TextView textView = (TextView) findViewById(R.id.textView); textView.setText(result); //返ってきた情報をテキストビューで一応見れるように } }.execute(); editText.setText(""); //投稿した後に入力フォームを空にする } }); |
情報取得のときのInputStreamとは逆にOutputStreamを使います。
JSON文字列を作る方法はちゃんとあると思いますが、無理やり文字列で作ってしまいました。
データを送信するときはPOSTの宣言、データ形式の指定、送信情報のバイトエンコードが重要です。
フォームに入力してボタンを押すと投稿できました。
最後に
SNSはAPIを触るのにとても良い教材ですね。
四苦八苦して書いたコードから投稿されたときはとても感動しました。
調べているとmastodon4jというJavaのライブラリがありました。
Androidアプリのサンプルもあるのでそちらを参考にするのも良さそうです。
私もいずれ触ってみたいと思います。
※このページのコードは私が勉強用に継ぎ接ぎで作ったコードですので動作の保証はできません。
お世話になったサイト