2013年8月19日月曜日

AndroiderのためのEclipse設定メモ(2013. 夏)

今更感溢れますが。。
最近、android.importorderのことを知ったので... メモ。

android.importorderは、Organize Importしたときの各import文の並び順のルールが記載されている。

公式ドキュメント

ここに載ってました。
こんなドキュメントあったんですね...
Using Eclipse | Android Developers
http://source.android.com/source/using-eclipse.html#eclipse-formatting

※このドキュメント、Eclipseのメモリ設定についても書かれていたりします。
Using Eclipse | Android Developers
http://source.android.com/source/using-eclipse.html#increase-eclipses-memory-settings

ダウンロード

GitHubのこちらから取得しました。
platform_development/ide/eclipse at master · android/platform_development · GitHub https://github.com/android/platform_development/tree/master/ide/eclipse


platform_developmentをgit cloneするか
ブラウザから欲しいファイルを開いて
[Raw]ボタンクリック -> [別名で保存] (Chromeの場合)
とかでもいけると思います。


設定

[Preferences] -> [Java] -> [Code Style] -> [Organize Imports]の
[import]ボタンからDLしたandroid.importorderをインポートする。


インポートすると、こんな感じに。


おすすめEclipse設定資料



過去に書いたEclipse設定メモ まとめ

Android開発向けEclipse設定メモ
Eclipse Java ; { の入力
Eclipse Working Set
Android Layout XMLのコーディングを快適にする方法

2013年8月15日木曜日

Google Play アルファ/ベータ版テスト メモ

こちらの投稿を参考にアルファ/ベータ版テストをさわってみたメモ。
スクリーンショット付きで詳しく解説されていて、先人に感謝。

Android - Google playでのベータ版配布機能について - Qiita [キータ]
http://qiita.com/kaa/items/3904845d225d40f9a6ef

Google+コミュニティの作り方

アルファ/ベータ版テストを行うには、「Google Group」 or 「Google+コミュニティ」が必要。
参考サイトではGoogle Groupを使用していたので、Google+コミュニティでも試したみた。

まず、Google+にアクセスしてサイドバーから「コミュニティ」を選択。


「コミュニティを作成」をクリック。


コミュニティの公開範囲を聞かれるので、テストを行う範囲に応じて決める。
今回は限定公開かつ検索結果に表示しないようにする。
(コミュニティ作成後に公開範囲設定の変更は出来ない)

公開範囲についてはこちらの公式ドキュメントの表がわかりやすかった。
Google+ コミュニティを作成する - Google+ ヘルプ https://support.google.com/plus/answer/2872671?hl=ja


適宜、キャッチフレーズやアイコン画像を設定して「完了」クリック。

以上でGoogle+コミュニティの作成は完了です。

コミュニティのURLをGoogle Play Developer Consoleに設定しますので、ブラウザのURL控えておく。
例:https://plus.google.com/communities/<ID>

あとは、テスターのGoogleアカウントをコミュニティに追加してください。

まとめ

・アルファ/ベータ版テストを行うには、アプリを「公開中」の状態にする必要がある。
・APKファイルをアップしてからGoogle Play反映にかかる時間は、製品版を公開する場合と同様と思われる
・「テスターのリストを管理」に設定するGoogle Group/Google+コミュニティのオーナーはGoogle Play開発者アカウントと別のGoogleアカウントでも良い。
・有料アプリは、アルファ/ベータテストユーザーに対しても有料。

その他

・「下書き」の状態だとアプリの削除が可能。
・「下書き」削除後にパッケージ名の再利用は可能。
・「下書き」の状態で、同一パッケージ名のAPK(別アプリ)はアップロードできない。
(別Google Play開発者アカウントからは試してません)

2013年8月11日日曜日

iosched(2013)をビルドしてみる

ioschedとは

Google主催の開発者向けイベント「Google I/O」の公式ガイドアプリです。

Google I/O 2013 - Google Play の Android アプリ
https://play.google.com/store/apps/details?id=com.google.android.apps.iosched&hl=ja

毎年イベント終了後にソースコードが公開されます。
先日、ソースコードが公開されましたので今回はビルドして端末にインストールするところまでやってみます。

環境

・Mac OS X 10.8.4
・Android Studio 0.2.3

ダウンロード

ソースコードはこちらのプロジェクトサイトで公開されています。
iosched - Google I/O App for Android - Google Project Hosting
https://code.google.com/p/iosched/

プロジェクトはGitで管理されていますので
ターミナルから下記のコマンドでプロジェクトを取得します。
git clone https://code.google.com/p/iosched/
※Gitのセットアップ等については割愛

準備

ioschedアプリが使用するライブラリをAndroid SDK Managerでインストールしておきます。

Extras
 Android Support Repository
 Google Repository

インポート

Android Studioを起動して、こちらの画面の[Import Project]を開く。
ioschedプロジェクトルートのsettings.gradleを選択して[OK]ボタンをクリック。
※プロジェクトのディレクトリ名はiosched -> iosched2013にリネームしています。


[OK]ボタンをクリック。


そうするとこんな感じでプロジェクトが開くと思います。


ビルド

一番左の[Sync Project with Gradle Files]をクリックして暫し待つ。


緑三角の[Run]ボタンをクリック。


うまくいくと、こんな画面が立ち上がってきます。
※因みにこのスクリーンショットはAndroid Studioの[Frame Screenshot]機能を使って撮りました。



まとめ

毎年、ソースコードリーディングに挫折していますが...
今年もワントライしてみようと思います。

[補足]
ioschedプロジェクトのルートディレクトリに「BUILDING.txt」というファイル名前でビルド手順書が同梱されています。
こちらも一読されることをお勧めします。

2013年8月3日土曜日

Android開発環境ダウングレードメモ

退路確保のためメモる。

ADT

下記のサイトにまとめられています。感謝。
Bescotteeさん
Android SDKの古いADTのダウンロードURLの一覧を記載
http://andbrowser.com/development/918/old-adt-download-url/

SDK Tools

バージョン(r21.1)の部分は適宜変更してください。

SDK Platform-tools

バージョン(r16.0.2)の部分は適宜変更してください。

※SDK ToolsとSDK Platform-toolsのバージョン組み合わせが分からなくなったときはSDK Toolsのルートディレクトリに居るsource.properties(の末尾)を参照すると良いっぽい。


2013年7月10日水曜日

AndroidでCloud Save(Google Play App State)を使ってみた

Cloud Save(Google Play App State)とは

アプリのデータをGoogleのサーバ上に保存することができます。
・保存できるデータサイズは128KB × 4スロット (最大512KB)
・データフォーマットはバイト配列

公式ドキュメント
Google Play Game Services — Google Developers

今回クライアント側はAndroidですが、iOS/Webアプリからも利用できるようです。
ドキュメントではGoogle Play Game Servicesの括りで紹介されていますが、ゲーム以外でも活用できるんじゃないかと思います。

Androidアプリ向けのDeveloper's Guideはこちら。
Cloud Save in Android ― Google Play Game Services — Google Developers

Cloud Saveサービス側のセットアップ

1. Google API ConsoleにアクセスしてAPI Projectを作成。

2. Servicesから[Google Play App State]を有効化。

3. API Accessから[Create an OAuth 2.0 client ID...]をクリック。

4. [Produce name]を入力して[Next]をクリック。

5. Application typeは[Installed application]を選択。
    Installed application typeは[Android]を選択。
    [Pacage name]/[Signing certificate fingerprint]を入力。
    [Create client ID]をクリック。

そうするとClient IDがこんな感じで生成されますので、このClient IDを後述のAndroidManifest.xmlに設定します。

Androidアプリ側のセットアップ

1. Google Play servicesライブラリ(rev.7)をインストールしてEclipseにインポート
2. アプリから上記ライブラリを参照

サンプルコード

MainActivity.java
package com.example.cloudsave;

import android.app.Activity;
import android.content.Intent;
import android.content.IntentSender;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.EditText;
import android.widget.TextView;

import com.google.android.gms.appstate.AppStateClient;
import com.google.android.gms.appstate.OnStateLoadedListener;
import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.GooglePlayServicesClient.ConnectionCallbacks;
import com.google.android.gms.common.GooglePlayServicesClient.OnConnectionFailedListener;
import com.google.android.gms.common.Scopes;

public class MainActivity extends Activity implements ConnectionCallbacks, OnConnectionFailedListener,
        OnStateLoadedListener, OnClickListener {

    private AppStateClient mAppStateClient;
    private boolean mConnecting;
    private TextView mTextView;
    private EditText mEditText;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        String[] scopes = new String[] {
                Scopes.APP_STATE
        };
        mAppStateClient = new AppStateClient.Builder(this, this, this).setScopes(scopes).create();

        findViewById(R.id.button_update).setOnClickListener(this);
        findViewById(R.id.button_load).setOnClickListener(this);
        mTextView = (TextView) findViewById(R.id.textview);
        mEditText = (EditText) findViewById(R.id.edittext);
    }

    @Override
    protected void onStart() {
        super.onStart();

        if (!mConnecting) {
            mConnecting = true;
            mAppStateClient.connect();
        }
    }

    @Override
    protected void onStop() {
        super.onStop();

        mAppStateClient.disconnect();
    }

    @Override
    public void onConnected(Bundle bundle) {
        mConnecting = false;
        mAppStateClient.loadState(this, 0);
    }

    @Override
    public void onDisconnected() {
    }

    @Override
    public void onConnectionFailed(ConnectionResult connectionResult) {
        if (connectionResult.hasResolution()) {
            try
            {
                mConnecting = true;
                connectionResult.startResolutionForResult(this, 0);
            } catch (IntentSender.SendIntentException e) {
                mAppStateClient.connect();
            }
        } else {
            // TODO:接続失敗時の処理
        }
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);

        if (resultCode == RESULT_OK) {
            mAppStateClient.connect();
        }
    }

    @Override
    public void onStateConflict(int stateKey, String resolvedVersion, byte[] localData, byte[] serverData) {
        mAppStateClient.resolveState(this, 0, resolvedVersion, serverData);
    }

    @Override
    public void onStateLoaded(int statusCode, int stateKey, byte[] data) {
        if (data == null) {
            return;
        }
        mTextView.setText("Cloud Saveサービス上のデータ:" + new String(data));
        mEditText.setText("");
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.button_update:
                String message = mEditText.getText().toString();
                mAppStateClient.updateStateImmediate(this, 0, message.getBytes());
                break;
            case R.id.button_load:
                mAppStateClient.loadState(this, 0);
                break;
            default:
                break;
        }
    }

}
ざっくり解説
【データセーブ】
データのセーブはupdateStateImmediate()で行う。
更新成功するとonStateLoaded()が呼ばれる。
コンフリクト発生時はonStateConflict()が呼ばれるのでresolveState()で解決する。
【データロード】
データのロードはloadState()で行う。
読み込みが完了するとonStateLoaded()が呼ばれる。

このコードではデータのセーブにAppStateClient.updateStateImmediate()を使っていますが、AppStateClient.updateState()を使った方がバッテリー・ネットワークに優しいそうです。

AndroidManifest.xml
    <application>
        <meta-data
            android:name="com.google.android.gms.appstate.APP_ID"
            android:value="<Your Client ID>" />
    </application>
<application>タグの中にClient IDを設定する。
※レイアウトXMLは割愛

動かしてみた

ActivityのonStart()でconnect()を読んでいるので
アプリを起動するとログインを求めるダイアログが出ます。

サンプルアプリの動きは下記の通りです。
[Update State]ボタンでEditTextに入力した内容をCloud Saveに保存
[Load State]ボタンでCloud Saveからデータを読み込みTextViewに表示

2台の端末からデータのセーブを行うケースで、ローカルの情報が古い状態でデータのセーブを行うとコンフリクトが発生します。
サンプルでは、コンフリクト発生時はサーバ側のデータを採用するようにしています。

コンフリクトの解決についての公式ドキュメントはこちらです。#まだ読んでません...
Resolving Cloud Save Conflicts | Android Developers http://developer.android.com/training/cloudsave/conflict-res.html

最後に

API Consoleを見ると、20,000,000 requests/dayとあります。
上限を超過したときってどうなるんでしょう?
ご存知の方がおられましたら教えてください。

2013年6月8日土曜日

Activity遷移時のアニメーションを無効化する方法

やり方

Activityクラスの下記のメソッドを使う。
public void overridePendingTransition (int enterAnim, int exitAnim)
Activity | Android Developers
http://developer.android.com/reference/android/app/Activity.html#overridePendingTransition(int, int)

公式リファレンスに
Parameters
enterAnimA resource ID of the animation resource to use for the incoming activity. Use 0 for no animation.
exitAnimA resource ID of the animation resource to use for the outgoing activity. Use 0 for no animation.
とあるので
引数enterAnim/exitAnimに0を指定すると、Activity遷移時のアニメーションを無効化できる。

コード

MainActivityのButtonからSubActivityを呼び出す構成。
SubActivityからBackキーで戻って来る時のアニメーションも無効化するため
SubActivityのfinish()をオーバーライドしてoverridePendingTransition()を呼んでいる。

MainActivity.java
public class MainActivity extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        findViewById(R.id.button).setOnClickListener(new View.OnClickListener() {

            @Override
            public void onClick(View v) {
                startActivity(new Intent(MainActivity.this, SubActivity.class));
                overridePendingTransition(0, 0);
            }
        });
    }

}

SubActivity.java
public class SubActivity extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_sub);
    }

    @Override
    public void finish() {
        super.finish();

        overridePendingTransition(0, 0);
    }

}
※Layout XMLは割愛

まとめ

今回のやり方は、下記の動画で知りました。

この動画のサンプルコードはChet HaaseさんのGoogle+のリンクからダウンロードできます。
https://plus.google.com/104755487586666698979/posts/i38BBQnjoFU

この他にも有用な動画があるので、Android Developers LiveのDEV.BYTESシリーズおすすめです。
DevBytes - YouTube
http://www.youtube.com/playlist?list=PLWz5rJ2EKKc_XOgcRukSoKKjewFJZrKV0

2013年5月24日金曜日

Android Live Wallpaper Thumbnail Image Guideline(オレオレ版)

ライブ壁紙一覧に表示されるサムネイルイメージの話。


サムネイルイメージのサイズをいくつにすればジャストサイズになるんだろう?
Launcher Iconみたいにガイドラインがあるのかな?と思い
Android Developersのサイトで検索するも見つからず。

ということで、実測によるオレオレGuidelineを作ったメモ。

測定方法

UI Automatorを使用。

ライブ壁紙一覧を表示した状態でDDMSパースペクティブから
上記の[Dump View Hierarchy for UI Automator]ボタンをクリック。
表示されたDumpからサムネイル画像部分のImageViewを選択。
boundsに表示される値からサイズを取得した。

測定に使用した実機およびエミュレータ
ldpi:Emulator
mdpi:Emulator
tvdpi:Nexus 7
hdpi:Nexus S
xhdpi:SH-04E(AQUOS PHONE EX)
xxhdpi:SO-02E(Xperia Z)

Size and Format

フォーマット
Launcher Iconで推奨されている32-bit PNGでよいかと思う。
【参考】
サイズ
ldpi 56 x 56 px
mdpi 75 x 75 px
tvdpi 100 x 100 px
hdpi 113 x 113 px
xhdpi 150 x 150 px
xxhdpi 225 x 225 px

その他

<wallpaper />で指定するandroid:descriptionは表示されない端末がある。
※僕の手持ち端末調べでは、4.1以上はdescriptionが表示されない。