package android.webkit;
import android.annotation.NonNull;
import android.annotation.SystemApi;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Point;
import android.graphics.Rect;
import android.text.Editable;
import android.text.Selection;
import android.text.Spannable;
import android.text.TextWatcher;
import android.view.ActionMode;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.inputmethod.InputMethodManager;
import android.widget.EditText;
import android.widget.TextView;
@SystemApi
public class FindActionModeCallback implements ActionMode.Callback, TextWatcher,
View.OnClickListener, WebView.FindListener {
private View mCustomView;
private EditText mEditText;
private TextView mMatches;
private WebView mWebView;
private InputMethodManager mInput;
private Resources mResources;
private boolean mMatchesFound;
private int mNumberOfMatches;
private int mActiveMatchIndex;
private ActionMode mActionMode;
public FindActionModeCallback(Context context) {
mCustomView = LayoutInflater.from(context).inflate(
com.android.internal.R.layout.webview_find, null);
mEditText = (EditText) mCustomView.findViewById(
com.android.internal.R.id.edit);
mEditText.setCustomSelectionActionModeCallback(new NoAction());
mEditText.setOnClickListener(this);
setText("");
mMatches = (TextView) mCustomView.findViewById(
com.android.internal.R.id.matches);
mInput = context.getSystemService(InputMethodManager.class);
mResources = context.getResources();
}
public void finish() {
mActionMode.finish();
}
public void setText(String text) {
mEditText.setText(text);
Spannable span = (Spannable) mEditText.getText();
int length = span.length();
Selection.setSelection(span, length, length);
span.setSpan(this, 0, length, Spannable.SPAN_INCLUSIVE_INCLUSIVE);
mMatchesFound = false;
}
public void setWebView(@NonNull WebView webView) {
if (null == webView) {
throw new AssertionError("WebView supplied to "
+ "FindActionModeCallback cannot be null");
}
mWebView = webView;
mWebView.setFindDialogFindListener(this);
}
@Override
public void onFindResultReceived(int activeMatchOrdinal, int numberOfMatches,
boolean isDoneCounting) {
if (isDoneCounting) {
updateMatchCount(activeMatchOrdinal, numberOfMatches, numberOfMatches == 0);
}
}
private void findNext(boolean next) {
if (mWebView == null) {
throw new AssertionError(
"No WebView for FindActionModeCallback::findNext");
}
if (!mMatchesFound) {
findAll();
return;
}
if (0 == mNumberOfMatches) {
return;
}
mWebView.findNext(next);
updateMatchesString();
}
public void findAll() {
if (mWebView == null) {
throw new AssertionError(
"No WebView for FindActionModeCallback::findAll");
}
CharSequence find = mEditText.getText();
if (0 == find.length()) {
mWebView.clearMatches();
mMatches.setVisibility(View.GONE);
mMatchesFound = false;
mWebView.findAll(null);
} else {
mMatchesFound = true;
mMatches.setVisibility(View.INVISIBLE);
mNumberOfMatches = 0;
mWebView.findAllAsync(find.toString());
}
}
public void showSoftInput() {
if (mEditText.requestFocus()) {
mInput.showSoftInput(mEditText, 0);
}
}
public void updateMatchCount(int matchIndex, int matchCount, boolean isEmptyFind) {
if (!isEmptyFind) {
mNumberOfMatches = matchCount;
mActiveMatchIndex = matchIndex;
updateMatchesString();
} else {
mMatches.setVisibility(View.GONE);
mNumberOfMatches = 0;
}
}
private void updateMatchesString() {
if (mNumberOfMatches == 0) {
mMatches.setText(com.android.internal.R.string.no_matches);
} else {
mMatches.setText(mResources.getQuantityString(
com.android.internal.R.plurals.matches_found, mNumberOfMatches,
mActiveMatchIndex + 1, mNumberOfMatches));
}
mMatches.setVisibility(View.VISIBLE);
}
@Override
public void onClick(View v) {
findNext(true);
}
@Override
public boolean onCreateActionMode(ActionMode mode, Menu menu) {
if (!mode.isUiFocusable()) {
return false;
}
mode.setCustomView(mCustomView);
mode.getMenuInflater().inflate(com.android.internal.R.menu.webview_find,
menu);
mActionMode = mode;
Editable edit = mEditText.getText();
Selection.setSelection(edit, edit.length());
mMatches.setVisibility(View.GONE);
mMatchesFound = false;
mMatches.setText("0");
mEditText.requestFocus();
return true;
}
@Override
public void onDestroyActionMode(ActionMode mode) {
mActionMode = null;
mWebView.notifyFindDialogDismissed();
mWebView.setFindDialogFindListener(null);
mInput.hideSoftInputFromWindow(mWebView.getWindowToken(), 0);
}
@Override
public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
return false;
}
@Override
public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
if (mWebView == null) {
throw new AssertionError(
"No WebView for FindActionModeCallback::onActionItemClicked");
}
mInput.hideSoftInputFromWindow(mWebView.getWindowToken(), 0);
switch(item.getItemId()) {
case com.android.internal.R.id.find_prev:
findNext(false);
break;
case com.android.internal.R.id.find_next:
findNext(true);
break;
default:
return false;
}
return true;
}
@Override
public void beforeTextChanged(CharSequence s,
int start,
int count,
int after) {
}
@Override
public void onTextChanged(CharSequence s,
int start,
int before,
int count) {
findAll();
}
@Override
public void afterTextChanged(Editable s) {
}
private Rect mGlobalVisibleRect = new Rect();
private Point mGlobalVisibleOffset = new Point();
public int getActionModeGlobalBottom() {
if (mActionMode == null) {
return 0;
}
View view = (View) mCustomView.getParent();
if (view == null) {
view = mCustomView;
}
view.getGlobalVisibleRect(mGlobalVisibleRect, mGlobalVisibleOffset);
return mGlobalVisibleRect.bottom;
}
public static class NoAction implements ActionMode.Callback {
@Override
public boolean onCreateActionMode(ActionMode mode, Menu menu) {
return false;
}
@Override
public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
return false;
}
@Override
public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
return false;
}
@Override
public void onDestroyActionMode(ActionMode mode) {
}
}
}