DialogFragmentでダイアログ表示
2013-05-02-1 / カテゴリ: [Android] / [permlink]

GB(Android2.3)までは、Activity#onCreateDialog(int) をOverrideしたメソッドで AlertDialog を作るようにしておいて、ダイアログを表示したい箇所で Activity#showDialog(int) すればOKだったけど、API level 13からこれらの実装は deprecated になってる。

どうするかと言うと、DialogFragment を使ってダイアログを表示する。
API 10以下(GB以下)の場合は、Support Packageを使えばOK。
画面部分にFragmentを使わない(使いたくない)場合でも、ダイアログのみの利用でも大丈夫。
続きを読む

Tone*Belをリリースしました
2013-04-20-1 / カテゴリ: [Android] / [permlink]

AndroidアプリTone*Belをリリースしました。

年末にKindleで昔読んで面白かった漫画を見つけたので読んでたら、ポケベルが出てきて、「そういや昔使ってたポケベルに、電話帳からトーン発信して電話かける機能あったなー」と、似た機能のアプリがなさげだったので作り始めてみたけど、意外と4か月近くもかかってしまった(笑)
機種変してSIMを取っ払った古い端末でも、このアプリがあれば電話帳の番号を公衆電話とかから簡単に電話かけれるよーというコンセプトだったので、Android1.6から対応しようとしたのがそもそも(ry
電話帳参照のやり方がFroyo以前でEclairとDonutでも(あとエミュレータと実機でも)違ったので、その辺りはちょっと苦労してしまった。
検証用にHT-03Aを送ってくれた @kanchan0318 さんありがとー!

使いかたは端末のスピーカーを受話器に固定して(実際はもっとゼロ距離レベルでくっつけて下さい)、

アプリから掛けたい電番を選択すればトーンがなって電話がかかる仕組み。

PhoneGapを使ったhtmlベースのAndroid/iOSハイブリッドアプリ開発環境メモ
2013-02-25-1 / カテゴリ: [html][Android] / [permlink]

PhoneGapを使ったアプリ開発をする機会があった(というか現在進行形)ので簡単にメモ(Android視点)。
2013-02-25時点で2.4.0がリリース版は最新(2/7頃に更新されてて開発サイクルは結構速い)。ドキュメントも公式で日本語化されたものが公開されてる(ただし今日の時点で2.2まで)

PhoneGap用の開発環境があるわけではなく、PhoneGapを利用した動作に必要なライブラリ込みのAndroidアプリプロジェクトをツールを使って生成・ADTの「既存のコードからAndroidプロジェクト生成」を行い、通常のAndroid同様Eclipse(や信仰しているエディタ)で開発する流れ。

続きを読む

ubuntu11.04(x64)でJDK6+Eclipse+Androidアプリ開発環境
2011-09-26-1 / カテゴリ: [linux][Java][ubuntu][Android] / [permlink]

デスクトップで開発できるように環境構築。

JDK6

ここを参考: Ubuntu 11.04 日本語 Remix CD からのインストールログ
このまま叩けばOK
# add-apt-repository "deb http://archive.canonical.com/ natty partner"
# apt-get update
# aptitude install sun-java6-jdk

これで以下のjavaが入る(2011-09時点)
zaki@cheddar% java -version
java version "1.6.0_26"
Java(TM) SE Runtime Environment (build 1.6.0_26-b03)
Java HotSpot(TM) 64-Bit Server VM (build 20.1-b02, mixed mode)
zaki@cheddar% javac -version
javac 1.6.0_26

Eclipse

ここを参考: Ubuntu 11.04 Eclipse 3.6 Heliosのインストール

aptだと3.5.2が入るみたいなので、公式から3.6を持ってきた。このへんのEclipse IDE for Java DevelopersのLinux 64-bitから。
ダウンロードしたら、適当な場所で展開すればOK。それだけ。
自分とこは /home/zaki/local/eclipse に置いてる。

日本語化(Pleiades)については公式から1.3.3のzipを落としてくる。
適当にunzipしたら、featuresとpluginsディレクトリをeclipse直下にコピーして、eclipse.iniを編集すればOK
zaki@cheddar% cp -a features ~/local/eclipse             [~/Downloads/pleiades]
zaki@cheddar% cp -a plugins ~/local/eclipse              [~/Downloads/pleiades]

--- eclipse.ini.org     2011-09-26 21:49:04.095663261 +0900
+++ eclipse.ini 2011-09-26 22:27:57.125662531 +0900
@@ -17,3 +17,4 @@
 -XX:MaxPermSize=256m
 -Xms40m
 -Xmx384m
+-javaagent:/home/zaki/local/eclipse/plugins/jp.sourceforge.mergedoc.pleiades/pleiades.jar
readmeには javaagent:plugins/jp.sourceforge.mergedoc.pleiades/pleiades.jar としか書かれていないけれど、eclipseの実行ディレクトリからの相対パス(あるいは絶対パス)で記述していないとエラーになるため「eclipseのインストールディレクトリにPath設定してどこからでも起動可能に設定」とかできなくなってしまうので絶対パスで記述。

あとはWindowsと同じ要領で
% eclipse -clean
で起動してやればOK

Android SDK

公式からLinux用のtgzを落としてきて、適当なディレクトリに展開。Windowsと違ってSDK Manager.exeはなく、tools/android を実行すればOK。
ただしSDKは32bit版しかないため、64bitOSの場合は、ia32-libsパッケージが必要なのでインストールしておく。
([2011-09-23-1]で書いてる内容で flashplugin-installer をaptでインストール済みなら依存で入っているはず)
自分とこは /home/zaki/local/android-sdk に置いて、toolsとplatform-toolsにPath設定してる。

SDK起動後の操作と、(eclipseから実行する)ADTについては省略!

実機との接続

手持ちの実機はほとんど(HTC Desire(X06HTII)/Nexus S/XOOM au版/XPERIA X10 mini pro)はケーブルを繋ぐだけでadbが認識してパッケージインストールやLogCatなど問題なく動作したけど、T-01C(ドコモ版REGZAフォン)のみシリアルNoが"????????"と表示されて認識されなかった。
みたいなことをツイートした@lgfuserさんと@sato_cさんからノータイムで教えて いただき ました m__m

WindowsのときにデバイスマネージャでベンダーIDとか書き写してドライバインストールしたり面倒だった作業に相当するのかな。
こちらを参照: http://developer.android.com/guide/developing/device.html

結局T-01Cの場合は以下のファイルを作成してUSBケーブルを接続しなおせばadbで認識した。
root@cheddar:/# cat /etc/udev/rules.d/51-android.rules
SUBSYSTEM=="usb", ATTRS{idVendor}=="0930", MODE="0666"
複数デバイス記述が必要な場合は、2行目3行目と追記していけばよさげ。

あとは旧環境からデバッグキーを持ってくるなりエクスポートした設定を持ってくるなりすればOK


まぁもうすぐ11.10になるんだろうけど…

LaunchWatchをリリースしました
2011-08-16-1 / カテゴリ: [Android] / [permlink]

AndroidアプリLaunchWatchをリリースしました。


探しても見つからなかった「秒表示」する時計ウィジェットと、「ショートカットの登録」に対応した時計ウィジェット&アプリランチャーアプリです。
よかったら使ってみてください。

ちなみに時計の秒表示は、通常時はオフで、ウィジェットをタップした時のみ表示されるので「毎秒動作だとバッテリ消費が〜」ということはないです。

あとお遊びでUNIX timeの表示ができます。

将来的には聖剣伝説(2〜)のリングコマンドみたいにしたいんだけど、まぁいつになることやら…

PreferenceScreenに依存設定(dependency)を行ってチェックオフ時は操作不可
2011-07-25-1 / カテゴリ: [Android] / [permlink]

[2011-07-03-1]の各設定を「通信を許可」というチェックがonの場合のみ操作できるようにしてみる。


「通信を許可」のチェックボックス追加

ここは簡単に。
チェックボックスを追加して
Android XML
属性を設定(手抜き見やすくするために 以下同文)
Android XML

依存の設定

既存の「SSL(チェックボックス)」「サイト名(EditText)」「端末(List)」それぞれの「Dependency」に、追加した「通信を許可」のKeyを設定する。
Android XML

全体でこんな感じ
<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen
	xmlns:android="http://schemas.android.com/apk/res/android"
>
	<CheckBoxPreference
		android:title="通信"
		android:key="enable_transmit"
		android:summary="通信を許可"
	></CheckBoxPreference>

	<CheckBoxPreference
		android:summary="SSLを使用する"
		android:key="usessl"
		android:title="SSL"
		android:dependency="enable_transmit"
	></CheckBoxPreference>

	<EditTextPreference
		android:key="name"
		android:title="サイト名"
		android:summary="サイトの名称"
		android:dependency="enable_transmit"
	></EditTextPreference>

	<ListPreference
		android:key="list"
		android:entryValues="@array/listpref_value"
		android:entries="@array/listpref_entries"
		android:title="端末"
		android:summary="端末を選択"
		android:dependency="enable_transmit"
	></ListPreference>
	

</PreferenceScreen>

で、こうなる。
左がチェックoff(disable)で、右がチェックon


さらに、子要素用のレイアウト指定をすることで(図はわかりにくいけど)若干インデントが掛かる

xml(抜粋) はこんな感じ
	<CheckBoxPreference
		android:summary="SSLを使用する"
		android:key="usessl"
		android:title="SSL"
		android:dependency="enable_transmit"
		android:layout="?android:attr/preferenceLayoutChild"
	></CheckBoxPreference>

PopupWindow#showAtLocation()が表示されない?
2011-07-13-1 / カテゴリ: [Android] / [permlink]

newした後、showAsDropDown()だけなら表示されるのに、showAtLocation()だけ叩いても表示されなかった。
いろいろ試してみると setWindowLayoutMode() が必要なのがわかった。

こんな感じ
mPopup = new PopupWindow(this);
mPopup.setWindowLayoutMode(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
mPopup.setFocusable(true);	// これがないとタッチとかできない

// 必要に応じて
mPopup.setWidth(WindowManager.LayoutParams.WRAP_CONTENT);
mPopup.setHeight(WindowManager.LayoutParams.WRAP_CONTENT);

// ポップアップで表示するビューの設定
mPopup.setContentView(view);

if (mPopup.isShowing()) {
	// 開いてたら閉じる
	mPopup.dismiss();
	mPopup = null;
}
else {
	// 開いてなければ表示
	mPopup.showAtLocation(base, Gravity.NO_GRAVITY, x, y);
}

ちなみにdismiss()せずにActivityを閉じると例外(?)が発生するので注意
ERROR/WindowManager(13219): Activity (パッケージ名).(クラス名) has leaked window android.widget.PopupWindow$PopupViewContainer@40533870 that was originally added here
ERROR/WindowManager(13219): android.view.WindowLeaked: Activity (パッケージ名).(クラス名) has leaked window android.widget.PopupWindow$PopupViewContainer@40533870 that was originally added here
:

ショートカット一覧の取得
2011-07-12-1 / カテゴリ: [Android] / [permlink]

アプリ一覧はいくらでも出てくるのに、ショートカットは検索しても出てこなかった(多分探し方が悪い)ので標準のホームアプリ(ランチャー)のソースを見て調べてみた。

ショートカットの一覧の取得は、アプリ一覧と異なり「Gmailのラベル」や「ブックマーク」などの「選択した後にアプリ固有の設定(選択)」があるので、単純なリスト取得ではできず、startActivityForResult()による起動と、onActivityResult()による結果の取得が必要。
たぶん
  • インテント ACTION_PICK_ACTIVITY を起動し、標準(?)の選択ダイアログを起動して固有設定を選択させる(ホーム画面のロングタップの「ショートカット」と同じ要領)
  • ショートカットの大元の一覧を取得し、自前のListViewなどで表示・ユーザに更に固有設定を選択させる
のどちらか。(他にもあるかも?)

ACTION_PICK_ACTIVITYを使ったショートカット一覧の取得

適当なとこで以下のIntentを起動。
(ダイアログのタイトル(EXTRA_TITLE))はリソースに記述して getText(res_id) が良いけどとりあえず)
	Intent intent = new Intent(Intent.ACTION_PICK_ACTIVITY);
	intent.putExtra(Intent.EXTRA_INTENT, new Intent(Intent.ACTION_CREATE_SHORTCUT));
	intent.putExtra(Intent.EXTRA_TITLE, "ショートカットのリスト");

	startActivityForResult(intent, REQUEST_PICK_SHORTCUT);
startActivityForResult()は、一覧の取得と、固有設定の結果取得の2度呼ばれるので、第2引数のフラグ値は2種類定義しておく。
こんな感じ
private static final int REQUEST_PICK_SHORTCUT = 0x100;
private static final int REQUEST_CREATE_SHORTCUT = 0x200;

この時点(startActivityForResult()コール)で、ショートカット一覧のダイアログは起動する。

その中から一つ選択すると、onActivityResult()がコールバックされる。

とりあえず全体。
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
	super.onActivityResult(requestCode, resultCode, data);
	Log.d(TAG, "onActivityResult()" + resultCode);

	if (resultCode == RESULT_OK) {
		switch (requestCode) {
		case REQUEST_PICK_SHORTCUT:
			Log.d(TAG, "REQUEST_PICK_SHORTCUT");
			processShortcut(data);
			break;
			
		case REQUEST_CREATE_SHORTCUT:
			Log.d(TAG, "REQUEST_CREATE_SHORTCUT");
			completeAddShortcut(data);
			break;
			
		default:
			Log.d(TAG, "default");
			break;
		}
	}
	else {
		Log.d(TAG, "canceled");
	}
}
最初(リストからの選択)は、case REQUEST_PICK_SHORTCUT の部分。
void processShortcut(Intent intent) {
	startActivityForResult(intent, REQUEST_CREATE_SHORTCUT);
}
とくに処理なし(笑)、onActivityResult()でコールバックされてきた Intent 変数を、再度 startActivityForResult() する。
これで、「Gmailのラベル選択」や「twiccaのリスト選択」などのActivityが起動する。固有設定が特にないショートカットについては、startActivityForResult()のあとすぐに onActivityResult()がコールバックされる。
固有設定がある項目の場合は、設定(選択)が完了すれば onActivityResult() がコールバックされる。

で case REQUEST_CREATE_SHORTCUT の部分。
private void completeAddShortcut(Intent data) {
	String name = data.getStringExtra(Intent.EXTRA_SHORTCUT_NAME);
	Log.d(TAG, "shortcut name: " + name);
	
	Intent intent = data.getParcelableExtra(Intent.EXTRA_SHORTCUT_INTENT);
	
	// とりあえず
	startActivity(intent);
}
getParcelableExtra()で、対象のショートカットのIntentが取得できる。

自前で取得・表示・選択処理

アプリの一覧取得と同じく、android.content.pm.PackageManager を使う。
	PackageManager pm = getPackageManager();
	List<ResolveInfo> infos = pm.queryIntentActivities(new Intent(Intent.ACTION_CREATE_SHORTCUT), 0);
	
	for (ResolveInfo info : infos) {
		Log.d(TAG, "packagename: " + info.activityInfo.packageName);	// パッケージ名
		Log.d(TAG, "activityname: " + info.activityInfo.name);		// アクティビティ名
		Log.d(TAG, "appname: " + info.loadLabel(pm).toString());	// 表示名
		Log.d(TAG, "icon: " + info.loadIcon(pm));			// アイコン
	}
これで表示に必要なショートカット名とアイコン、起動に必要なパッケージ名とアクティビティ名が取れる。

あとはこのリストをListViewに突っ込み、タップ時に
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
	ListView list = (ListView) parent;
	
	/* 中略 */
	String packagename = "パッケージ名";		// ↑のinfo.activityInfo.packageName の値
	String activityname = "アクティビティ名";	// ↑のinfo.activityInfo.name の値
	
	Intent intent = new Intent(Intent.ACTION_CREATE_SHORTCUT);
	intent.setClassName(packagename, activityname);
	startActivityForResult(intent, 1);
}
などすれば、ACTION_PICK_ACTIVITY 使用の場合と同じく、固有設定のActivityが起動される(なければすぐ onActivityResult()がコールバック)ので、後はcompleteAddShortcut()と同じ。

一応、ResolveInfo の情報を保持するためのクラスと、ListViewにsetするためのAdapterListView内のレイアウトファイル
全体でこんな感じ
	ArrayList<AppInfoItem> items = new ArrayList<AppInfoItem>();
	AppInfoItem item;
	ListView listView = (ListView) findViewById(R.id.listView1);

	PackageManager pm = getPackageManager();
	List<ResolveInfo> infos = pm.queryIntentActivities(new Intent(Intent.ACTION_CREATE_SHORTCUT), 0);
	
	for (ResolveInfo info : infos) {
		item = new AppInfoItem();
		Log.d(TAG, "packagename: " + info.activityInfo.packageName);
		Log.d(TAG, "classname: " + info.activityInfo.name);
		Log.d(TAG, "appname: " + info.loadLabel(pm).toString());
		Log.d(TAG, "activityname: " + info.activityInfo.name);
		
		item.setAppName(info.loadLabel(pm).toString());
		item.setAppPackageName(info.activityInfo.packageName);
		item.setAppIcon(info.loadIcon(pm));
		item.setActivityName(info.activityInfo.name);
		items.add(item);
	}
	
	AppInfoAdapter adapter = new AppInfoAdapter(this, items);
	listView.setAdapter(adapter);
	listView.setOnItemClickListener(this);




以下、調べたときのログ。↑でうまくいかないときはチェックしてみて(ぇ
続きを読む

PreferenceのEditTextとListは文字列だよ
2011-07-04-1 / カテゴリ: [Android] / [permlink]

ListPreference で文字列しか使用できないのは、内部処理がテキスト配列として認識しているため。
    public ListPreference(Context context, AttributeSet attrs) {
        super(context, attrs);

        TypedArray a = context.obtainStyledAttributes(attrs,
                com.android.internal.R.styleable.ListPreference, 0, 0);
        mEntries = a.getTextArray(com.android.internal.R.styleable.ListPreference_entries);
        mEntryValues = a.getTextArray(com.android.internal.R.styleable.ListPreference_entryValues);
        a.recycle();

同じように、EditTextPreferenceがテキストonlyなのは、内部処理がテキスト扱いのため。
    public void setText(String text) {
        final boolean wasBlocking = shouldDisableDependents();

        mText = text;

        persistString(text);

        final boolean isBlocking = shouldDisableDependents();
        if (isBlocking != wasBlocking) {
            notifyDependencyChange(isBlocking);
        }
    }
    protected void onSetInitialValue(boolean restoreValue, Object defaultValue) {
        setText(restoreValue ? getPersistedString(mText) : (String) defaultValue);
    }
Referrer (Inside): [2011-07-03-1] [2011-06-08-1]

PreferenceActivity/PreferenceScreenで設定画面(Checkbox, EditText, List)
2011-07-03-1 / カテゴリ: [Android] / [permlink]

画面は、Windows7(x64)+Eclipse3.6+ADT11環境
[2011-06-08-1]の続きみたいな感じで。
超基礎(笑)
続きを読む
Referrer (Inside): [2011-07-25-1]

Preferencesで設定値の読書き
2011-06-08-1 / カテゴリ: [Java][Android] / [permlink]

設定の読書き。というか備忘録。
SharedPreferencesインスタンス経由で読んで、SharedPreferences.Editor経由で書き込む。

PreferenceActivity(PreferenceScreen)はまたそのうち。

続きを読む
Referrer (Inside): [2011-07-03-1]

Serviceの起動状態確認
2011-05-29-1 / カテゴリ: [Android] / [permlink]

ピンポイントで「○○の実行状態取得」ってのはなさそう。
「実行中のサービス一覧取得」があるので、リスト内に対象のクラスが含まれてるか確認する手段で。
public boolean isServiceRunning(Context c, Class<?> cls) {
	ActivityManager am = (ActivityManager) c.getSystemService(Context.ACTIVITY_SERVICE);
	List<RunningServiceInfo> runningService = am.getRunningServices(Integer.MAX_VALUE);
	for (RunningServiceInfo i : runningService) {
		Log.d(TAG, "service: " + i.service.getClassName() + " : " + i.started);
		if (cls.getName().equals(i.service.getClassName())) {
			Log.d(TAG, "running");
			return true;
		}
	}
	return false;
}
こんな感じで。
呼び出し側はContext(Activityとかならthis)と、チェック対象のサービスのClassインスタンスを突っ込めばOK
boolean isRunning = isServiceRunning(this, MyService.class);

それにしても RunningServiceInfo#started って何だろう。
IMEなんかがfalseでリストされるけど、trueにしても特に動きがない…

参考
起動中のサービス一覧を取得する - Androidプログラマへの道 〜 Moonlight 明日香 〜 - livedoor Wiki(ウィキ)

衝動買い
2011-04-08-1 / カテゴリ: [Android][diary] / [permlink]

買っちまった(*´∇`*)
Xoom

SQLiteのデータベースファイルをSDカードに保存
2011-02-24-1 / カテゴリ: [SQL][Android] / [permlink]

というのを年末調べてたけどまだまとめてなかったので。
内蔵ストレージに作られたファイルをコピーじゃなくて、最初からSD保存する構成。
大容量のデータを扱いたいとかデバッグ時とか。

というか、書籍やネットで「ファイル名を指定」している箇所をフルパスで指定(してandroid.permission.WRITE_EXTERNAL_STORAGEを付加)すればOK

SDK付属サンプルのNotePadであれば、
--- src/com/example/android/notepad/NotePadProvider.java.org	2011-01-20 16:43:58.312750000 +0900
+++ src/com/example/android/notepad/NotePadProvider.java	2011-02-24 21:06:29.551900000 +0900
@@ -30,6 +30,7 @@
 import android.database.sqlite.SQLiteOpenHelper;
 import android.database.sqlite.SQLiteQueryBuilder;
 import android.net.Uri;
+import android.os.Environment;
 import android.text.TextUtils;
 import android.util.Log;
 
@@ -43,7 +44,7 @@
 
     private static final String TAG = "NotePadProvider";
 
-    private static final String DATABASE_NAME = "note_pad.db";
+    private static final String DATABASE_NAME = Environment.getExternalStorageDirectory() + "/note_pad.db";
     private static final int DATABASE_VERSION = 2;
     private static final String NOTES_TABLE_NAME = "notes";
 
するとこんな感じ。(DesireIIで確認)
% adb shell ls -l /mnt/sdcard/note_pad.db
----rwxr-x system   sdcard_rw     4096 2011-02-24 11:50 note_pad.db

あとはファイルの暗号化機能とかあればなぁ…

ボリューム変更ハードキーのハンドリング
2011-02-17-2 / カテゴリ: [Android] / [permlink]

動作確認用アプリなんかで手抜きをしたい^a^k上下スクロールとかズームイン/アウトなんかにキー操作を割り当てしたい場合に。
標準だと「押す:ボリューム変更&UI表示 / 離す:通知音を鳴らす」という動作のため、KEYCODE_VOLUME_UPとKEYCODE_VOLUME_DOWNの両方をフックする必要がある。

というわけで、こんな感じ。
@Override
public boolean dispatchKeyEvent(KeyEvent event) {
	switch (event.getAction()) {
	case KeyEvent.ACTION_DOWN:
		switch (event.getKeyCode()) {
		case KeyEvent.KEYCODE_VOLUME_UP:
			// ここにボリュームupが押されたときに走らせたい処理
			return true;
			
		case KeyEvent.KEYCODE_VOLUME_DOWN:
			// ここにボリュームdownが押されたときに走らせたい処理
			return true;
			
		default:
			break;
		}
		break;
		
	case KeyEvent.ACTION_UP:
		switch (event.getKeyCode()) {
		case KeyEvent.KEYCODE_VOLUME_UP:
		case KeyEvent.KEYCODE_VOLUME_DOWN:
			// キーが離された場合はイベントを捨てる
			return true;
		default:
			break;
		}
		break;
		
	default:
		break;
	}
	return super.dispatchKeyEvent(event);
}

Androidでキー長押しはACTION_DOWNがコールバックされまくるんだな。

参考
キーイベントに応答するには - 逆引きAndroid入門
ハードキーフックの方法 - 明日の鍵

AlarmManagerで単純にブロードキャストを投げるだけ
2011-02-06-2 / カテゴリ: [Java][Android] / [permlink]

__軽く__ググってみても、「指定Activityを起動」「BroadcastReceiverをextendsした自前レシーバで受ける」しか見つけられなかったので「特定Activityが生きてれば受ける、なければ無視」をお試し。

ちなみに、定期/定時処理の実装は以下の方法がある(ほかにも有るかも。知らないだけで)
Timerアプリで管理
AlarmManagerシステム(android)で管理
するようなイメージみたい。(thanks! @amay077さん)
AlarmManagerはシステムレベルで動作するので、アプリの寿命と独立してるのがポイント。

public final static String INTENT_ACTION = "alarmmgr_broadcast";
:
:
	AlarmManager am = (AlarmManager)getSystemService(ALARM_SERVICE);

	Intent intent = new Intent(INTENT_ACTION);
	PendingIntent pending = PendingIntent.getBroadcast(this, 0, intent, 0);

	long current = SystemClock.elapsedRealtime();
	// 5秒後(current + 5*1000)にpending発動(1回きり)
	am.set(AlarmManager.ELAPSED_REALTIME, current + 5*1000, pending);
このBroadcastを受けるには[2011-02-06-1]の例で。

指定時間にブロードキャストでなく、単にActivityを起動するだけなら、
Intent intent = new Intent(this, FooBar.class);
PendingIntent pending = PendingIntent.getActivity(this, 0, intent, 0);
こんな感じで。サービス起動もいけそう(getService()で)。

AlarmManager.ELAPSED_REALTIME で「○msec後」に起動設定になる。
「Y年m月d日H時M分S秒に」の場合は、AlarmManager.RTC を使う
			Calendar cal = Calendar.getInstance();
			cal.set(2011, 1, 6, 3, 0, 0);	// 2011-02-06 03:00:00 の場合。monthは0オリジン(-1する)
			long next = cal.getTimeInMillis();
			Log.d(TAG, "current: " + next);
			am.set(AlarmManager.RTC, next, pending);

前記のAlarmManager#setは、指定時間に単に起動するのみ。
定期的に実行するにはAlarmManager#setRepeating()を使う。

キューに入ってるアラームを停止するにはcancel()
am.cancel(pending);
キャンセルは、Intentが同じ(filterEquals()がtrue)ものが対象らしい。
Remove any alarms with a matching Intent. Any alarm, of any type, whose Intent matches this one (as defined by filterEquals(Intent)), will be canceled.

AlarmManager#cancel

参考
Service(サービス)関連 mucchinのAndroid戦記
AlarmManagerで指定した時間に処理させる - Tech Booster
AlarmManager - 愚鈍人
Taosoftware: AlarmManager1 AndroidでCronみたいなことをするには
AlarmManagerの使用 - すにぺっと

レシーバおぼえがき(BroadcastReceiver)
2011-02-06-1 / カテゴリ: [Java][Android] / [permlink]

そのActivityがtopにいる場合にブロードキャストを受けるには。
public class FooBar extends Activity {
:
:
    // スレッドの通知を受けるためのレシーバ
	private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
		@Override
		public void onReceive(Context context, Intent intent) {
			Log.d(TAG, "  onReceive(" + context + ", " + intent + ")");
			if (intent.getAction().equals(Internal.INTENT_ACTION)) {
				Log.d(TAG, "intent val: " + intent.getIntExtra(Internal.INTENT_VAL, 0));
			}
		}
	};
	@Override
	public void onResume() {
		super.onResume();
		Log.d(TAG, "onResume()");
		// レシーバの登録
		registerReceiver(mReceiver, new IntentFilter(Internal.INTENT_ACTION));
	}
	
	@Override
	public void onPause() {
		super.onPause();
		Log.d(TAG, "onPause()");
		// レシーバの解除
		unregisterReceiver(mReceiver);
	}
:
registerReceiver()第2引数でフィルタを設定しているので、onReceive()内でgetAction()でAction名をチェックしてるのはこの例では冗長。
(rergisterReceive()を複数叩いて複数のレシーバを受ける場合は必要)

おまけ
↑のInternalは別クラス(ブロードキャスト元)
投げる部分はこんな感じ。(適当)
public class Internal extends Activity {
	public final static String INTENT_ACTION = "internal_thread_broadcast";
	public final static String INTENT_VAL = "internal_thread_val";
	private int mCount = 0;

// 中略
			Thread thread = new Thread(new Runnable() {

				@Override
				public void run() {
					// TODO 自動生成されたメソッド・スタブ
					try {
						for (;;) {
							Thread.sleep(1000);
							mCount ++;
							Log.d(TAG, "running count("+ mCount + ")");
							Intent intent = new Intent(INTENT_ACTION);
							intent.putExtra(INTENT_VAL, mCount);
							sendBroadcast(intent);
						}
					} catch (InterruptedException e) {
						// TODO 自動生成された catch ブロック
						e.printStackTrace();
					}
				}
				
			});
			thread.start();

// 後略
Referrer (Inside): [2011-02-06-2]

シェルの起動
2011-02-01-2 / カテゴリ: [shell][Android] / [permlink]

% adb shell
adbはSDK revision7まではtoolsの下にあったけど、revision8からはplatform-tools以下にあるので注意。(6→7だったかも知れん。うろ覚え)
エミュレータと実機、みたいに、複数のデバイスが接続されている場合は
% adb shell
error: more than one device and emulator
となってしまうので、
% adb devices
List of devices attached
emulator-5554   device
HT**********    device
で一覧を出して、
% adb -s emulator-5554 shell
と、シリアルナンバーでデバイスを指定してやる。

詳しくは
% adb -h
で見れる。

なお、シェルコマンドは(sshのリモートコマンド実行の要領で)
% adb [-s serialnumber] shell [command [arg...]]
で、一気に実行できる。

ちなみにシェルはsh(だよな?)。タイムマシンに乗った気分。
環境によって違うと思うけど、エミュレータ(2.2/デフォルトパラメタで作成)だと
# printenv
printenv
ANDROID_ROOT=/system
LD_LIBRARY_PATH=/system/lib
PATH=/sbin:/system/sbin:/system/bin:/system/xbin
ASEC_MOUNTPOINT=/mnt/asec
BOOTCLASSPATH=/system/framework/core.jar:/system/framework/ext.jar:/system/framework/framework.jar:/system/framework/android.policy.jar:/system/framework/services.jar
ANDROID_BOOTLOGO=1
ANDROID_ASSETS=/system/app
EXTERNAL_STORAGE=/mnt/sdcard
ANDROID_DATA=/data
ANDROID_PROPERTY_WORKSPACE=9,32768
# cat /proc/cpuinfo
cat /proc/cpuinfo
Processor       : ARM926EJ-S rev 5 (v5l)
BogoMIPS        : 279.34
Features        : swp half thumb fastmult vfp edsp java
CPU implementer : 0x41
CPU architecture: 5TEJ
CPU variant     : 0x0
CPU part        : 0x926
CPU revision    : 5

Hardware        : Goldfish
Revision        : 0000
Serial          : 0000000000000000

desireII(2.2/ドノーマル)だと
$ printenv
printenv
ANDROID_ROOT=/system
LD_LIBRARY_PATH=/system/lib
PATH=/sbin:/system/sbin:/system/bin:/system/xbin
ASEC_MOUNTPOINT=/mnt/asec
BOOTCLASSPATH=/system/framework/core.jar:/system/framework/ext.jar:/system/framework/framework.jar:/system/framework/android.policy.jar:/system/framework/services.jar:/system/framework/com.htc.framework.jar:/system/framework/com.htc.android.pimlib.jar:/system/framework/com.htc.android.easopen.jar:/system/framework/com.scalado.util.ScaladoUtil.jar
ANDROID_BOOTLOGO=1
ANDROID_ASSETS=/system/app
EXTERNAL_STORAGE=/mnt/sdcard
ANDROID_DATA=/data
ANDROID_PROPERTY_WORKSPACE=10,65536
$ cat /proc/cpuinfo
cat /proc/cpuinfo
Processor       : ARMv7 Processor rev 2 (v7l)
BogoMIPS        : 162.54
Features        : swp half thumb fastmult vfp edsp thumbee neon
CPU implementer : 0x51
CPU architecture: 7
CPU variant     : 0x0
CPU part        : 0x00f
CPU revision    : 2

Hardware        : bravo
Revision        : 0083
Serial          : 0000000000000000

などなど

エミュレータでパケットキャプチャ
2011-02-01-1 / カテゴリ: [command][network][Android] / [permlink]

ホストPCやサーバ側でキャプチャできない環境の場合、エミュ自身の内蔵tcpdumpでキャプチャ可能。
多分使う機会はホストがwindowsで、ホスト自身で動かしてるサーバへアクセスする際のキャプチャくらいかね。(リモートならEthereal使えば良し)

基本は
# tcpdump -s 0 -X port 80
など。

-s 0 は、未指定だとデフォルトキャプチャサイズが96bytesになり、キャプチャ漏れが発生するので、適当に指定すればよし。0指定だと65535byteになる。
-X は指定なしだと訓練されていないと読めない(笑)

port 80 の部分は、キャプチャのフィルタ条件。指定がないとidleでも結構パケットが飛んでいるので、見たいパケットが見えなくなる。
"port 80" で 80/TCP の全てのパケット
"host jp-z.jp" で、jp-z.jp との全パケット
"port 80 and host jp-z.jp" で↑のand

「コマンドラインじゃわからん!wiershark/etherealで見たいんだ!」な場合は
# tcpdump -s 0 -w /mnt/sdcard/capture.cap port 80
でキャプチャ結果をファイルへ出力できるので、DDMSのファイルエクスプローラで取り出して見ればOK
あとはググッてねん。

ちなみに停止の^Cで、シェルごとなぜか抜けてしまうので、PCのシェル(コマンドライン)上で
% adb -s emulator-5554 shell tcpdump -s 0 -w /mnt/sdcard/capture.cap port 80 and host jp-z.jp
とした方が良いかも知れん。


実機だとpermission deniedになる。うーん、rootな端末もあった方が良いのかも。

PKCS#5パディングでAES暗号化/復号
2011-01-25-2 / カテゴリ: [暗号][Java][Android] / [permlink]

初のAndroidネタ。というかJavaネタも初だった^^;

Androidは初学者だからもっと初学者ネタ(環境構築とか?)でもと思ったけど、入門系情報はたくさんあるので、うちらしく戯術っぽく小ネタで(ぇ
続きを読む
カテゴリ: Android / 1 2 次ページ

最終更新時間: 2013-05-02 16:12