blob: 72aae48db15f321cfafe34c4c79aff0b3526d2a3 [file] [log] [blame]
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package org.chromium.content.browser.selection;
import android.annotation.TargetApi;
import android.app.PendingIntent;
import android.app.RemoteAction;
import android.content.Context;
import android.os.Build;
import android.os.Handler;
import android.text.TextUtils;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.textclassifier.TextClassification;
import org.chromium.base.Log;
import org.chromium.base.annotations.VerifiesOnP;
import java.util.HashMap;
import java.util.Map;
// TODO(ctzsm): Add unit tests for this class once this is upstreamed.
/**
* Implements AdditionalMenuItemProvider interface.
* We prevent inlinings since this uses a number of new Android APIs which would create verification
* errors (on older Android versions) which would require a slow re-verification at runtime.
*/
@VerifiesOnP
@TargetApi(Build.VERSION_CODES.P)
public class AdditionalMenuItemProviderImpl implements AdditionalMenuItemProvider {
private static final String TAG = "MenuItemProvider";
// We want the secondary assist actions to come after the default actions but before the text
// processing actions. This constant needs to be greater than all of the default action orders
// but small enough so that all of the secondary items have order less than
// MENU_ITEM_ORDER_TEXT_PROCESS_START in SelectionPopupControllerImpl.
private static final int MENU_ITEM_ORDER_SECONDARY_ASSIST_ACTIONS_START = 50;
// Record MenuItem OnClickListener pair we added to menu.
private final Map<MenuItem, OnClickListener> mAssistClickHandlers = new HashMap<>();
@Override
public void addMenuItems(Context context, Menu menu, TextClassification classification) {
if (menu == null || classification == null) return;
final int count = classification.getActions().size();
// Fallback to new API to set icon on P.
if (count > 0) {
RemoteAction primaryAction = classification.getActions().get(0);
MenuItem item = menu.findItem(android.R.id.textAssist);
if (primaryAction.shouldShowIcon()) {
setItemIconFromRemoteAction(context, primaryAction, item);
} else {
item.setIcon(null);
}
}
// First action is reserved for primary action.
for (int i = 1; i < count; ++i) {
RemoteAction action = classification.getActions().get(i);
final OnClickListener listener =
getSupportedOnClickListener(action.getTitle(), action.getActionIntent());
if (listener == null) continue;
// We have to use android.R.id.textAssist as group id to make framework show icons for
// these menu items.
MenuItem item = menu.add(android.R.id.textAssist, Menu.NONE,
MENU_ITEM_ORDER_SECONDARY_ASSIST_ACTIONS_START + i, action.getTitle());
item.setContentDescription(action.getContentDescription());
if (action.shouldShowIcon()) {
setItemIconFromRemoteAction(context, action, item);
}
// Set this flag to SHOW_AS_ACTION_IF_ROOM to match text processing menu items. So
// Android could put them to the same level and then consider their actual order.
item.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
mAssistClickHandlers.put(item, listener);
}
}
@Override
public void clearMenuItemListeners() {
mAssistClickHandlers.clear();
}
@Override
public void performAction(MenuItem item, View view) {
OnClickListener listener = mAssistClickHandlers.get(item);
if (listener == null) return;
listener.onClick(view);
}
private static OnClickListener getSupportedOnClickListener(
CharSequence title, PendingIntent pendingIntent) {
if (TextUtils.isEmpty(title) || pendingIntent == null) return null;
return v -> {
try {
pendingIntent.send();
} catch (PendingIntent.CanceledException e) {
Log.e(TAG, "Error creating OnClickListener from PendingIntent", e);
}
};
}
private static void setItemIconFromRemoteAction(
Context context, RemoteAction action, MenuItem item) {
action.getIcon().loadDrawableAsync(context, item::setIcon, new Handler());
}
}