MarshmallowにGoogle翻訳アプリを入れておけば、テキスト選択機能で、アプリ内翻訳ができるよという記事があったので、何もしなくてもサクッと使えるのかと思ったら、そうでもなかったので、翻訳機能付きのTextViewをザクッと作ってみました(厳密には翻訳専用というわけじゃないんだけど)。
とりあえず、ソース
public class TranslatableTextView extends TextView {
public TranslatableTextView(Context context) {
super(context);
init();
}
public TranslatableTextView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public TranslatableTextView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
/**
* 共通した初期化処理
*/
private void init() {
// Marshmallow以降のときだけはたらく
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
// 強制的にセレクタブル
setTextIsSelectable(true);
// カスタムアクションを設定
setCustomSelectionActionModeCallback(new TranslateCallback());
}
}
/**
* @return 選択されている文字列
*/
CharSequence selectedText() {
int start = getSelectionStart();
int end = getSelectionEnd();
return getText().subSequence(start, end);
}
@TargetApi(Build.VERSION_CODES.M)
private class TranslateCallback extends ActionMode.Callback2 {
@Override
public boolean onCreateActionMode(ActionMode mode, Menu menu) {
if (getSupportedActivities().size() > 0) {
// 本当の翻訳だけなのかというのは議論の余地がある
menu.add("翻訳");
return true;
} else {
return false;
}
}
@Override
public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
return false;
}
@Override
public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
if (item.getTitle().equals(TRANSLATE)) {
// ACTION_PROCESS_TEXTなインテントを投げる
getContext().startActivity(createProcessTextIntent(selectedText()));
// ActionMode終わり
mode.finish();
return true;
} else {
return false;
}
}
@Override
public void onDestroyActionMode(ActionMode mode) {}
/**
* ACTION_PROCESS_TEXTなインテント
*
* @param text nullで無ければ、processの対象
* @return インテント
*/
private Intent createProcessTextIntent(@Nullable CharSequence text) {
Intent intent = new Intent();
intent.setAction(Intent.ACTION_PROCESS_TEXT);
intent.setType("text/plain");
if (text != null) {
intent.putExtra(Intent.EXTRA_PROCESS_TEXT, text);
intent.putExtra(Intent.EXTRA_PROCESS_TEXT_READONLY, false);
}
return intent;
}
/**
* ACTION_PROCESS_TEXTに反応するアクティビティのリストを取得するメソッド
* ここでは、その数しか使用してないけど
*
* @return ACTION_PROCESS_TEXTに反応するアクティビティのリスト
*/
private List<ResolveInfo> getSupportedActivities() {
PackageManager manager = getContext().getPackageManager();
return manager.queryIntentActivities(createProcessTextIntent(null), 0);
}
}
}
Marshmallowで追加されたActionMode.Callback2から、派生クラスを作って、そこでやりたいことをカスタマイズします。そのインスタンスをsetCustomSelectionActionModeCallback()で渡してやれば、カスタマイズされたコンテキスト・メニューが表示されます。
メニューがタップされたら、選択されている文字列をExtraに格納したACTION_PROCESS_TEXTなインテントを投げています。ACTION_PROCESS_TEXTは、翻訳専用というアクションではないので、対応したアプリがインストールされている場合は、なにか別のことをされてしまうかもしれないので、本当ならResolveInfoのリストを元にIntent#setClassName()でターゲットを絞るなどの工夫が必要です。
とりあえず、試す
試してみました。
新規作成時にScrolling Activityを選択すると作られるアクティビティのTextViewをTranslatableTextViewに置き換えてあります。
長押しして選択すると、メニューに「翻訳」が含まれています。
はい、翻訳できました。
EXTRA_PROCESS_TEXT_READONLYでfalseを渡しているのに、「置換」があるのがいまいちです。
試した環境では、ACTION_PROCESS_TEXTに対応しているのがGoogle翻訳しかなかったので、素直に翻訳してくれました。
まとめ
要点は、
- テキスト選択したときのコンテキスト・メニューの表示
- ACTION_PROCESS_TEXTなインテントを使ったアプリ間連携
の2点で、それぞれはさほど大変なことではないので、細かいことを考慮しなければザクッと作れます。
ACTION_PROCESS_TEXTが加わったことで、Androidらしいアプリ間連携の幅が広がったので、今後、対応アプリが増えてくることが期待できますね。