2011年1月19日水曜日

Androidアプリのバグ報告システム(アプリケーションのアップロード試してみる編)

以下のスタートガイドを参考に、
前回までに作成した開発環境をいじってみることで、なんとなく、
Google App Engine と Python が分かってきた気がします。

スタート ガイド: Python
http://code.google.com/intl/ja/appengine/docs/python/gettingstarted/

スタートガイドでは締めくくりとして、"アプリケーションのアップロード" を実施しています。

これは、ガイド内で作成した helloworld アプリを
Google App Engine サイトにアップロードする手順になっています。

以下に、実際にやってみた際の記録をメモしておきます。

helloworld アプリは C:\Python_Test\helloworld に配置しています。

  1. Google App Engine のサイトにアクセスし、ログイン。

    http://appengine.google.com/

  2. "Create Application" ボタンを押す。


  3. 携帯電話のメールアドレス情報を入力。

    ここでは Softbank の IPhone 用のアドレスを入力しました。
    Vodafone となっているところが泣けます。
    Username の値はメールアドレスの @ の左側部分を入力します。


  4. 入力した携帯電話のメールアドレスに届けられたメールに記載されている Google App Engine Code の数字を確認して入力。


  5. アプリケーション情報を入力。

    Application Identifier と Application Title を入力します。
    将来的には、バグ管理システムにしたいので、Application Title は Bug Tracking System としました。


  6. アプリケーション作成に成功すると最後に以下の画面が表示されます。


  7. アプリケーションのアップロード。

    C:\Python_Test\helloworld\app.yaml にある application: の値を 5. で入力した Application Identifier の文字列に変更します。

    コマンド・プロンプトを起動し、以下を実行します。

    青字が入力。
    > cd C:\Python_Test
    > set HTTPS_PROXY=xxx.yyy.zzz.com:8080
    > appcfg.py update helloworld
    Application: application-id名 ; version: 1.
    Server: appengine.google.com.
    Scanning files on local disk.
    Initiating update.
    2011-01-19 12:59:28,328 WARNING appengine_rpc.py:405 ssl module not found.
    Without the ssl module, the identity of the remote host cannot be verified, and
    connections may NOT be secure. To fix this, please install the ssl module from
    http://pypi.python.org/pypi/ssl .
    To learn more, see http://code.google.com/appengine/kb/general.html#rpcssl .
    Email: Google アカウントのメールアドレス

    Password for Google アカウントのメールアドレス: パスワード
    Cloning 1 static file.
    Cloning 9 application files.
    Uploading 1 files and blobs.
    Uploaded 1 files and blobs
    Precompilation starting.
    Precompilation completed.
    Deploying new version.
    Checking if new version is ready to serve.
    Will check again in 1 seconds.
    Checking if new version is ready to serve.
    Will check again in 2 seconds.
    Checking if new version is ready to serve.
    Will check again in 4 seconds.
    Checking if new version is ready to serve.
    Closing update: new version is ready to start serving.

  8. アクセス確認

    http://<application identifier>.appspot.com/ をブラウザでアクセスし、表示されることを確認します。


そろそろ、目的である android バグ報告システムの構築に取り組めそうな予感です。

2011年1月17日月曜日

Androidアプリのバグ報告システム(Hello, World! 作成編)

バグ報告システムの構築は、まだまだ先になりそうです。。

前回、開発環境として、Python 2.5.4 と Google App Engine 1.4.1 を導入したので、
以下のサイトを参考し、サンプル・アプリとして、Hello, World! を動かしてみます。
Hello, World!
http://code.google.com/intl/ja/appengine/docs/python/gettingstarted/helloworld.html

  1. C:\Python_Test\helloworld フォルダを作成。

  2. C:\Python_Test\helloworld\helloworld.py ファイルを作成し、以下の内容を記述。
    print 'Content-Type: text/plain'
    print ''
    print 'Hello, world!'

  3. C:\Python_Test\helloworld\app.yaml ファイルを作成し、以下の内容を記述。
    application: helloworld
    version: 1
    runtime: python
    api_version: 1
    
    handlers:
    - url: /.*
      script: helloworld.py
    

  4. Web サーバーを起動。
    > cd C:\Program Files\Google\google_appengine
    > dev_appserver.py C:\Python_Test\helloworld

  5. ブラウザで http://localhost:8080/ にアクセス。
    Hello, world! と表示されることを確認。

サイト通りにやれば簡単にできました。

Androidアプリのバグ報告システム(開発環境を構築編)

前回に引き続き、Google App Engine も Python は経験ありませんが、
バグ報告システムの構築に向けて、第一歩、開発環境を構築をしようと思います。

以下のサイトを見ながら作業を進めていきます。
開発環境
http://code.google.com/intl/ja/appengine/docs/python/gettingstarted/devenvironment.html
 
開発環境の構築といっても、以下の2つを実施するだけです。
  1. Python 導入
  2. App Engine SDK 導入

それでは、早速、"1. Python 導入" を行います。

Google のサイト上では、Python 2.5 をダウンロードしろ、とあるんですが、
Python のサイトを見ると、最新版として、Python 2.7.1 と 3.1.3 があります。(2011年1月17日時点)

但し、ここで、2.7.1 を導入したところ、"2. App Engine SDK 導入" 開始時点で、
" Please make sure Python 2.5 is installed." とエラーが表示され、導入ができません。



なので、2.5 の最新版である 2.5.4 (ファイル名は python-2.5.4.msi) を以下からダウンロードし、導入することにします。
Python 2.5.4
http://www.python.org/download/releases/2.5.4/
本当は 2.5.5 が 2.5 系の最新ですが、2.5.5 はソースしかないため、バイナリが提供されている 2.5.4 を使うことにしています。


Python 2.5.4 のインストールは、デフォルトのまま次に進んでいくだけですが、
念のため、画面コピー付きで手順を貼っておきます。

(1)  python-2.5.4.msi を実行。Next を押す。


(2)  導入先は C:\Python27\ (デフォルト値) のままにする。

(3)  全部 (デフォルト) を導入する。

(4)  インストールが開始される。30 秒くらい待つ。

(5)  インストール完了。


次に、 App Engine SDK をインストールします。

以下のサイトより、Google App Engine SDK for Python をダウンロードします。
ダウンロード
http://code.google.com/intl/ja/appengine/downloads.html

2011年1月17日時点の最新 GoogleAppEngine-1.4.1.msi をダウンロードしました。


(1)  GoogleAppEngine-1.4.1.msi を実行。Python 2.5 found. と表示される。

(2) 同意するにチェックを入れて、次へ。

(3) デフォルトのまま次へ。

(4) 次へ。

(5) インストールが開始される。30 秒くらい待つ。

(6) インストール完了。


以上で、開発環境の導入は完了です。

次回は、Hello, World! を動かしてみます。

Androidアプリのバグ報告システム(構築したい動機芽生え編)

愛用させて頂いているアプリ Simeji の作成者さんのサイトで、
Android アプリのバグ報告システムに関する情報が公開されています。

Androidアプリのバグ報告システムを考える
http://www.adamrocker.com/blog/288/bug-report-system-for-android.html

ふむふむ。
アプリ公開に向けて、このバグ報告システムの構築は非常に魅力です!

ぜひ、このシステムを導入したい!

と思ったのですが、
そもそも Google App Engine って名前しか知らない。。
Python も、ストリート・ファイターⅡに出てくるバイソンに名前が似ている、ということくらいしか知らない。。

ということで、以下のサイトを読んだりして、Google App Engine ってそもそも何?なところから調べてみることにしました。
Google App Engine について
http://code.google.com/intl/ja/appengine/docs/whatisgoogleappengine.html

要点としては、以下のような感じでしょうか。
  • 要は Web アプリケーションだ。
  • 実行場所は Google のインフラ上だ。
  • 開発言語は Java か Python (パイソン) だ。
  • Google App Engine は従量課金制だ。但し、ストレージ/CPUなどリソース使用の制限付きで無料で使うことができる。
  • BigTableという非リレーショナルなデータベースが用意されている。
  • 開発時は、SDK に含まれる開発用Webサーバーでテストを行う。

なんとなく、Google App Engine なるものが分かった気がしてきたので、
次は、開発環境構築を行ってみようと思います。

2011年1月12日水曜日

Google マップの ItemizedOverlay でアイコンが表示されない現象について

android developers の以下のサイトですが、

Hello View, Google Map View
http://developer.android.com/resources/tutorials/views/hello-mapview.html


ItemizedOverlay の派生クラスである HelloItemizedOverlay に、
コンストラクタが2つ定義されています。

boundCenterBottom()を使用するコンストラクタ
 public HelloItemizedOverlay(Drawable defaultMarker) {
     super(boundCenterBottom(defaultMarker));
 }
boundCenterBottom()を使用しないコンストラクタ
 public HelloItemizedOverlay(Drawable defaultMarker, Context context) {
     super(defaultMarker);
     mContext = context;
 }

このうち、boundCenterBottom()を使用しないコンストラクタを使用すると、
アイコン(ドロイド君)が表示されません (つω・。)うぅぅ…

HelloItemizedOverlay.java
package jp.kochi.GoogleMapOverlayTest;

import java.util.ArrayList;
import android.app.AlertDialog;
import android.content.Context;
import android.graphics.drawable.Drawable;
import com.google.android.maps.ItemizedOverlay;
import com.google.android.maps.OverlayItem;

public class HelloItemizedOverlay extends ItemizedOverlay<OverlayItem> {

  private ArrayList<OverlayItem> mOverlays = new ArrayList<OverlayItem>();
  private Context mContext;

  public HelloItemizedOverlay(Drawable defaultMarker) {
    super(boundCenterBottom(defaultMarker));
  }

  public HelloItemizedOverlay(Drawable defaultMarker, Context context) {
    super(defaultMarker);
    mContext = context;
  }
  @Override
  protected OverlayItem createItem(int i) {
    return mOverlays.get(i);
  }

  @Override
  public int size() {
    return mOverlays.size();
  }

  public void addOverlay(OverlayItem overlay) {
    mOverlays.add(overlay);
    populate();
  }

  @Override
  protected boolean onTap(int index) {
    OverlayItem item = mOverlays.get(index);
    AlertDialog.Builder dialog = new AlertDialog.Builder(mContext);
    dialog.setTitle(item.getTitle());
    dialog.setMessage(item.getSnippet());
    dialog.show();
    return true;
  }

}


GoogleMapOverlayTestActivity.java
package jp.kochi.GoogleMapOverlayTest;

import java.util.List;
import com.google.android.maps.GeoPoint;
import com.google.android.maps.MapActivity;
import com.google.android.maps.MapView;
import com.google.android.maps.Overlay;
import com.google.android.maps.OverlayItem;
import android.graphics.drawable.Drawable;
import android.os.Bundle;

public class GoogleMapOverlayTestActivity extends MapActivity {
  /** Called when the activity is first created. */
  @Override
  public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);

    MapView mapView = (MapView) findViewById(R.id.mapview);

    // ビルドイン-ズームコントローラー使用
    mapView.setBuiltInZoomControls(true);

    // 航空写真モードに変更
    mapView.setSatellite(true);

    // Droidアイコン表示
    List<Overlay> mapOverlays = mapView.getOverlays();
    Drawable drawable = this.getResources().getDrawable(R.drawable.androidmarker);
    
    // こっちは表示される。
    HelloItemizedOverlay itemizedoverlay = new HelloItemizedOverlay(drawable);

    // こっちだと表示されない。
    // HelloItemizedOverlay itemizedoverlay = new HelloItemizedOverlay(drawable, GoogleMapOverlayTestActivity.this);
    GeoPoint point = new GeoPoint(19240000,-99120000);
    OverlayItem overlayitem = new OverlayItem(point, "Hola, Mundo!", "I'm in Mexico City!");

    itemizedoverlay.addOverlay(overlayitem);
    mapOverlays.add(itemizedoverlay);

  }

 @Override
 protected boolean isRouteDisplayed() {
  // TODO Auto-generated method stub
  return false;
 }
}


、なんでか~?と調べたところ、

そもそも Drawable に対し、setBounds() の実行が必要で、
直接、Drawable に対し、setBounds() を実行するか、または、
boundCenter() または boundCenterBottom() を実行する必要があるようです。

Drawable より抜粋
The setBounds(Rect) method must be called to tell the Drawable where it is drawn and how large it should be. All Drawables should respect the requested size, often simply by scaling their imagery. A client can find the preferred size for some Drawables with the getIntrinsicHeight() and getIntrinsicWidth() methods.

対応としては、素直に、boundCenter() または boundCenterBottom() を使えばよいと思うのですが、
setBounds() を使用する方法でも構いません。

GoogleMapOverlayTestActivity.java の抜粋 (setBounds()の修正案)
// setBounds追加すると表示される。
drawable.setBounds(0, 0, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight());
HelloItemizedOverlay itemizedoverlay = new HelloItemizedOverlay(drawable, GoogleMapOverlayTestActivity.this);

2011年1月7日金曜日

Intent で Parcelable を実装した自作オブジェクトの受け渡しをしてみた

Activity から別の Activity を起動する際に、
自作クラスのオブジェクトの受け渡しについてサンプル・アプリを作ってみました。

Intent のメソッド一覧を見ていると、
putExtra(String name, <何か>) で、Intent にいろんな値をセットできるようになっています。

それを呼び出された側の Activity では、get<何たら>Extra(String name) で
値を取得できるようになっています。

この中に、putExtra(String name, Parcelable value) と getParcelableExtra(String name) があります。

Parcelable を自作クラスに適用すれば Activity 間で、オブジェクトの受け渡しができるだろう、
ということで、作ってみたサンプル・アプリが以下になります。

アクティビティ間でオブジェクトの受け渡しを行うということで、
メインとサブの2つのアクティビティを用意しました。

メイン・アクティビティ (IntentParcelableTestActivity) にはボタンを1個配置しています。

このボタンを押すと、サブ・アクティビティ (SubActivity) を起動するようになっており、
このとき、Pacelable インタフェースを実装した自作クラス (ParcelableTest) のオブジェクトを渡すようにしました。

    メイン・アクティビティ画面


サブ・アクティビティでは、受け取った ParcelableTest オブジェクトより、文字列 (String) と画像 (Bitmap) を取得し、表示します。

    サブ・アクティビティ画面


以下はソースです。

まずは、メイン・アクティビティのレイアウト・ファイルです。
ボタン1個しかありません。

main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
  android:orientation="vertical"
  android:layout_width="fill_parent"
  android:layout_height="fill_parent"
  >
<Button
  android:id="@+id/parceble_button"
  android:layout_width="wrap_content"
  android:layout_height="wrap_content"
  android:text="Parcelableの受け渡しテスト"
  />
</LinearLayout>


次は、アクティビティ間で受け渡しをするオブジェクト用クラスです。
ポイントは以下の2つです。
  1. Parcelable.Creator インターフェースを実装したオブジェクトを CREATOR という名前で作成する。

  2. writeToParcel(Parcel dest, int flags) と ParcelableTest(Parcel in) で読み書きするオブジェクトの順番は合わせる。

1. は android の API Reference にそうしろと書いてあります。
Parcelable
http://developer.android.com/reference/android/os/Parcelable.html

2. ですが、順番が合っていないと以下の Exception が発生しました。
(一部文字化けたままを引用)
Caused by: android.os.BadParcelableException: ClassNotFoundException when unmarshalling: Parcelable縺ョ繝・せ繝・
    at android.os.Parcel.readParcelable(Parcel.java:1822)

ParcelableTest.java
package jp.taki.parceble;

import android.graphics.Bitmap;
import android.os.Parcel;
import android.os.Parcelable;

public class ParcelableTest implements Parcelable {
  public String mTitle;
  public Bitmap mBitmap;
  public static final String PARCELABLE_TEST = "ParcelableTest";

  public ParcelableTest(String title, Bitmap bitmap) {
    this.mTitle = title;
    this.mBitmap = bitmap;
  }

  @Override
  public int describeContents() {
    // TODO Auto-generated method stub
    return 0;
  }

  @Override
  public void writeToParcel(Parcel dest, int flags) {
    dest.writeString(mTitle);
    dest.writeParcelable(mBitmap, flags);
  }

  public static final Parcelable.Creator<ParcelableTest> CREATOR = new Parcelable.Creator<ParcelableTest>() {
    public ParcelableTest createFromParcel(Parcel in) {
      return new ParcelableTest(in);
    }

    public ParcelableTest[] newArray(int size) {
      return new ParcelableTest[size];
    }
  };

  private ParcelableTest(Parcel in) {
    mTitle = in.readString();
    mBitmap = in.readParcelable(Bitmap.class.getClassLoader());
  }

}


次はメイン・アクティビティです。

画像ファイルはデフォルトで用意される icon.png (ドロイド君の画像) を利用しています。

ボタンを押すと、上記 ParcelableTest オブジェクトを作成し、Intent にセットして、
SubActivity を起動しています。

IntentParcelableTestActivity.java
package jp.taki.parceble;

import android.app.Activity;
import android.content.Intent;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;

public class IntentParcelableTestActivity extends Activity {
  /** Called when the activity is first created. */
  @Override
  public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);

    Resources r = getResources();
    final Bitmap b = BitmapFactory.decodeResource(r, R.drawable.icon);

    Button btn = (Button) findViewById(R.id.parceble_button);
    btn.setOnClickListener(new OnClickListener() {

      @Override
      public void onClick(View v) {

        ParcelableTest p = new ParcelableTest("Parcelableのテスト", b);
        Intent i = new Intent();
        i.putExtra(ParcelableTest.PARCELABLE_TEST, p);
        i.setClass(IntentParcelableTestActivity.this, SubActivity.class);
        startActivity(i);
      }
    });
  }
}


サブ・アクティビティのレイアウト・ファイルです。
文字列をセットする TextView (id=title) と画像をセットする ImageView (id=image) を配置しています。

sub.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
  android:orientation="vertical"
  android:layout_width="fill_parent"
  android:layout_height="fill_parent"
  >

<TextView
  android:layout_width="wrap_content"
  android:layout_height="wrap_content"
  android:text="Parcelableデータを以下に表示"
  />

<TextView
  android:id="@+id/title"
  android:layout_width="wrap_content"
  android:layout_height="wrap_content"
  />

<ImageView
  android:id="@+id/image"
  android:layout_width="wrap_content"
  android:layout_height="wrap_content"
  />

</LinearLayout>


サブ・アクティビティです。

Intent より ParcelableTest オブジェクトを取り出し、
画面上の TextView と ImageView にセットしています。

SubActivity.java
package jp.taki.parceble;

import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.widget.ImageView;
import android.widget.TextView;

public class SubActivity extends Activity {

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    setContentView(R.layout.sub);

    Intent i = getIntent();
    ParcelableTest p = i.getParcelableExtra(ParcelableTest.PARCELABLE_TEST);

    TextView title = (TextView) findViewById(R.id.title);
    title.setText(p.mTitle);

    ImageView image = (ImageView) findViewById(R.id.image);
    image.setImageBitmap(p.mBitmap);

  }
}


最後に、マニフェスト・ファイルに、SubActivity を登録して完成です。

AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="jp.taki.parceble"
    android:versionCode="1"
    android:versionName="1.0">
  <application android:icon="@drawable/icon" android:label="@string/app_name">
    <activity android:name=".IntentParcelableTestActivity"
          android:label="@string/app_name">
      <intent-filter>
        <action android:name="android.intent.action.MAIN" />
        <category android:name="android.intent.category.LAUNCHER" />
      </intent-filter>
    </activity>
    <activity android:name=".SubActivity"
          android:label="@string/app_name" />
  </application>
  <uses-sdk android:minSdkVersion="4" />

</manifest>

まあ、今回のように、String 1個と Bitmap 1個を受け渡すだけなら、それぞれ、
putExtra(String name, String value) と putExtra(String name, Parcelable value) を呼び出せば、
もっと簡単だと思うんですけど。。(; ̄ー ̄A アセアセ・・・