2014年09月18日

[Android] 環境再構築

しばらく家での開発をサボっていて、久しぶりにPCを開いてみたら、いろいろと環境が古い状態だった。
これはイカンと環境を再構築することにした。
最初からやり直してみたかったので、既存のEclipseはポイっとゴミ箱へ。
最新のEclipseは4.4(Luna)になっていた。フラットデザインだ。

・ Eclipse Downloads https://www.eclipse.org/downloads/

今回はAndroid SDKをきちんと管理できるようにしたかったので、Homebrewを使ってAndroid SDKをインストールすることにした。
こちらのサイトを参考にさせていただいた。

・ 橋本商会 ≫ Android開発環境をインストールしなおした http://shokai.org/blog/archives/6407

Android SDKが簡単にインストールできるのは嬉しいのだけど、インストールされるパスが微妙ではなかろうか。
「/usr/local/Cellar/android-sdk/23.0.2/bin/android」のような感じで、途中にバージョンコードが入ってる。
23.0.2のディレクトリのままアップデートするのも微妙だし、バージョンごとにディレクトリ変えるのも微妙。
普通はインストール先を変えるものなのかな?

続いて、EclipseへADTをインストール。

・ Installing the Eclipse Plugin | Android Developers http://developer.android.com/sdk/installing/installing-adt.html

で、Eclipseの各種設定をしておしまい。
こちらのサイトを参考にしながら、適当に設定する。

・ モダン(かもしれない)なEclipse環境(Java)の構築方法 - wyukawa’s blog http://d.hatena.ne.jp/wyukawa/20100725/1280036738

新しい環境というのはいい感じだね。
posted by t2low at 02:35| Android

2014年02月03日

[Android] アプリの背景に設定されている壁紙(ライブ壁紙)を使う

アプリの背景に端末に設定された壁紙を利用したくて、以前こんな記事を書きました。

tappli blog: [Android] 壁紙を取得する

この方法で(静止画の)壁紙を取得することができるのですが、ライブ壁紙は取得できません。
ライブ壁紙が設定されていても、静止画の壁紙が返ってくるのです。
WallpaperManager#getWallpaperInfo()を使えば、ライブ壁紙の情報やサムネイル画像は取得できるのですが、背景として使うための画像は取得できそうにありませんでした。

いつものように検索してみると…

【ご助言お願いします】ホームアプリ作成での、ライブ壁紙を壁紙に配置する方法について - Google グループ

いつもいつも先達の知識に助けられてばかりです。ありがたいことです。
上のリンク先にも書かれていますが、テーマを設定するだけっぽいですね。
android:theme="@android:style/Theme.Wallpaper"

これだけでアプリの背景が壁紙と同じものになります。

posted by t2low at 22:00| Android

2014年01月30日

[Android] INSTALL_REFERRER のBroadcastについて

Google Playからアプリをインストールしたときに送信される(らしい)INSTALL_REFERRERというBroadcastアクションがあります。
(正確なアクション名は"com.android.vending.INSTALL_REFERRER")
開発中のアプリに組み込むライブラリの関係上、これを2つのBroadcastReceiverで受け取る必要が出てきました。

今までの自分の理解では、Broadcastは複数のレシーバに送信されるイメージでした。
UDPみたいなイメージです(自分のUDPのイメージも間違ってるかもしれません)。
それがこのINSTALL_REFERRERについては、通用しませんでした。
Google Play側で1アプリにINSTALL_REFERRERを受信する2つのBroadcastReceiverがあると送信しないようにしているんでしょうか…。
そのため、以下のような実装を行いました。
public class CustomReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
AReceiver a = new AReceiver();
a.onReceive(context, intent);

BReceiver b = new BReceiver();
b.onReceive(context, intent);
}
}

たぶんこれで動作するようになったはず…。
BroadcastReceiverって自分でインスタンス作っても良いんです?
これが正しい実装なんです?
イマイチすっきりしません。

すっきりしないので、サンプルアプリを作って確認してみました。
1つのアプリで、同じアクションを受け取る2つのBroadcastReceiverを定義して、adbからコマンド打って流してみたのです。
結果は2つともちゃんと受信しました…。
やっぱり受信しますよね。
Google Play側でそういう実装にしてるということなんですかねぇ…。
詳しい方がいましたら教えていただけると幸いですmm

posted by t2low at 23:00| Android

2014年01月29日

[Android] 強制停止するとBroadcastが受信できない

今開発中のアプリでBroadcastReceiverを使っています。
Manifestに書くだけで受信できるタイプのBroadcastを受け取るBroadcastReceiverです。

設定からアプリを「強制停止」させると、これが受信できなくなりました。
そういえば、いつのバージョンからかそういうことになっていた気がします。
検索したら↓こちらのブログが見つかりました。Android3.1以降だそうです。

Yukiの枝折: Android:Broadcastを受信できないアプリのSTOP状態

こちらの記事には、「一度も起動していない状態」のことは書かれていますが、「強制停止」したときのことは書かれていません。
たぶん同じだろうとは思いましたが、一応確認することにしました。
エミュレータでNexus4(API LEVEL 16)を作成して、"/data/system/packages-stopped.xml"を探します。
しかし、"/data/system/"に"packages-stopped.xml"がありません。
ソースコードを見てみると、記録されるファイルが変わっていました。
新しい場所は"/data/system/users/0/package-restrictions.xml"でした。(0はユーザーのID)
マルチユーザが導入されたのはAndroid4.2からだと思ってましたが、内部的にはAndroid4.1の頃からあったんですね。
XMLの中身も変わっているようです。こんな感じでした。
<?xml version='1.0' encoding='utf-8' standalone='yes' ?>
<package-restrictions>
<pkg name="com.hoge.app" stopped="true" nl="true" />
<pkg name="com.fuga.app" stopped="true" />
<pkg name="com.piyo.app" />
</package-restrictions>

一度も起動していないと「nl="true"」付くようです。
停止状態だと「stopped="true"」が付くようです。これが付いているとBroadcastを受け取れないんですね。
お勉強になりました。

posted by t2low at 22:00| Android

2014年01月23日

[Android] Fragment内のWebViewで先読みするにはどうすれば良いだろう?(2)

以前、以下の様な記事を書きました。

tappli blog: [Android] Fragment内のWebViewで先読みするにはどうすれば良いだろう?

結局うまいこと出来なかったのでした。
今回、また先読みの要望があがりました。
前回のように「ダイアログ風のレイアウト」で済ますのは難しそうな状況です。

Viewを渡すメソッドを作れば渡せないこともないと思いますが、それはちょっとねぇ。


前回、こんなことを書きましたが、これはFragmentのインスタンスが再生成されたときに、ちゃんと表示されないことが容易に想像できたからです。
今回、ふと思いつきました。逆ならいけるんじゃないかと。
FragmentのonCreateView()、またはDialogFragmentのonCreateDialog()あたりで、呼び出し元のActivityが特定のinterfaceを実装しているかチェックし、実装していたらそのinterfaceのメソッドでWebViewを取得する。
WebViewに表示するページはActivityで予め読み込んでおく、という寸法です。
これならインスタンスがシステムによって破棄されても、呼び出し元のActivityの再生成されるはずなので、WebViewが表示できないということはないはず…。
このやり方がイケていないというのはわかっていますが、試してみたところ、Fragment表示直後にWebページを表示したWebViewを表示することができました。

イケてないけど、動くことが大事!
と自分に言い聞かせているところです。
posted by t2low at 23:00| Android

2014年01月21日

[Android] 画像と文字列を下端で揃えたいとき

画像と文字列の下端を揃えたいという要望がありました。
RelativeLayoutでlayout_alignBottomを設定すると以下のようになります。
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_margin="8dp" >
<ImageView
android:id="@+id/green"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:src="@drawable/green" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignBottom="@id/green"
android:background="#ddf"
android:layout_toRightOf="@id/green"
android:text="テスト"
android:textAppearance="?android:attr/textAppearanceLarge" />
</RelativeLayout>

align_bottom.png
しかし、これでは上の画像のように、文字列の下に少し隙間が開いてしまいます。
フォントにはdescentという部分があるためですね。

descentを考慮して画像と文字列の下端を合わせたいときはlayout_alignBaselineを使います。
同時に画像を表示するImageViewにはbaselineAlignBottomをtrueにしておく必要があります。
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_margin="8dp" >
<ImageView
android:id="@+id/green"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:baselineAlignBottom="true"
android:src="@drawable/green" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignBaseline="@id/green"
android:layout_toRightOf="@id/green"
android:background="#ddf"
android:text="テスト"
android:textAppearance="?android:attr/textAppearanceLarge" />
</RelativeLayout>

align_baseline.png
こんな感じです。
これで画像と文字列の下端が揃いました。

posted by t2low at 22:00| Android

2014年01月20日

[Android] デフォルトで起動するアプリの判定

デフォルトで起動するように設定されているホームアプリの取得方法を調べてみました。
PackageManagerから簡単に習得できるんじゃないかと考えて、適当にクラスを眺めていましたが、見つからないので結局コードを追いかけることに…。

どうやらgetPreferredActivities()というメソッドを使えば良いようです。
String defaultHomeApp = "";

Intent intent = new Intent(Intent.ACTION_MAIN);
intent.addCategory(Intent.CATEGORY_HOME);

PackageManager pm = getPackageManager();
List<ResolveInfo> list = pm.queryIntentActivities(intent, 0);
for (ResolveInfo info : list) {
List<IntentFilter> filters = new ArrayList<IntentFilter>();
List<ComponentName> activities = new ArrayList<ComponentName>();
pm.getPreferredActivities(filters, activities, info.activityInfo.packageName);
if (activities.size() > 0) {
defaultHomeApp = activities.get(0).toString();
break;
}
}
Log.d("LOG", "Home app : " + defaultHomeApp);



posted by t2low at 23:00| Android

2013年12月18日

[Android] dumpsysコマンド

dumpsysコマンドについて調べようと思い、検索したら以下のページが引っかかりました。
公式サイトのようです。
Dumpsys | Android Developers

ちゃんと読んでないのですが、Usageのところのコマンドが気になりました。
$ adb shell su -- dumpsys window

dumpsysの後にwindowと書かれています。
そして、その後にはWINDOW_MANAGERとも書かれています。
これはContext#getSystemService()で取得できる「○○マネージャー」に相当する文字列を指定できるのではないかと考えました。

Context | Android Developers

dumpsysの後に、↑ここで書かれている文字列を指定してみました。
shell@android:/ $ dumpsys window # → OK
shell@android:/ $ dumpsys layout_inflater # → NG
shell@android:/ $ dumpsys activity # → OK
shell@android:/ $ dumpsys power # → OK
shell@android:/ $ dumpsys alarm # → OK
shell@android:/ $ dumpsys notification # → OK
shell@android:/ $ dumpsys keyguard # → NG
shell@android:/ $ dumpsys location # → OK
shell@android:/ $ dumpsys search # → OK
shell@android:/ $ dumpsys vibrator # → OK?
shell@android:/ $ dumpsys connection # → NG
shell@android:/ $ dumpsys wifi # → OK
shell@android:/ $ dumpsys input_method # → OK
shell@android:/ $ dumpsys uimode # → OK
shell@android:/ $ dumpsys download # → NG

結果が返ってきたものは「OK」、エラーになったものは「NG」としました。
幾つかはエラーになってしまいましたが、だいたいのものはこれで情報が取得できるようです。
出力される情報の意味もわかっていませんが、dumpsysはこうやって使うんですね。
英語をちゃんと読めばこういうことも書いてあるんでしょうか…。
こういったコマンドも開発には役立ちそうなので、使い方を覚えてみるのも良さそうですね。


タグ:android ADB dumpsys
posted by t2low at 01:26| Android

2013年12月16日

[Android] Android4.2.2での9patch画像表示不具合?

9path画像をTextViewの背景画像として使用していたのですが、Android4.2.2の端末で正しく表示されない現象が発生しました。
以下のような画像を表示した際に、中心部分の赤丸が右下に寄ってしまうのです。

flag.9.png

ヤバそうな気配を感じましたが、幸い回避方法を公開してくれている方がいました。


Android - 9patchでStretchable Areaを横2箇所設置時に同幅にスケールしない問題の解決方法 - Qiita [キータ]


この記事のとおりに画像の幅を奇数にしたところ正しく表示されるようになりました。

HTL22(4.2.2)(上が偶数サイズの画像、下が奇数サイズの画像)
htl22_ss.png

ISW12HT(4.0.3)(上が偶数サイズの画像、下が奇数サイズの画像)
isw12ht_ss.png

画像はありませんがNexus7(2012)(4.4.2)では、偶数サイズでも正常に表示されました。
Android4.2.2の不具合なのでしょうか。


あと、この件とは関係ないのですが、上記画像に「J」という文字が表示されていますが、どれも「Content Area」に表示されていません。
「Content Area」は「Stretchable Area」と重ならないといけないのでしたっけ。
テキトーな知識で進めるからこうなるのでしょうね…。


タグ:android 9patch
posted by t2low at 23:00| Android

2013年12月13日

[Android] Fragmentの中でFragmentを使う

Fragmentは機能をまとめておけて便利ですね。
少し前に、Fragmentの中でもFragmentを利用できることを知りました。
getChildFragmentManager()でFragmentManagerを取得すれば良いようです。
以下はサンプルです。

MainActivity.java
public class MainActivity extends FragmentActivity {

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

Fragment fragment = NestFragment.newInstance(0);
getSupportFragmentManager().beginTransaction().add(R.id.content, fragment).commit();
}

public static class NestFragment extends Fragment {
private static final String ARG_ID = "id";

public static NestFragment newInstance(int id) {
Bundle args = new Bundle();
args.putInt(ARG_ID, id);

NestFragment fragment = new NestFragment();
fragment.setArguments(args);
return fragment;
}

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.main, null);

int id = getArguments().getInt(ARG_ID);
if (id < 5) {
Fragment fragment = NestFragment.newInstance(id + 1);
getChildFragmentManager().beginTransaction().add(R.id.content, fragment).commit();
}
((TextView) view.findViewById(R.id.text)).setText(Integer.toString(id));

int colorId = ((id & 1) == 1) ? android.R.color.black : android.R.color.white;
view.setBackgroundColor(getResources().getColor(colorId));

return view;
}
}
}


layout/main.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".MainActivity" >

<TextView
android:id="@+id/text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="TEXT"
android:textColor="#888" />

<FrameLayout
android:id="@+id/content"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="16dp" >
</FrameLayout>

</LinearLayout>

表示するとこんな感じになります。(こんな使い方はしませんが)

device-2013-12-13-200829.png

posted by t2low at 23:00| Android

2013年12月12日

[Android] onLoadFinished()でDialogFragmentを表示する

データを読み込んで、ある条件に一致したらダイアログを表示しようと考え、以下のようなコードを書きました。
CursorLoaderを使ってデータを読み込んでいます。
条件を満たしていたら、onLoadFinished()でダイアログを表示します。
public class MainActivity extends FragmentActivity {

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

getSupportLoaderManager().initLoader(0, null, new LoaderCallbacks<Cursor>() {
@Override
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
return new CursorLoader(getApplication(), HOGE_URI, null, null, null, null);
}

@Override
public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) {
// .. 何らかの処理 ..

DialogFragment fragment = new MyDialogFragment();
fragment.show(getSupportFragmentManager(), "dialog");
}

@Override
public void onLoaderReset(Loader<Cursor> loader) {
}
});
}
}

しかし、このコードを実行すると以下の例外が発生し、アプリがクラッシュしてしまいました。

java.lang.IllegalStateException: Can not perform this action inside of onLoadFinished

どうやら、onLoadFinished()内でダイアログを表示してはいけないようです。
UIスレッド以外でUIの操作をしてはいけないという制限によるものなのかと、ダイアログ表示部分を以下のように書き換えてみました。
runOnUiThread(new Runnable() {
@Override
public void run() {
DialogFragment fragment = new MyDialogFragment();
fragment.show(getSupportFragmentManager(), "dialog");
}
});

これもダメでした。同じ例外が発生してクラッシュしてしまいます。
インターネット上で検索してみると、Handlerを使えばうまくいくらしいことがわかりました。
以下のようにします。
handler.post(new Runnable() {
@Override
public void run() {
DialogFragment fragment = new MyDialogFragment();
fragment.show(getSupportFragmentManager(), "dialog");
}
});

これなら動きました。

イマイチよくわかりません。
時間があれば、もう少し調べてみたいところですが、今はちょっと忙しいのでここまで。

posted by t2low at 22:00| Android

2013年12月11日

[Android] キーがintのMapについて

以下のようなコードを書くと…
Map<Integer, String> map = new HashMap<Integer, String>();
map.put(0, "ZERO");
map.put(1, "ONE");
map.put(2, "TWO");

Lintさんに怒られます。
「Use new SparseArray<String>(...) instead for better performance」
SparseArrayを使え、ということですね。↓こんな感じ。
SparseArray<String> map = new SparseArray<String>();
map.put(0, "ZERO");
map.put(1, "ONE");
map.put(2, "TWO");


また、調子に乗って、以下のように値がIntegerのSparseArrayを書くと…
SparseArray<Integer> map = new SparseArray<Integer>();
map.put(0, 10);
map.put(1, 100);
map.put(2, 1000);

やっぱりLintさんに怒られます。
「Use new SparseIntArray(...) instead for better performance」
SparseIntArrayを使えということですね。
SparseIntArray map = new SparseIntArray();
map.put(0, 10);
map.put(1, 100);
map.put(2, 1000);

少しでもアプリを良くしようとがんばってるLintさんステキです。

posted by t2low at 22:00| Android

2013年12月09日

[Android] Bitmapを回転する

アプリ内で画像(Bitmap)を回転しなければいけなくなりました。
カメラを使ったアプリを作ったときに一度実装したのですが、覚えていなかったので結局また調べて実装することに…。

Bitmap#createBitmap (Bitmap source, int x, int y, int width, int height, Matrix m, boolean filter) | Android Developers

使うのは↑このメソッドですね。
最後の引数のfilterは、変形する時にメソッド内部でPaint#setFilterBitmap()に渡す値のようです。
通常はtrueにしておくのが良いでしょう。
Bitmap orgBmp = BitmapFactory.decodeResource(getResources(), R.drawable.ic_launcher);

Matrix matrix = new Matrix();
matrix.postRotate(90);

Bitmap rotatedBmp = Bitmap.createBitmap(orgBmp, 0, 0, orgBmp.getWidth(), orgBmp.getHeight(), matrix, true);

こんな感じ。
回転しかしないならmatrix#setRotate()でも良いですね。
以上です。

posted by t2low at 22:00| Android

2013年12月05日

[Android] Android4.4搭載端末で操作動画を撮影する

Android4.4搭載端末であれば画面の録画ができると聞いて試してみました。

Android Debug Bridge | Android Developers

「adb shell」後に「screenrecord [ファイル名]」と実行すれば良いようです。
止めるには「Ctrl-C」ですね。
$ adb shell
shell@grouper:/ $ cd storage/sdcard0/tmp/
shell@grouper:/storage/sdcard0/tmp $ screenrecord test.mp4
^C
shell@grouper:/storage/sdcard0/tmp $ exit
$ adb pull /storage/sdcard0/tmp/test.mp4 ~/Documents/

保存したファイルは「adb pull」で取得出来ます。
Playストアに載せる動画を撮るのが簡単になりましたね。すごい。

タグ:android ADB
posted by t2low at 22:00| Android

2013年11月30日

[Android] IntentServiceを継承したServiceをBroadcastReceiverの中に書く

IntentServiceという便利なクラスがあります。
バックグラウンドで動作してくれて、処理が終わったら勝手に終了してくれるService(という認識)です。
BroadcastReceiver契機で時間のかかる処理を行いたいときに使うと便利ですね。

今回は特定のBroadcastReceiverからしか呼ばれない処理だっため、以下のようにIntentServiceを書いてみました。
public class HogeReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
// ... なんらかの処理

context.startService(new Intent(context, FugaService.class));
}

public static class FugaService extends IntentService {
public FugaService() {
super("FugaService");
}

@Override
protected void onHandleIntent(Intent intent) {
// ... バックグラウンドで行いたい処理
}
}
}


AndroidManifest.xmlはこんな感じ。
<receiver android:name=".HogeReceiver" />
<service android:name=".HogeReceiver$FugaService" />


ここでのBroadcastReceiverとServiceは2つで1つな感じなので、こんな感じで書いた方がつながりがわかって良いかな、と思った次第。
IntentServiceのクラスを外側にしても良いかもしれない。
BroadcastReceiver内に非同期処理が書ければこんな書き方しなくて済むのですけどね。

posted by t2low at 21:00| Android

2013年11月29日

[Android] android.intent.action.PACKAGE_〜

BroadcastReceiverで受信できる「PACKAGE_〜」のアクションについて調べてみました。
おこなったのは以下のIntentFilterを設定したBroadcastReceiverで受信したActionをログに表示するというもの。
Androidのソースを読んで詳細に調べたわけでなく、動作を確認しただけです。

<intent-filter>
<action android:name="android.intent.action.PACKAGE_ADDED" />
<action android:name="android.intent.action.PACKAGE_CHANGED" />
<action android:name="android.intent.action.PACKAGE_DATA_CLEARED" />
<action android:name="android.intent.action.PACKAGE_FIRST_LAUNCH" />
<action android:name="android.intent.action.PACKAGE_FULLY_REMOVED" />
<action android:name="android.intent.action.PACKAGE_INSTALL" />
<action android:name="android.intent.action.PACKAGE_NEEDS_VERIFICATION" />
<action android:name="android.intent.action.PACKAGE_REMOVED" />
<action android:name="android.intent.action.PACKAGE_REPLACED" />
<action android:name="android.intent.action.PACKAGE_RESTARTED" />
<action android:name="android.intent.action.PACKAGE_VERIFIED" />

<data android:scheme="package" />
</intent-filter>


確認した端末は以下の3つ。括弧内はAndroidのバージョンです。
・HTC Desire (2.3.3)
・HTC Evo 3D (4.0.3)
・HTC J One (4.2.2)

以下が「操作」とその時に受信した「Broadcast」です。

--------------------------------
■インストール
android.intent.action.PACKAGE_ADDED

■アップデート
android.intent.action.PACKAGE_REMOVED
android.intent.action.PACKAGE_ADDED
android.intent.action.PACKAGE_REPLACED

■アンインストール
android.intent.action.PACKAGE_REMOVED
android.intent.action.PACKAGE_FULLY_REMOVED (4.0以降のみ)

■強制停止
android.intent.action.PACKAGE_RESTARTED

■データ削除
android.intent.action.PACKAGE_RESTARTED
android.intent.action.PACKAGE_DATA_CLEARED

■無効化/有効化 (4.0以降のみ)
android.intent.action.PACKAGE_CHANGED (4.0以降のみ)

■SDカードへ移動/携帯端末へ移動
(なし)
--------------------------------

アップデートはADDEDもREMOVEDも受信するのがめんどくさいですね。
インストール/アップデート/アンインストールで何らかの処理を行いたい場合、単純にADDEDやREMOVED毎に処理を行ってしまうと、アップデート時には2回処理されてしまいます。
アップデート時は受信したIntentのExtraに「android.intent.extra.REPLACING」というキーで「true」が詰められているようなので、それを使って判断するのが良さそうです。
(Intentに EXTRA_REPLACING として定義されています)
あと「SDカードへの移動/携帯端末へ移動」も何かしらBroadcastされるかと思ってましたが何も来ませんでした。


また、以下はそれぞれのアクションがどのAPIレベルで追加されたかの一覧です。

android.intent.action.PACKAGE_ADDED (Lv1)
android.intent.action.PACKAGE_CHANGED (Lv1)
android.intent.action.PACKAGE_INSTALL (Lv1) (deprecated in Lv14)
android.intent.action.PACKAGE_REMOVED (Lv1)
android.intent.action.PACKAGE_RESTARTED (Lv1)
android.intent.action.PACKAGE_DATA_CLEARED (Lv3)
android.intent.action.PACKAGE_REPLACED (Lv3)
android.intent.action.PACKAGE_FIRST_LAUNCH (Lv12)
android.intent.action.PACKAGE_FULLY_REMOVED (Lv14)
android.intent.action.PACKAGE_NEEDS_VERIFICATION (Lv14)
android.intent.action.PACKAGE_VERIFIED (Lv17)


いくつか確認できてないActionがあります。
FIRST_LAUNCHなんて受信できたら何かおもしろそうなことが出来そうな気がしますが、今回確認した範囲では受信しなかったです。
何かpermissionでも必要なんでしょうか。

どの操作で、どのBroadcastが届くのか、また調べるのがめんどくさいのでメモとして残しました。
今回はここまで。

タグ:android
posted by t2low at 21:00| Android

2013年11月28日

[Android] SQLのUPDATEで更新された行数を取得する

AndroidのSQLiteDatabaseクラスで用意されているupdate()では無理そうなSQLを発行する必要がありました。
ContentProviderのupdate()内で呼ぶために、UPDATEで変更された行数を知らなければなりません。
しかし、SQLiteDatabaseクラスのexecSQL()は戻り値を返しません。
UPDATEを行う前に同条件で該当行数を調べておくべきでしょうか。

少し調べてみたところ、SQLiteStatement#executeUpdateDelete()を使えば変更された行数を返してくれるようです。
SQLiteStatementはSQLiteDatabase#compileStatement()で取得することが出来そうです。
しかし、このメソッドが追加されたのはAPI Level 11でした。
残念ながら現在開発中のアプリはAPI Level 9から対応させる予定のため、このメソッドは使えません。

もう少し調べてみたところ、SQLにchanges()というメソッドがあるようです。
UPDATEを実行した後、続けてchanges()のSQLを発行すれば良さそうです。
public static int changes(SQLiteDatabase db) {
Cursor cursor = null;
try {
cursor = db.rawQuery("select changes();", null);
if (cursor.moveToFirst()) {
return cursor.getInt(0);
}
} finally {
if (cursor != null) {
cursor.close();
}
}
return -1;
}

上記のようなメソッドを作成し試してみたところ、期待した結果が返ってきました。
(僕が知らないだけで標準のクラスやメソッドで出来たりするんだろうか…?)
とりあえずはこのメソッドを使っていこうと思います。


タグ:android SQLite
posted by t2low at 21:00| Android

2013年11月27日

[Android] IME以外からパスワードを入力させるときでも、文字がわかるようにする

パスワードを入力する画面を作っています。
この画面ではアプリが表示するキーボードを使うため、IMEは使いません。
キーボードが押される度に以下のようなコードを実行していました。
textView.setText(textView.getText() + moji);

この方法でも文字の追加は出来ますが、入力した文字がわかりません。
マスクされた状態で表示されてしまうのです。
通常であればパスワード入力欄は*や●でマスクされる前に1秒程度文字を確認できる時間があります。

特別な処理が必要かと考えましたが、なんてことはありませんでした。
TextViewに文字を追加するためのメソッドが用意されていました。
textView.append(moji);

これで期待通りの動きになりました。

今までTextViewの操作はほとんどgetText()/setText()で行っていたので、append()の存在に気付きませんでした。
思い込みはダメですね。


posted by t2low at 21:00| Android

2013年11月26日

[Android] 壁紙を取得する

Activityに壁紙を取得するメソッドがあったような気がする、ということで、Eclipseの補完機能を頼りにとりあえず使ってみました。
Drawable wallpaper = getWallpaper();

取得できました。ばんざーい。
一応、リファレンスを見てみます。

Context | Android Developers

API Level5でdeprecatedになっていました。
(Eclipseでは警告が出ないのは何故でしょう…?)
何にしても、WallpaperManagerを使った方が良いようです。
WallpaperManager wpm = WallpaperManager.getInstance(getApplication());
Drawable wallpaper = wpm.getDrawable();
// または
Drawable wallpaper = wpm.getFastDrawable();

これでも問題なく取得できました。
壁紙の取得は簡単ですね。
posted by t2low at 21:00| Android

2013年11月25日

[Android] DialogFragmentの呼び出し元で結果を知るには

ダイアログ内のユーザが入力した情報を、ダイアログを呼び出したFragment側で知る必要が出てきました。
つまり、コールバックする仕組みが欲しいと思ったのです。
public class HogeDialogFragment extends DialogFragment {
// ... HogeDialogFragmentの実装

public interface Callback {
void onFuga();
}
}

ダイアログは上記のようにDialogFragmentを継承して実装しています。
そのため、コンストラクタやメソッドを使ってCallbackを渡すのはよろしくなさそう(※)です。
(※システム側でFragmentが再生成されたときに云々という話がありますね)
他に良い方法があるか調べてみましたが、どうもそういった仕組みはないようです。

いろいろと調べて、これしかないかなぁ、というのが以下のような方法

1.9. DialogFragment 実装例 − Kojionilk

android - Callback to a Fragment from a DialogFragment - Stack Overflow

ただ、この方法だと呼び出す側のクラス(ActivityかFragment)がCallbackを実装しなければならないんですよね。
public class HogeHogeFragment extends Fragment 
implements HogeDialogFragment.Callback {
// ...HogeHogeFragmentの実装
}

うーん、うーん…。なんだかイマイチな…。でも、これしか方法がなさそうな…。うーん…。
posted by t2low at 21:00| Android