javascriptからAndroidを呼び出す/Androidからjavascriptを呼び出す
javascript→Android
javascript interfaceを用意
適当なjavaオブジェクトでおっけー
今回はToastを表示するオブジェクト作りました
import android.content.Context; import android.widget.Toast; public class Toaster { private Context context; public Toaster(Context context) { this.context = context; } public void show(String text) { Toast.makeText(context, text, Toast.LENGTH_SHORT).show(); } }
WebViewにjavascript interfaceを追加する
WebViewのインスタンスを取得したらさっき作ったオブジェクトを追加します
@Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); // WebView取得 browser = (WebView) findViewById(R.id.browser); // javascript有効化 browser.getSettings().setJavaScriptEnabled(true); // Toastを表示するjavascript interfaceを追加 // 第一引数がオブジェクトで、第二引数がjavascriptから呼び出すときの名前 browser.addJavascriptInterface(new Toaster(this), Toaster.class.getSimpleName()); // assetのindex.htmlを読み込み browser.loadUrl("file:///android_asset/index.html"); }
javascriptから呼び出す
あとはこんな感じで呼び出せます
<input type="button" value="click me" onclick="Toaster.show('Hello, World!')" />
Android→javascript
html側に実行するjavascriptを用意
javascriptの関数書きます
抜粋するとこんな感じ
<head> <script type="text/javascript"> function change_message(message){ document.getElementById('message').innerHTML = message; } </script> </head> <body> <p id="message"></p> </body>
alert()でもよかったんですが、android-webviewのalertは表示部分がJavaになって分かりづらいので
今回はテキストを変更する関数を書きました。
Androidから呼び出す
とっても簡単、loadUrlにjavascript書くだけ
browser.loadUrl("javascript:change_message('HogeHoge')");
Screen Capture Shortcutをリリースしました
残念な仕様
Galaxyシリーズにはスクリーンキャプチャを撮るための仕組みがついています。
GalaxySであれば戻るボタンを押しながらホームボタンを、
GalaxyTabであれば戻るボタンを押しながら電源ボタンを押せばスクリーンキャプチャが撮れます。
どちらも戻るボタンを押さねばなりません。
そこでいざスクリーンキャプチャを撮ろうとすると今の画面が取りたいのに前の画面へ戻ってしまいます。
戻る押してるんだから当然ですね…。
そこで
戻るボタンとかホームボタンとか押さなくてもスクリーンキャプチャを呼び出せるアプリを作りました!
使用方法は2通りです。
・notificationバーから呼び出す
・端末を振る(有料版のみ)
これで
いろんな画面を簡単にキャプチャーできますねー。
Launcherからアプリを消したい
だれか助けてください
今書いているアプリでどうしても必要な機能なのですが、どうにも上手くうごきません…、だれか助けてください…
Launcherから消し去りたい
アプリのLauncher表示の切り替えをしたくてPackageManager#setComponentEnabledSettingを使い切り替えるコードを書きました。
PackageManager#setComponentEnabledSettingについてはこちら
Taosoftware: Android Intent呼び出しを自分でコントロール方法 http://www.taosoftware.co.jp/blog/2010/04/android_intent.html
taosoftwareさんではACTION_VIEW/CATEGORY_BROWSABLEのActivityの切り替えをしていますが、ACTION_MAIN/CATEGORY_LAUNCHERのActivityの切り替えをしたらLauncherからの非表示ができるのではないかと考えたわけです。
検証用のコードはこちら
SettingActivity
import android.content.ComponentName; import android.content.SharedPreferences; import android.content.SharedPreferences.OnSharedPreferenceChangeListener; import android.content.pm.PackageManager; import android.os.Bundle; import android.preference.PreferenceActivity; import android.preference.PreferenceManager; public class SettingActivity extends PreferenceActivity implements OnSharedPreferenceChangeListener { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); addPreferencesFromResource(R.xml.setting); int state = getPackageManager().getComponentEnabledSetting(new ComponentName(this, SettingActivity.class)); if (state == PackageManager.COMPONENT_ENABLED_STATE_ENABLED) { PreferenceManager.getDefaultSharedPreferences(this).edit().putBoolean("visible_in_launcher_1", true); } else { PreferenceManager.getDefaultSharedPreferences(this).edit().putBoolean("visible_in_launcher_1", false); } state = getPackageManager().getComponentEnabledSetting(new ComponentName(this, SettingActivity.class)); if (state == PackageManager.COMPONENT_ENABLED_STATE_ENABLED) { PreferenceManager.getDefaultSharedPreferences(this).edit().putBoolean("visible_in_launcher_2", true); } else { PreferenceManager.getDefaultSharedPreferences(this).edit().putBoolean("visible_in_launcher_2", false); } } @Override protected void onResume() { super.onResume(); PreferenceManager.getDefaultSharedPreferences(this).registerOnSharedPreferenceChangeListener(this); } @Override protected void onPause() { super.onPause(); PreferenceManager.getDefaultSharedPreferences(this).unregisterOnSharedPreferenceChangeListener(this); } @Override public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) { int newState; if (sharedPreferences.getBoolean(key, true)) { newState = PackageManager.COMPONENT_ENABLED_STATE_ENABLED; } else { newState = PackageManager.COMPONENT_ENABLED_STATE_DISABLED; } String packageName = getPackageName(); ComponentName componentName; if (key.equals("visible_in_launcher_1")) { componentName = new ComponentName(packageName, packageName + ".SettingActivity"); } else { componentName = new ComponentName(packageName, packageName + ".SettingActivityAlias"); } PackageManager packageManager = getPackageManager(); packageManager.setComponentEnabledSetting(componentName, newState, PackageManager.DONT_KILL_APP); } }
AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="jp.tomorrowkey.android.visibleinlauncherapp" android:versionCode="1" android:versionName="1.0"> <application android:icon="@drawable/icon" android:label="@string/app_name"> <activity android:name=".SettingActivity" android:label="App1" android:enabled="true"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> <activity-alias android:name=".SettingActivityAlias" android:targetActivity=".SettingActivity" android:label="App2" android:enabled="true"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity-alias> <receiver android:name=".PackageChangedReceiver"> <intent-filter> <action android:name="android.intent.action.PACKAGE_CHANGED" /> <data android:scheme="package" /> </intent-filter> </receiver> </application> <uses-sdk android:minSdkVersion="3" /> </manifest>
ちょっとわけあってsetComponentEnabledSettingでenableを切り替えるActivityを2つにしています。
また、setComponentEnabledSettingを使って変更をするとPACKAGE_CHANGEDがブロードキャストされるので、ブロードキャストされたことを分かりやすく見せるためにToastを表示するようにしています。
package jp.tomorrowkey.android.visibleinlauncherapp; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.widget.Toast; public class PackageChangedReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { Toast.makeText(context, "receive package changed", Toast.LENGTH_SHORT).show(); } }
動かない…
まず、Launcherの状態がこちら
アプリを起動するとこんな画面が表示されます。
1のチェックボックスを外すと
PACKAGE_CHANGEDが受信され
LauncherからApp1のリンクが消えます。
思ったように動きます。
ここからが問題です。
この状態でApp2のチェックボックスを外すと…
PACKAGE_CHANGEDが受信され
Launcherから消えない…
なぜか消えません…
ちなみに起動しようとしてみると
「アプリがインストールされてません」って言われます…
どうも、最後の1つは消えてくれないようです。
たすけてください
どうにかしてLauncherから消す方法はないでしょうか…。
知っている方おしえてください…
ある程度時間が経過したらプログレスダイアログを表示する
最初からプログレスを表示せずにある程度時間が経ったらプログレスダイアログを表示します。
処理時間がまちまちな時に使えるんじゃないかなと思います。
onPostExecuteでプログレス非表示/メッセージキャンセルのif文がこんなので大丈夫か不安です。
初めてメッセージ使った。
AsyncTaskソース
処理が2秒以上掛かる場合はプログレスを表示します。
class WaitTask extends AsyncTask<Integer, Void, Integer> { /* プログレスが表示されるまでの閾値 */ private static final int PROGRESS_DELAY = 2000; /* Message識別*/ private final int MESSAGE_WHAT = 100; private Context context; private ProgressDialog progressDialog = null; private Handler handler; public WaitTask(Context context) { this.context = context; handler = new Handler() { @Override public void handleMessage(Message msg) { progressDialog = new ProgressDialog(WaitTask.this.context); progressDialog.setMessage("please wait"); progressDialog.show(); } }; } @Override protected Integer doInBackground(Integer... params) { Message msg = new Message(); msg.what = MESSAGE_WHAT; handler.sendMessageDelayed(msg, PROGRESS_DELAY); try { //何か時間が掛かる処理 Thread.sleep(params[0]); } catch (InterruptedException e) { Log.d("DelayedProgress", e.getMessage(), e); } return params[0]; } @Override protected void onPostExecute(Integer result) { if (progressDialog != null && progressDialog.isShowing()) { progressDialog.dismiss(); } else { handler.removeMessages(MESSAGE_WHAT); } Toast.makeText(context, String.format("%d second has passed", result), Toast.LENGTH_LONG).show(); } }
無料アプリと有料アプリのプロジェクトを管理する
アプリを作った時に無料アプリと有料アプリと2バージョン作る事ってよくあると思います。
無料アプリと有料アプリに分けたい場合、パッケージ名を変えないといけないのでプロジェクトを2つ立てる必要があります。
その場合、ソースが2重管理になってしまい、リリース後の改修が非常にめんどくださいです。
これはデコ美のバージョンアップでだいぶ苦しめられました…。
これを楽にしようと考えて方法を考えました。
プロジェクト作る
Androidライブラリを使うとソースが1つになり、管理が楽になります。
プロジェクトは合計3つ作ります。
方針
AndroidAppCodeにすべてのコードを書きます。
AndroidPayAppとAndroidFreeAppの2つのアプリはAndroidAppCoreを参照します。
AndroidPayAppとAndroidFreeAppの2つのプロジェクトには一切ソースは置きません。
AndroidManifest.xml
AndroidPayAppとAndroidFreeAppのAndroidManifestにはAndroidAppCoreのActivityの定義を書きます。
今回は1つのActivityのみなので以下のようになります。
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="jp.tomorrowkey.android.androidpayapp" android:versionCode="1" android:versionName="1.0"> <application android:icon="@drawable/icon" android:label="@string/app_name"> <activity android:name="jp.tomorrowkey.android.androidlib.MainActivity"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> </application> <uses-sdk android:minSdkVersion="4" /> </manifest>
掲載したのはAndroidPayAppのAndroidManifestです。
AndroidFreeAppのAndroidManifestはほぼ一緒なので載せません。
これでアプリからライブラリに定義されているActivityが呼び出せます。
無料アプリと有料アプリの判断
無料アプリは機能に制限をもたせないといけないので、無料アプリと有料アプリの判断をするコードが必要です。
Context#getPackageName()で有料アプリ、無料アプリそれぞれのパッケージ名が返ってくるのでそれで判断します。
Utilクラスでも作っておきましょう。
import android.content.Context; public class Util { private static final String PAY_APP_PACKAGE_NAME = "jp.tomorrowkey.android.androidpayapp"; public static boolean isPaymentApp(Context context) { String packageName = context.getPackageName(); return PAY_APP_PACKAGE_NAME.equals(packageName); } }
動作
アイコンとアプリ名はAndroidManifestにそれぞれ定義するので
無料アプリと有料アプリで別のものを使用することができます。
今回は無料アプリにグレーのアイコンを、有料アプリに緑色のアイコンを使いました。
それぞれのアプリを実行しました。
パッケージ名から無料版・有料版の判断をしてそれぞれの文字列を出力しています。
サンプルソース
今回検証用に作ったソースはここにあります
http://code.google.com/p/tomorrowkey/source/browse/#svn/trunk/FreeAndPaymentSourceControl
これで
かなり管理が楽に!
もう何も考えなくていいよう!
Androidでカラーピッカーを作ろう
HSVって?
HSV色空間 - Wikipedia http://ja.wikipedia.org/wiki/HSV%E8%89%B2%E7%A9%BA%E9%96%93
- Hue - 色相
- Saturation - 彩度
- Value - 明度
の3つで表す色の指定方法です。
RGBの親戚みたいなものです。
wikipedia読むとなにやら難しい事書いてあります。
ぼくはあんまりよんでません。
カラーピッカーを作りたい
wikipediaに書いてある事をぜんぶ読まなくても
AndroidにはHSV⇔RGBのメソッドが用意されているんで読まなくてもいいです。
理解するのは値の範囲くらい
色相 | 0〜360 |
彩度 | 0%〜100% |
明度 | 0%〜100% |
使うメソッドはColor#HSVToColor(float[])てやつ
Color | Android Developers http://developer.android.com/reference/android/graphics/Color.html#HSVToColor(float[])
配列にHSVの値を入れたらRGBを返してくれるメソッドです。
色相バーを作ろう
色相の値を1ずつずらして色を並べれば色相バーが作れます。
コード
import android.app.Activity; import android.graphics.Bitmap; import android.graphics.Color; import android.graphics.Bitmap.Config; import android.os.Bundle; import android.widget.ImageView; public class MainActivity extends Activity { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // 画像を作る int height = 60; int width = 360; Bitmap bitmap = Bitmap.createBitmap(width, height, Config.ARGB_8888); int color; // 色を設定する for (int i = 0; i < 360; i++) { // 色相に対応する色を取得する color = Color.HSVToColor(new float[] { i, 1, 1 }); // 画像に色を設定する for (int j = 0; j < height; j++) { bitmap.setPixel(i, j, color); } } // イメージをActivityに表示する ImageView imageView = new ImageView(this); imageView.setImageBitmap(bitmap); setContentView(imageView); } }
彩度や明度
色相とほぼ同じコードで作れると思います。
がんばって!
カラーピッカー
私はこんな感じのカラーピッカー作りました。
ソースはこちら
http://tomorrowkey.googlecode.com/svn/trunk/ColorPallet
すぐ動くサンプルはこちら
http://tomorrowkey.googlecode.com/svn/trunk/ColorPallet/bin/ColorPallet.apk
署名してません。