2010年11月10日水曜日

Subversion リポジトリのバックアップ (svnsync編)

Subversion のリポジトリについて、バックアップ手順をメモ。

まずは、svnsync の場合。

svnsync は、別の PC に Subversion のリポジトリを作成し、複製するというもの。

  1. 複製用リポジトリ作成

    C:\> mkdir C:\subversion

    C:\> cd C:\subversion

    C:\subversion> svnadmin create backup


  2. pre-revprop-change.bat 作成
    C:\subversion\backup\hooks\pre-revprop-change.bat を新規に作成し、
    exit /b 0 の1行のみを記述。

    これを実施しないと、svnsync sync 実行時に以下のエラーが出力される。

    svnsync: リポジトリが、リビジョン属性を変更できるようにはなっていません。
    管理者に pre-revprop-change フックを作成するよう頼んでください


  3. 同期を実施

    C:\subversion> svnsync init file:///subversion/backup https://www.xxx.yyy.zzz:8080/svn/svn_project
    リビジョン 0 の属性をコピーしました。

    C:\subversion> svnsync sync file:///subversion/backup
    リビジョン 1 をコミットしました。
    リビジョン 1 の属性をコピーしました。
    リビジョン 2 をコミットしました。
    リビジョン 2 の属性をコピーしました。
    リビジョン 3 をコミットしました。
    (中略)
    ファイルのデータを送信しています ..
    リビジョン 113 をコミットしました。
    リビジョン 113 の属性をコピーしました。
    C:\subversion>

    ちなみに proxy 経由で、複製元(http(s)://~)に接続する場合は、
    servers ファイル(*1)に proxy 設定の記述が必要。

    (*1) Windows XP の場合。
           C:\Documents and Settings\<ユーザー名>\Application Data\Subversion\servers

ん~。
ちなみに複製元(http(s)://~)には、Basic 認証でユーザー名/パスワードを入力する必要があるのですが、
svnsync init 及び sync ともに、ユーザー名/パスワードを指定していません。

何で指定しなくても処理できるのか、薄気味悪かったのですが、
調べてみると、以下のフォルダにあるファイルにて、ユーザー名/パスワードを記録しているようです。

C:\Documents and Settings\<ユーザー名>\Application Data\Subversion\auth\svn.simple

確かに、上記フォルダを見ると、
16進数(0~9, a~f)で構成された 30 文字くらいのファイルがあり、
そのファイルの中身を見ると、パスワードは暗号化されているようで分かりませんでしたが、
(当たり前と言えば当たり前)、ユーザー名が記述されていました。

AdMob に挑戦!

AdMob とは、モバイル向けの広告サービス(Google AdSense のモバイル版)です。

ということを本日知りました(^_^;)アセアセ

・・・ということで早速試してみることにしました!



しか~し!
当初、以下のサイトを見ていたのですが、どうも情報が古いようで。。

http://developer.admob.com/wiki/Android

上記サイト通りにやると、以下のようなエラーが LogCat に記録されて、
ちっとも広告が表示されませんでした。シクシク。

could not find com.admob.android.ads.AdMobActivity, please make sure it is registered in AndroidManifest.xml
com.admob.android.ads.AdMobActivity must be registered in your AndroidManifest.xml file.


ということで、、

以下のPDFを参考にして作業をすれば、無事、広告表示までサンプル・アプリを作成できました。
参考PDF:
http://www.admob.com/docs/AdMob_Android_SDK_Instructions.pdf


以下に、手順をメモしておきます。

  1. AdMob のアカウントを作成。

    AdMob サイト より、"未登録の方は、今すぐ登録する" を選択します。

    すると、アカウント情報を入力する画面になるので、太字になっている箇所を入力します。

    入力が終わると、アカウント情報で指定したメールアドレスにメールが配信されます。
    配信されたメールに、アカウントを有効化にするリンクがあるので、このリンクをクリックします。

    以上で、アカウント作成は完了です。


  2. AdMob SDK 入手

    作成したアカウントでログインします。

    ログイン後、"サイト及びアプリケーション" のタブを選択し、
    "+サイト/アプリケーションの追加" をクリックします。



    アプリケーションのタイプとして、"Android アプリケーション" を選択します。
    App名、ジャンル、Appの説明の 3 つは入力が必須なので、適当に入力します。
    Android パッケージURL は必須ではないので、特になければ空白にしておきます。



     アプリケーションの登録が完了後、
    "AdMob Android SDK のダウンロード" をクリックし、SDK をダウンロードします。



  3. AdMob サンプル・アプリの作成 (プロジェクト作成~ライブラリ追加)

    android プロジェクトを作成します。
    とりあえず、以下の感じで作成してみました。

    項目 値
    Project name AdMobTest Proj
    Build Target Android 1.6
    Application name   AdMobTest
    Package name jp.kochi.AdMobTest   
    Create Activity AdMobTestActivity
    Min SDK Version 4


    プロジェクトを作成したら、libs という名前でフォルダを作成します。

    libs フォルダを作成したら、その中に、admob-sdk-android.jar をコピー(Import)します。

    次に、プロジェクトの Properties を開き、"Java Build Path" を選択し、
    "Libraries" から "Add JARs..." を実行して、libs フォルダにコピーした admob-sdk-android.jar を選びます。




  4. AdMob サンプル・アプリの作成 (パブリッシャーIDの登録~アプリ完成/起動確認)

    マニフェスト・ファイルに修正(青字部分が追記部分)します。

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

        <!-- The application's publisher ID assigned by AdMob -->
        <meta-data android:value="a14xxxxxxxxxxxx" android:name="ADMOB_PUBLISHER_ID" />

        <!-- AdMobActivity definition -->
        <activity android:name="com.admob.android.ads.AdMobActivity"
          android:theme="@android:style/Theme.NoTitleBar.Fullscreen"
          android:configChanges="orientation|keyboard|keyboardHidden" />
        <!-- Track Market installs -->
        <receiver android:name="com.admob.android.ads.analytics.InstallReceiver"
          android:exported="true">
          <intent-filter>
            <action android:name="com.android.vending.INSTALL_REFERRER" />
          </intent-filter>
        </receiver>

        <meta-data android:value="true" android:name="ADMOB_ALLOW_LOCATION_FOR_ADS" />

      </application>
      <uses-sdk android:minSdkVersion="4" />

      <uses-permission android:name="android.permission.INTERNET" />
      <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
      <uses-permission android:name="android.permission.ACCESS_MOCK_LOCATION" />
    </manifest> 

    meta-data android:value="a14xxxxxxxxxxxx" で指定するパブリッシャーIDは、
    AdMob のサイトにて、"サイト及びアプリケーション" から該当アプリの "設定管理" より確認できます。



    res/values フォルダに、attr.xml を新規作成し、以下の内容を記述します。

    res/values/attr.xml
    <?xml version="1.0" encoding="utf-8"?>
    <resources>
      <declare-styleable name="com.admob.android.ads.AdView">
        <attr name="backgroundColor" format="color" />
        <attr name="primaryTextColor" format="color" />
        <attr name="secondaryTextColor" format="color" />
        <attr name="keywords" format="string" />
        <attr name="refreshInterval" format="integer" />
      </declare-styleable>
    </resources>


    res/layout/main.xml に以下の内容を記述します。
    xmlns:myapp="http://schemas.android.com/apk/res/<パッケージ名>" の <パッケージ名> には、
    マニフェスト・ファイルに記述されているパッケージ名を指定します。

    res/layout/main.xml
    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
      xmlns:myapp="http://schemas.android.com/apk/res/jp.kochi.AdMobTest"
      android:orientation="vertical"
      android:layout_width="fill_parent"
      android:layout_height="fill_parent">
      <com.admob.android.ads.AdView
        android:id="@+id/ad"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        myapp:backgroundColor="#000000"
        myapp:primaryTextColor="#FFFFFF"
        myapp:secondaryTextColor="#CCCCCC" />
    </LinearLayout>


    最後に Activity です。
    エミューレーターで動作確認をする場合、
    以下のように、テストモードに設定する必要があるようです。

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

            AdManager.setTestDevices(new String[] { AdManager.TEST_EMULATOR });

            AdView adView = (AdView)findViewById(R.id.ad);
            adView.requestFreshAd();

        }
    }

    以上でサンプル・アプリ作成は完了です。

    ちなみに、実機で稼動させると、LogCat 上に以下のようなメッセージ(デバイスID)が表示されます。

    I/AdMobSDK( 1712): To get test ads on this device use AdManager.setTestDevices(new String[] { "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" } )

    エミュレータに加え、実機でもテスト・モードで稼動させるには、
    この xxxxx のデバイスIDも、AdManager.setTestDevices() で指定すればよいです。

    AdManager.setTestDevices(new String[] { AdManager.TEST_EMULATOR, "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" });


    エミュレータで稼動させると、1回目はうまく表示されませんでしたが、
    2回目は、きちんと広告が表示されました~


何だか、最後まで、すんなりといきませんでしたが、
とりあえずヨシとしておきます。

2010年11月8日月曜日

BroadcastReceiver を別プロセスで動かしてみたときの感想文

BroadcastReceiver を別プロセスで動かしたところ、
ただの知識不足が原因と言えるのですが、
一部、予想外の動きだったので、その驚き?をメモしておきます。

別プロセスで動かすということで、
マニフェスト・ファイルには、以下のように記述しました。

AndroidManifest.xml
<receiver android:name=".LocationReceiver" android:process=":remote"/>

LocationReceiver が BroadcastReceiver を拡張したクラスになります。


LocationReceiver には以下のような記述をしました。

LocationReceiver.java
public class LocationReceiver extends BroadcastReceiver {

  LocationManager mLocationMgr;
  
  public void onReceive(Context arg0, Intent arg1) {

    if (mLocationMgr == null) {
      mLocationMgr = (LocationManager) arg0
        .getSystemService(arg0.LOCATION_SERVICE);
    }
  }
  
}

で、ここから意外だったんですが、
毎回毎回 mLocationMgr が null になることが分かりました。

2回目以降の呼び出しでは、null 以外を想定していました。


なんでじゃい!と思って、調べてみると、

まず、BroadcastReceiver は onReceive() を実行している間のみ Active で、
実行が終われば、Inactive になると記述があります。

Broadcast receiver lifecycle からの抜粋。
When a broadcast message arrives for the receiver, Android calls its onReceive() method and passes it the Intent object containing the message. The broadcast receiver is considered to be active only while it is executing this method. When onReceive() returns, it is inactive.

さらに同ページのプロセスのライフ・サイクルの記述を見ると、
Empty process という存在の記述があります。

Processes and lifecycles
  1. An empty process is one that doesn't hold any active application components. The only reason to keep such a process around is as a cache to improve startup time the next time a component needs to run in it. The system often kills these processes in order to balance overall system resources between process caches and the underlying kernel caches.

DDMS で見ると、":remote" がプロセス名に付与されたプロセスが同一の PID で存在し続けるので、
onReceive() 実行後も empty process として残っていると考えられます。

以上の2点を踏まえて、今回の動きを説明すると、
どうも以下のような動きをしているようです。
  1. プロセス生成。
  2. LocationReceiver 初期化、onReceiver() 実行。
  3. onReceiver() 終了後、LocationReceiver を破棄。
  4. プロセスは残る。

で、2回目以降の呼び出しでは、2. から実行される、と思われます。

ふ~ん、、、って感じですね。。
( ̄ー ̄;アセアセ

2010年11月4日木曜日

gapps-mdpi-tiny-20101020-signed.zip を適用してみた

いまさらですが。。

CyanogenMod 6.0.0 に更新してから、2ヶ月以上経過して、
GPS が機能していないことに気付きました。

う~ん。いろいろと、いじっているうちに、おかしくなったのかもしれませんが、
最初から、こうだったのかは、もう分かりません Y(>_<、)Y ヒェェ!

HT-03A を CyanogenMod 6.0.0 に更新する際に、
以下の2ファイルを適用したわけですが、Google Addon については、
さらに新しいバージョンが公開されていたので、適用したら、GPS 使えました!


9月15日に更新したファイル

update-cm-6.0.0-DS-signed.zip
gapps-mdpi-tiny-20100816-signed.zip


以下に手順をメモしておきます。

[注意]
  手順の中で Wipe を実行することで、導入したアプリの削除、
  各種設定の初期化を実施しています。

  1. RA-sapphire を起動 (HOME+電源キー同時押し)

  2. バックアップ取得。
    Backup/Restore --> Nand backup

  3. Google Addon をダウンロード。

    サイト:
      http://wiki.cyanogenmod.com/index.php?title=Latest_Version

    ファイル:
      gapps-mdpi-tiny-20101020-signed.zip

  4. SDカードにファイルを配置。

  5. PCとHT-03Aを結ぶUSBケーブルを外す。

  6. RA-sapphire を起動 (HOME+電源キー同時押し)

  7. Wipe 実行。
    Wipre --> Wipe data/factory reset

  8. gapps-mdpi-tiny-20100816-signed.zip 適用。
    Flash zip from sdcard -->SDCARD:gapps-mdpi-tiny-20101020-signed.zip

  9. 起動。
    Reboot system now

無事、Google Map で GPS 機能が動いていることが確認できました v(。・ω・。)ィェィ♪

しかし、アプリの再導入やら、Google アカウントの初期設定やら、
全てやり直しですけど。。Σ(T▽T;) ぐわわぁぁ~ん!

2010年11月1日月曜日

MapView でアニメーション (Overlay編)

MapView を使って、Google マップを表示させている上に、
画像をアニメーションさせるサンプル・プログラムの2つ目です。

2つ目は、日本Androidの会のメーリング・リストでやりとりされていた
以下のスレッドを参考にさせて頂きました。
参考サイト: OverlayにImageViewを表示する方法について

参考サイトでは、OverlayItem を使用しているのですが、
表示させたい画像(アニメーション)は1つしかないので、Overlay を使用するようにしました。


<設計方針>

Overlay を使います。

Overlay の draw() で画像を表示させるわけですが、画像を60枚用意しておき、
draw() が実行される都度、画像を切り替えるようにしておきます。
(最後の画像になったら、最初の画像に戻ります)

MapView に対して定期的に invalidate() を実行することで、Overlay の draw() が定期的に
実行されるようになり、パラパラ漫画(アニメーション)の完成です。

<問題点>

問題点でないですが、MapView に対して invalidate() を実行しているため、
複数の画像(静止画)があった場合、それらも再描画対象となるかなぁというのが気にかかります。


以下にポイントとなるソースを貼り付けます。

まずはレイアウトです。
FrameLayout に対し、MapView のみを表示させています。
(子要素が MapView しかないので、FrameLayout である必要はないんですけど。)

main.xml
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    >
<com.google.android.maps.MapView
 android:id="@+id/mapview"
 android:layout_width="fill_parent"
 android:layout_height="fill_parent"
 android:enabled="true"
 android:clickable="true"
 android:apiKey="xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
 />
</FrameLayout>


次は Activity (MapActivity継承) クラスです。

onCreate() では、person_01.png ~ person_60.png の 60 枚の画像を読み込んでいます。
その後、アニメーションを行う Overlay を MapView に紐付けています。

MapTestActivity.java
package jp.kochi.test;

import java.util.ArrayList;
import java.util.List;
import com.google.android.maps.GeoPoint;
import com.google.android.maps.MapActivity;
import com.google.android.maps.MapView;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.drawable.AnimationDrawable;
import android.os.Bundle;
import android.util.Log;
import android.widget.ImageView;

public class MapTestActivity extends MapActivity {
 ImageView mImageView;
 AnimationOverlay mAnimOverlay;

 @Override
 public void onCreate(Bundle savedInstanceState) {
  Log.v("MapTest", "onCreate()");
  super.onCreate(savedInstanceState);
  setContentView(R.layout.main);

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

  mapView.setBuiltInZoomControls(true);

  ArrayList<Bitmap> list = new ArrayList<Bitmap>();
  Resources r = getResources();

  int id;
  Bitmap bmp;
  String str;
  for (int i = 1; i <= 60; i++) {
   str = "person_" + String.format("%1$02d", i);
   id = r.getIdentifier(str, "drawable", getPackageName());
   Log.v("MapTest", "str = " + str + ", id = " + id);
   bmp = BitmapFactory.decodeResource(r, id);
   list.add(bmp);
  }

  Log.v("MapTest", "list.size() = " + list.size());

  // アニメーション表示
  // お遍路さん2番札所
  double latitude  = 34.156028;
  double longitude = 134.49025;
  GeoPoint gpoint = new GeoPoint((int)(latitude * 1E6), (int)(longitude * 1E6));
  List listOverlays = mapView.getOverlays();
  mAnimOverlay = new AnimationOverlay(gpoint, list, mapView);
  listOverlays.add(mAnimOverlay);

 }
 
 @Override
 protected void onResume() {
  Log.v("MapTest", "onResume()");
  super.onResume();
  mAnimOverlay.resume();
 }
 
 @Override
 protected void onPause() {
  Log.v("MapTest", "onPause()");
  super.onPause();
  mAnimOverlay.pause();
 }

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

onResume()、onPause() では、Overlay のアニメーション (定期的に invalidate() 実行) を
再開/停止させる処理を追加しています。


最後は AnimationOverlay (Overlay継承) クラスです。

AnimationOverlay.java
package jp.kochi.test;

import java.util.ArrayList;
import java.util.List;
import android.R.bool;
import android.R.drawable;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Point;
import android.graphics.drawable.AnimationDrawable;
import android.graphics.drawable.Drawable;
import android.os.Handler;
import android.util.Log;
import com.google.android.maps.GeoPoint;
import com.google.android.maps.MapView;
import com.google.android.maps.Overlay;
import com.google.android.maps.Projection;

public class AnimationOverlay extends Overlay implements Runnable {
 private Handler mHandler;
 private ArrayList<Bitmap> mPngList;
 private int current_frame;
 private MapView mMapView;
 private GeoPoint mGeoPoint;
 final private int sInterval = 100;
 private boolean mStop = false;

 public AnimationOverlay(GeoPoint point, ArrayList<Bitmap> list,
   MapView mview) {
  Log.v("MapTest", "AnimationOverlay()");

  mPngList = list;
  mMapView = mview;
  mGeoPoint = point;

  current_frame = 0;

  mHandler = new Handler();
  mHandler.postDelayed(this, sInterval);
 }

 @Override
 public void draw(Canvas canvas, MapView mapView, boolean shadow) {
  super.draw(canvas, mapView, shadow);

  if (shadow == false) {
   Log.v("MapTest", "draw() : shadow = false");
   Point pxPoint = new Point();

   Projection projection = mMapView.getProjection();
   projection.toPixels(mGeoPoint, pxPoint);

   canvas.drawBitmap(mPngList.get(current_frame), pxPoint.x,
     pxPoint.y, null);
  } else {
   Log.v("MapTest", "draw() : shadow = true");
  }
 }

 @Override
 public void run() {
  Log.v("MapTest", "run()");
  if (current_frame == mPngList.size() - 1)
   current_frame = 0;
  else
   current_frame++;

  mMapView.invalidate();

  if (mStop == false) mHandler.postDelayed(this, sInterval);
  
 }
 
 public void resume() {
  mHandler.postDelayed(this, sInterval);
  mStop = false;
 }
 
 public void pause() {
  mStop = true;
 }
 
}


以下は実行画面です。
これもまた、分かりにくいですが、画面中央付近の画像が変化(アニメーション)しています。