Hatena::ブログ(Diary)

明日の鍵

2011-01-25

暗号本メモ

[twitter:@inuchin] さんオススメ

図解入門よくわかる最新暗号技術の基本と仕組み (How‐nual Visual Guide Book)

図解入門よくわかる最新暗号技術の基本と仕組み (How‐nual Visual Guide Book)

  • 古いけど入門書にいいらしい
新版暗号技術入門 秘密の国のアリス

新版暗号技術入門 秘密の国のアリス

  • レビューもいい感じだし、ポチッた

[twitter:@hamatz] さんオススメ

  • 辞書的に使える
  • かなりオススメらしい
  • 詳しくなったらほしいな
  • 欠点:重い
  • 入門に良さそう?
  • レビューがなくて判断しづらい
暗号解読―ロゼッタストーンから量子暗号まで

暗号解読―ロゼッタストーンから量子暗号まで

[twitter:@keiji_ariyama] さんオススメ

Javaで作って学ぶ暗号技術 - RSA,AES,SHAの基礎からSSLまで

Javaで作って学ぶ暗号技術 - RSA,AES,SHAの基礎からSSLまで

  • javaで実装されてるならコード読めるし、分かりやすいかも
改訂新版 暗号の数理―作り方と解読の原理 (ブルーバックス)

改訂新版 暗号の数理―作り方と解読の原理 (ブルーバックス)

  • 内容がきになる。
  • 暗号解読と同じく歴史が読めるかも?

2011-01-16

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!')" />
実行イメージ

f:id:tomorrowkey:20110116111430p:image:w200

ボタンを押すと…

f:id:tomorrowkey:20110116111431p:image:w200

Toastが表示されました!

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')");
実行イメージ

f:id:tomorrowkey:20110116111432p:image:w200

メニューを押すと

f:id:tomorrowkey:20110116111433p:image:w200

テキストが変わりました!

ソースとか

ソース

http://code.google.com/p/tomorrowkey/source/browse/#svn%2Ftrunk%2FWebViewApp

apk

http://tomorrowkey.googlecode.com/svn/trunk/WebViewApp/bin/WebViewApp.apk

2010-12-07

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の状態がこちら

f:id:tomorrowkey:20101207070948p:image:w200

アプリを起動するとこんな画面が表示されます。

f:id:tomorrowkey:20101207070947p:image:w200

1のチェックボックスを外すと

f:id:tomorrowkey:20101207070949p:image:w200

PACKAGE_CHANGEDが受信され

f:id:tomorrowkey:20101207070951p:image:w200

LauncherからApp1のリンクが消えます。

f:id:tomorrowkey:20101207070950p:image:w200

思ったように動きます。

ここからが問題です。

この状態でApp2のチェックボックスを外すと…

f:id:tomorrowkey:20101207070952p:image:w200

PACKAGE_CHANGEDが受信され

f:id:tomorrowkey:20101207070953p:image:w200

Launcherから消えない…

f:id:tomorrowkey:20101207070954p:image:w200

なぜか消えません…

ちなみに起動しようとしてみると

f:id:tomorrowkey:20101207070955p:image:w200

「アプリがインストールされてません」って言われます…

どうも、最後の1つは消えてくれないようです。

検証Homeアプリ
  • GalaxyS純正Launcher
    • 消えない
  • HT-03A純正Launcher
    • 消えない
  • ADWLauncher
    • 消えない
  • Xperia(1.6)純正Launcher
    • 消える
  • Xperia mini pro(1.6)純正Launcher
    • 1つも消えない

たすけてください

どうにかしてLauncherから消す方法はないでしょうか…。

知っている方おしえてください…

apk

VisibleInLauncherApp.apk 直

コード

http://code.google.com/p/tomorrowkey/source/browse/#svn/trunk/VisibleInLauncherApp

2010-10-26

ある程度時間が経過したらプログレスダイアログを表示する

最初からプログレスを表示せずにある程度時間が経ったらプログレスダイアログを表示します。

処理時間がまちまちな時に使えるんじゃないかなと思います。

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();
  }
}

全ソース

http://code.google.com/p/tomorrowkey/source/browse/#svn/trunk/DelayedProgress

2010-10-20

無料アプリと有料アプリのプロジェクトを管理する

アプリを作った時に無料アプリと有料アプリと2バージョン作る事ってよくあると思います。

無料アプリと有料アプリに分けたい場合、パッケージ名を変えないといけないのでプロジェクトを2つ立てる必要があります。

その場合、ソースが2重管理になってしまい、リリース後の改修が非常にめんどくださいです。

これはデコ美のバージョンアップでだいぶ苦しめられました…。

これを楽にしようと考えて方法を考えました。

プロジェクト作る

Androidライブラリを使うとソースが1つになり、管理が楽になります。

プロジェクトは合計3つ作ります。

  • AndroidPayApp(Androidアプリ)
    • 有料アプリ
    • package:jp.tomorrowkey.android.androidpayapp
  • AndroidFreeApp(Androidアプリ)
    • 無料アプリ
    • package:jp.tomorrowkey.android.androidfreeapp
  • AndroidAppCore(Androidライブラリ)
    • 実装コード
    • package:jp.tomorrowkey.android.androidcoreapp

方針

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);
  }
}

動作

f:id:tomorrowkey:20101020225601p:image

アイコンとアプリ名はAndroidManifestにそれぞれ定義するので

無料アプリと有料アプリで別のものを使用することができます。

今回は無料アプリにグレーのアイコンを、有料アプリに緑色のアイコンを使いました。

f:id:tomorrowkey:20101020225602p:image

f:id:tomorrowkey:20101020225603p:image

それぞれのアプリを実行しました。

パッケージ名から無料版・有料版の判断をしてそれぞれの文字列を出力しています。

サンプルソース

今回検証用に作ったソースはここにあります

http://code.google.com/p/tomorrowkey/source/browse/#svn/trunk/FreeAndPaymentSourceControl

これで

かなり管理が楽に!

もう何も考えなくていいよう!