Android Publisherによるストア管理の自動化

技術部開発基盤グループの id:gfx と申します。

Google I/O 2014で発表されたGoogle Play Developer API (Android Publisher)を調べてみました。

Android PublisherはPlayのアプリケーション情報やAPKバイナリの操作を行うAPIで、これがあればリリースエンジニアリングの自動化(Release Engineering as Code)や、アプリの説明文や更新情報のgit管理などができるようになります。リリースエンジニアリングは属人化しがちなので、その作業をコードに落としこむという点で大きな意味を持つAPIだと言えるでしょう。AndroidアプリケーションにおけるRelease Engineering as Codeの実現に一歩近づくことができます。

本エントリでは、Android Publisher API(以下、単に「API」といいます)について簡単に紹介します。

APIでできること

  • APKのアップロードやアプリの説明文、更新情報などを取得・更新ができる
  • IABの設定を行える(このエントリでは未検証)
  • APIリクエストの限界は 200,000 queries per day

サンプルコードについて

詳細はこのエントリでは省きますが、このエントリのコードはGradle+Groovyプロジェクトで試しました。依存ライブラリは以下のとおりです。

dependencies {
    compile 'com.google.apis:google-api-services-androidpublisher:v2-rev2-1.19.0'
    compile 'com.google.api-client:google-api-client:1.19.0'
    compile 'com.google.api-client:google-api-client-gson:1.19.0'
}

APIを使う準備

AndroidPublisher service clientの生成

認証方法は「OAuth2」と「service account」の二種類あります。今回はservice accountで認証しましょう。以下の「Getting Started」に従って、Developer Consoleでservice accountを作ってAPIの設定をしてください。

認証の流れとしては、認証情報を設定したGoogleCredentialを生成し、それをもとにAndroidPublisherを生成します。

認証情報としては、Google API Developer Consoleで生成した Google Play Android Developer-xxxx.json のなかの client_email と、 Google Play Android Developer-xxxx.p12 ファイルが必要です。

// Google Play Android Developer-xxx.json
public static class Account {

    public String private_key_id

    public String private_key

    public String client_email

    public String client_id

    public String type
}

// ...

// create AndroidPublisher instance
def account = new Gson().fromJson(credentialFile(jsonName).text, Account);
assert account.client_id;

def httpTransport = GoogleNetHttpTransport.newTrustedTransport()
def jsonFactory = JacksonFactory.getDefaultInstance()

def credential = new GoogleCredential.Builder()
        .setTransport(httpTransport)
        .setJsonFactory(jsonFactory)
        .setServiceAccountId(account.client_email)
        .setServiceAccountScopes(AndroidPublisherScopes.all())
        .setServiceAccountPrivateKeyFromP12File(credentialFile(p12Name))
        .build()

def publisher = new AndroidPublisher.Builder(httpTransport, jsonFactory,
        credential)
        .build()

このpublisherオブジェクトに対してリクエストを生成することで、Playのアプリケーション情報を操作します。この部分はメソッドにして分離しておくといいですね。

なお、認証情報などのクライアント生成のための情報が足りないとIllegalArgumentExceptionをただ投げるだけなので、これに悩まされたら思い切ってソースを読みましょう。

編集の準備を行う

AndroidPublisherを生成したら、APIを呼ぶために、リクエストのための準備をします。これはどのリクエストでも共通です。

// 操作は特定のpackage名(application ID)に対して行う
def packageName = "com.example.android.app"


// 編集操作の集合であるeditsを作る
// 最終的に `edits.commit()` することで
// editsにリクエストされた操作がコミットされる
def edits = publisher.edits()
def editRequest = edits.insert(packageName, null)
def appEdit = editRequest.execute()

// `appEdit.getId()` を使ってeditsに対して操作する
// ...

これでようやくAPIを呼ぶ準備が整いました。次に、実際にAPIを見てみましょう。

Android Publisher API

Edits.apks

APKを操作します。list()とupload()メソッドを持っています。

まずlist()で情報を取得してみます。取得できるのはversionCodeとapk binaryのSHA1値です:

// list
Apk apk = edits.apks()
        .list(packageName, appEdit.getId())
        .execute()
        .getApks()
        .first()
println("versionCode: ${apk.getVersionCode()}, SHA1: ${apk.getBinary().getSha1()}")

次にupload()ですが、これはpackage名とapkファイルがあればできます。最後にcommitするまで実際の更新は適用されません。なお、不正なファイルをアップロードするとエラーになります:

def mimeType = "application/vnd.android.package-archive"
def apkFile = new FileContent(mimeType,
        new File('./app/build/outputs/apk/app-release.apk'))

Apk apk = edits.apks(
        .upload(packageName, appEdit.getId(), apkFile)
        .execute()

println("uploaded: versionCode: ${apk.getVersionCode()}, sha1: ${apk.getBinary().getSha1()}")

edits.commit(packageName, appEdit.getId())
    .execute()

Edits.apklistings

その他のAPIについても同様に操作できます。たとえば、Edits.apklistingsは言語(ロケール)ごとの更新情報を取得・更新できます。操作にはバージョンコードが必要なので、必要に応じて Edits.apks から入手するとよいでしょう。

edits.apklistings()
        .list(packageName, appEdit.getId(), apk.getVersionCode())
        .execute()
        .getListings()
        .each { ApkListing listing ->
    println("recent changes: ${listing.getRecentChanges()} (${listing.getLanguage()})")
}

同様に、 管理者の連絡先用の Edits.details 、公開状態を操作する Edits.tracks 、タイトルや説明文を操作する Edits.listings などがあります。

さてここまでの前提知識があればオフィシャルのREST APIリファレンスとAndroidPublisherのjavadocを元にAPIを使えるようになると思います。

おまけ:API使用の注意点

API Usage Instructions に書いてあることのまとめです。

  • productionはもちろんのこと、alpha/betaリリースであっても、必要のない限り1日1度以上リリースすべきではない。頻繁なリリースはユーザーに負担をかける。
  • 外注先などの協力会社にアプリの作成や公開・配付を行わせてはいけない。それはGoogle Play Developer API Terms of Serviceに違反している
  • 協力会社にアプリの開発を任せる場合は、開発用アカウントを共有してはいけない。パーミッションを制限したアカウントを作ってそれを使わせること
  • 協力会社には作成したサービスアカウントを与えないことを奨励する