アプリ内翻訳機能がついたTextViewをザクッと作ってみる話

  • 5
    いいね
  • 0
    コメント
この記事は最終更新日から1年以上が経過しています。

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()でターゲットを絞るなどの工夫が必要です。

とりあえず、試す

試してみました。

device-2015-12-18-173945.png

新規作成時にScrolling Activityを選択すると作られるアクティビティのTextViewをTranslatableTextViewに置き換えてあります。

device-2015-12-18-174004.png

長押しして選択すると、メニューに「翻訳」が含まれています。

device-2015-12-18-174022.png

はい、翻訳できました。
EXTRA_PROCESS_TEXT_READONLYでfalseを渡しているのに、「置換」があるのがいまいちです。

試した環境では、ACTION_PROCESS_TEXTに対応しているのがGoogle翻訳しかなかったので、素直に翻訳してくれました。

まとめ

要点は、

  • テキスト選択したときのコンテキスト・メニューの表示
  • ACTION_PROCESS_TEXTなインテントを使ったアプリ間連携

の2点で、それぞれはさほど大変なことではないので、細かいことを考慮しなければザクッと作れます。

ACTION_PROCESS_TEXTが加わったことで、Androidらしいアプリ間連携の幅が広がったので、今後、対応アプリが増えてくることが期待できますね。