Fabric from Android で写真を Tweet する。


Fabric


Fabric.io

Fabric は Twiter が提供しているツールで、アプリ開発時にアプリケーションをチームへスキームすることが可能です。
また、クラッシュログやアプリの配布に、Twitter ログインなど様々な機能が備わっています。

Fabric Kits

Fabric の中にはとても優れている Framework が存在します。

そんな中、今回お伝えするのは、Fabric の機能のでもある、Twitter Kit で画像データーを Tweet する処理を作ったので、その時の覚書にはなりますが、お伝えしたいと思います。

Twitter Kit

Twitter Kit

主な機能

  • Authentication
  • Use OAuth Echo
  • Request a User's Email Address
  • Access Twitter's REST API
  • Show Tweets
  • Show Timelines
  • Compose Tweets

目立った機能はここら辺です。
それぞれの機能の簡単な説明です。

Authentication

  • Log in with Twitter
    • 標準のログインボタンが用意され簡単にTwitter Log in が実装できる
    • ゲストログインが可能で、アカウントのないユーザーでもTwitterの機能の一部分を利用することができる。

Use OAuth Echo

  • OAuth 処理を簡単に実装できる
    • リクエストへ必要な Auth 情報を利用できる。

Request a User's Email Address

  • Email アドレスの要請

Access Twitter's REST API

  • サポートされている Twitter API を簡単に実装できる
  • 拡張し標準でサポートされていない Twitter API も作成可能

Show Tweets

  • 公式標準の TweetUI を開発者のアプリケーションに簡単に埋め込むことが可能
  • リスト表示にも対応
  • ダークモードに対応
  • デザインのカスタマイズに対応
  • Tweet Action に対応

Show Timelines

  • タイムラインを検索し一覧表示することができる

Compose Tweets

  • Tweet 機能を開発者のアプリケーションに簡単に組み込むことが可能
  • メッセージと画像投稿に対応

Access Twitter’s REST API

今回のメインはこちらになります。
Fabric の機能で REST API が利用可能なので、こちらの機能を使って対応を進めていくことにします。

まず、標準で対応してれば、標準で対応したいと思います。
(対応していれば苦労しなかったんですけどねw)

標準対応している API

  • Statuses (Tweets)
    • mentionsTimeline
    • userTimeline
    • homeTimeline
    • retweetsOfMe
    • show
    • lookup
    • update
    • retweet
    • unretweet
    • destroy
  • Favorites
    • list
    • create
    • destroy
  • Search
    • tweets
  • Lists
    • statuses
  • Collections
    • entries

この一覧が何を示しているかよくわからない人のために!
こちらは、Twitter API の URL Path になり、それぞれ Path ごとで機能が分かれています。
具体的な内容については、REST APIs をご覧ください。

Statuses (Tweet)

ざっくりとですが、Tweet関連の機能がまとめられている API 群です。

POST statuses/update_with_media

POST statuses/update_with_media

Tweet 投稿と一緒にメディアを投稿するためのAPIになります。

今までのうる覚えで使おうと思っていたのですが、Twitter Kit では標準対応されていません。
また、お気付きの方がいると思いますが、こちらの API は deprecated になっていました。
こちらの API は非推奨なので利用するのはやめましょう。

ということは、別の対応方法が存在します。

POST media/upload

POST media/upload

Twitter へメディアを登録するAPIになります。

60分間サーバーに保持してくれる?ので、その間にアップロードした画像を Tweet へ反映させなければいけません。

POST statuses/update

POST statuses/update

Tweet を投稿するAPIになります。

Tweet する際に MediaID を同梱することで、メディア付きな Tweet を投稿することが可能です。

この二つのAPI を利用することで画像投稿が可能になります。

実装編

※実際に実装したソースコードを抜粋しているため、導入する際は、よしなにしてください!

Twitter Login

Twitter ログインを行う。
Twitter ログインページを表示しアカウント情報を入力する画面が表示される。

TwitterCore twitterCore = TwitterCore.getInstance();
twitterCore.logIn(activity, new Callback<TwitterSession>() {
    @Override
    public void success(Result<TwitterSession> result) {
        // ログイン処理に成功
    }

    @Override
    public void failure(TwitterException e) {
        // ログイン処理に失敗
    }
})

Twitter Tweet (POST statuses/update)

Twitte Kit 標準で用意されている、Twitter へ Tweet する処理。
※ユーザーログイン処理がが完了していること!

TwitterSession twitterSession = TwitterCore.getInstance().getSessionManager().getActiveSession();
StatusesService statusesService = new TwitterApiClient(twitterSession).getStatusesService();
statusesService.update("Tweet Message.", null, false, null, null, null, false, false, new Callback<Tweet>() {
    @Override
    public void success(Result<Tweet> result) {
        // 投稿処理成功
    }

    @Override
    public void failure(TwitterException e) {
        // 投稿処理失敗
    }
});

Tweet ですが、こちらの標準だと、メディアを追加することができません。
ということから、こちらの処理を自前実装します。

StatusesService の拡張

標準で用意されている'StatusesService#update'に'media_ids'が対応していませんでしたので、
再定義し自身のinterfaceクラスを定義します。

Twitter Kit の通信処理では、Square の Retrofit を利用しています。

public interface SKNStatusesService {
...
@FormUrlEncoded
@POST("/1.1/statuses/update.json")
void update(
@Field("status") String status,
@Field("in_reply_to_status_id") Long inReplyToStatusId,
@Field("possibly_sensitive") Boolean possiblySensitive,
@Field("lat") Double lat,
@Field("long") Double longDouble,
@Field("place_id") String placeId,
@Field("display_cooridnates") Boolean displayCooridnates,
@Field("trim_user") Boolean trimUser,
@Field("media_ids") String mediaIds,
Callback<Tweet> cb);

TwitterApiClient の拡張

実際に先ほど作った、SKNStatusesService.class を生成する処理を実装します。
SKNStatusesServiceを使うときは必ず、このメソッドを利用します。

public class SKNTwitterApiClient extends TwitterApiClient {
...
public SKNStatusesService getfalStatusesService() {
    return (SKNLStatusesService)this.getService(SKNStatusesService.class);
}

Twitter Tweet with MediaID (POST statuses/update)

#extensions

先ほど作った、SKNStatusesService と SKNTwitterApiClient を使うぐらいで、あとは標準のと変わりありませんね!
案外簡単に拡張することが可能なのがわかります。

TwitterSession twitterSession = TwitterCore.getInstance().getSessionManager().getActiveSession();
SKNStatusesService statusesService = new SKNTwitterApiClient(twitterSession).getfalStatusesService();
statusesService.update("Tweet Message.", null, false, null, null, null, false, false, "media_id_string1,media_id_string2,media_id_string3,media_id_string4", new Callback<Tweet>() {
    @Override
    public void success(Result<Tweet> result) {
        // 投稿処理成功
    }

    @Override
    public void failure(TwitterException e) {
        // 投稿処理失敗
    }
});

ここまですんなりできたでしょうか?
さて、次に問題になってくるのが、MediaIDの取得です。
Android -> 画像 -> TypeFile -> POST -> Twitter -> MediaID
この流れで実際の画像から MediaID を生成し取得する必要があります。

画像を Twitter へ登録する

POST media/upload

残念ながら Twitter Kit では media/upload API に標準で対応していません。
対応していないのなら作るのが仕事!
さて作りましょう!
手順は先ほど statuses/update で対応した内容に +α します。

MediaService 作成

今回の API は画像のバイトデーターを送信するので、マルチパートにします。

public interface SKNMediaService {
...
@Multipart
@POST("/1.1/media/upload.json")
void upload(@Part("media") TypedFile media, Callback<Media> cb);

API Response Media の作成

media/Upload の Response の値を格納するモデルクラスを作成します。

public class Media {
    @SerializedName("media_id")
    public final long mediaId;
    @SerializedName("media_id_string")
    public final String mediaIdString;
    @SerializedName("size")
    public final long size;
    @SerializedName("image")
    public final Media.Image image;

    public Media(long mediaId, String mediaIdString, long size, Image image) {
        this.mediaId = mediaId;
        this.mediaIdString = mediaIdString;
        this.size = size;
        this.image = image;
    }

    public static class Image {
        @SerializedName("w")
        public final long width;
        @SerializedName("h")
        public final long height;
        @SerializedName("image_type")
        public final String imageType;

        public Image(long width, long height, String imageType) {
            this.width = width;
            this.height = height;
            this.imageType = imageType;
        }
    }
}

FALTwitterApiClient の拡張

media/upload API では、Twitter API の HOST 名を変更する必要があります。
すんなり変更できると思ったのですが、今のところ、私がわかっているのは、こちらの方法です。

public class SKNTwitterUploadApiClient {
...
    final ConcurrentHashMap<Class, Object> services;
    final RestAdapter adapter;

    static final String TWITTER_API_HOST_NAME = "https://upload.twitter.com";

    SKNTwitterUploadApiClient(TwitterAuthConfig authConfig, Session session, TwitterApi twitterApi, SSLSocketFactory sslSocketFactory, ExecutorService executorService) {
        if(session == null) {
            throw new IllegalArgumentException("Session must not be null.");
        } else {
            this.services = new ConcurrentHashMap();
            Gson gson = (new GsonBuilder()).registerTypeAdapterFactory(new SafeListAdapter()).registerTypeAdapterFactory(new SafeMapAdapter()).create();
            this.adapter = (new RestAdapter.Builder()).setClient(new AuthenticatedClient(authConfig, session, sslSocketFactory)).setEndpoint(twitterApi.getBaseHostUrl()).setConverter(new GsonConverter(gson)).setExecutors(executorService, new MainThreadExecutor()).build();
        }
    }

    public SKNTwitterUploadApiClient(Session session) {
        this(TwitterCore.getInstance().getAuthConfig(), session, new TwitterApi(TWITTER_API_HOST_NAME), TwitterCore.getInstance().getSSLSocketFactory(), TwitterCore.getInstance().getFabric().getExecutorService());
    }

    public SKNMediaService getMediaService() {
        return (SKNMediaService)this.getService(SKNMediaService.class);
    }

    protected <T> T getService(Class<T> cls) {
        if(!this.services.contains(cls)) {
            this.services.putIfAbsent(cls, this.adapter.create(cls));
        }

        return (T) this.services.get(cls);
    }s

POST media/upload 実装編

※ユーザーログイン処理がが完了していること!

File mediaFile ...
TypedFile typedFile = new TypedFile("multipart/form-data", mediaFfile);
...

TwitterSession twitterSession = TwitterCore.getInstance().getSessionManager().getActiveSession();
SKNMediaService mediaService = new SKNTwitterUploadAPIClient(twitterSession).getMediaService();
mediaService.upload(typedFile, new Callback<Media>() {
    @Override
    public void success(Result<Media> result) {
        // メディア登録完了           
    }

    @Override
    public void failure(TwitterException e) {
        // メディア登録失敗
    }
});

以上長くなってしまいましたが、こちらが、Fabric で 画像を Tweet する処理になります。

まとめ

  • Fabric
  • Retrofit
  • POST statuses/update の拡張
  • POST media/upload の作成
  • TwitterUploadApiClient の拡張
  • Media モデルクラスの作成
  • MediaService の作成

わかると意外に簡単ですが、意外に情報が無く結局自分でコードを読むことで解決することができました。
みなさんの参考になればと思います。