summaryrefslogtreecommitdiffstats
path: root/src-mailcommon
diff options
context:
space:
mode:
authorMindy Pereira <mindyp@google.com>2011-12-13 10:12:27 -0800
committerMindy Pereira <mindyp@google.com>2011-12-13 10:24:52 -0800
commit1a73ea8bc1b08ff4e7baac515ed54a15de13ffe2 (patch)
treeebb121487dcdce3625f9bd782aa07e9f4507e72c /src-mailcommon
parent0b2aaf43159dbb2d4627e687c21b226b6d61a5a1 (diff)
downloadandroid_packages_apps_UnifiedEmail-1a73ea8bc1b08ff4e7baac515ed54a15de13ffe2.tar.gz
android_packages_apps_UnifiedEmail-1a73ea8bc1b08ff4e7baac515ed54a15de13ffe2.tar.bz2
android_packages_apps_UnifiedEmail-1a73ea8bc1b08ff4e7baac515ed54a15de13ffe2.zip
Move mailcommon code to our new unified code base.
Change-Id: I1cf861371a54bb9af97b1952af8e7a44c2856f5b
Diffstat (limited to 'src-mailcommon')
-rw-r--r--src-mailcommon/com/android/mailcommon/MergedAdapter.java193
-rw-r--r--src-mailcommon/com/android/mailcommon/MultiAdapterSpinner.java186
-rw-r--r--src-mailcommon/com/android/mailcommon/WebViewContextMenu.java349
3 files changed, 728 insertions, 0 deletions
diff --git a/src-mailcommon/com/android/mailcommon/MergedAdapter.java b/src-mailcommon/com/android/mailcommon/MergedAdapter.java
new file mode 100644
index 000000000..77f8327f2
--- /dev/null
+++ b/src-mailcommon/com/android/mailcommon/MergedAdapter.java
@@ -0,0 +1,193 @@
+/*
+ * Copyright (C) 2011 Google Inc.
+ * Licensed to The Android Open Source Project.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package com.android.mailcommon;
+
+import android.database.DataSetObserver;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.BaseAdapter;
+import android.widget.ListAdapter;
+import android.widget.SpinnerAdapter;
+
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * An adapter that combines items from multiple provided adapters into a single list.
+ *
+ * @param <T> the class of each constituent adapter
+ */
+public class MergedAdapter<T extends MergedAdapter.ListSpinnerAdapter> extends BaseAdapter {
+
+ private List<T> mAdapters;
+ private final DataSetObserver mObserver;
+
+ /**
+ * A Mergeable adapter must implement both ListAdapter and SpinnerAdapter to be useful in lists
+ * and spinners.
+ */
+ public interface ListSpinnerAdapter extends ListAdapter, SpinnerAdapter {
+ }
+
+ public static class LocalAdapterPosition<T extends ListSpinnerAdapter> {
+ public final T mAdapter;
+ public final int mLocalPosition;
+
+ public LocalAdapterPosition(T adapter, int offset) {
+ mAdapter = adapter;
+ mLocalPosition = offset;
+ }
+ }
+
+ public MergedAdapter() {
+ mObserver = new DataSetObserver() {
+ @Override
+ public void onChanged() {
+ notifyDataSetChanged();
+ }
+ };
+ }
+
+ public void setAdapters(T... adapters) {
+ if (mAdapters != null) {
+ for (T adapter : mAdapters) {
+ adapter.unregisterDataSetObserver(mObserver);
+ }
+ }
+
+ mAdapters = Arrays.asList(adapters);
+
+ for (T adapter : mAdapters) {
+ adapter.registerDataSetObserver(mObserver);
+ }
+ }
+
+ public int getSubAdapterCount() {
+ return mAdapters.size();
+ }
+
+ public T getSubAdapter(int index) {
+ return mAdapters.get(index);
+ }
+
+ @Override
+ public int getCount() {
+ int count = 0;
+ for (T adapter : mAdapters) {
+ count += adapter.getCount();
+ }
+ return count;
+ // TODO: cache counts until next onChanged
+ }
+
+ /**
+ * For a given merged position, find the corresponding Adapter and local position within that
+ * Adapter by iterating through Adapters and summing their counts until the merged position is
+ * found.
+ *
+ * @param position a merged (global) position
+ * @return the matching Adapter and local position, or null if not found
+ */
+ public LocalAdapterPosition<T> getAdapterOffsetForItem(final int position) {
+ final int adapterCount = mAdapters.size();
+ int i = 0;
+ int count = 0;
+
+ while (i < adapterCount) {
+ T a = mAdapters.get(i);
+ int newCount = count + a.getCount();
+ if (position < newCount) {
+ return new LocalAdapterPosition<T>(a, position - count);
+ }
+ count = newCount;
+ i++;
+ }
+ return null;
+ }
+
+ @Override
+ public Object getItem(int position) {
+ LocalAdapterPosition<T> result = getAdapterOffsetForItem(position);
+ if (result == null) {
+ return null;
+ }
+ return result.mAdapter.getItem(result.mLocalPosition);
+ }
+
+ @Override
+ public long getItemId(int position) {
+ return position;
+ }
+
+ @Override
+ public int getViewTypeCount() {
+ int count = 0;
+ for (T adapter : mAdapters) {
+ count += adapter.getViewTypeCount();
+ }
+ return count;
+ }
+
+ @Override
+ public int getItemViewType(int position) {
+ LocalAdapterPosition<T> result = getAdapterOffsetForItem(position);
+ int otherViewTypeCount = 0;
+ for (T adapter : mAdapters) {
+ if (adapter == result.mAdapter) {
+ break;
+ }
+ otherViewTypeCount += adapter.getViewTypeCount();
+ }
+ int type = result.mAdapter.getItemViewType(result.mLocalPosition);
+ // Headers (negative types) are in a separate global namespace and their values should not
+ // be affected by preceding adapter view types.
+ if (type >= 0) {
+ type += otherViewTypeCount;
+ }
+ return type;
+ }
+
+ @Override
+ public View getView(int position, View convertView, ViewGroup parent) {
+ LocalAdapterPosition<T> result = getAdapterOffsetForItem(position);
+ return result.mAdapter.getView(result.mLocalPosition, convertView, parent);
+ }
+
+ @Override
+ public View getDropDownView(int position, View convertView, ViewGroup parent) {
+ LocalAdapterPosition<T> result = getAdapterOffsetForItem(position);
+ return result.mAdapter.getDropDownView(result.mLocalPosition, convertView, parent);
+ }
+
+ @Override
+ public boolean areAllItemsEnabled() {
+ boolean enabled = true;
+ for (T adapter : mAdapters) {
+ enabled &= adapter.areAllItemsEnabled();
+ }
+ return enabled;
+ }
+
+ @Override
+ public boolean isEnabled(int position) {
+ LocalAdapterPosition<T> result = getAdapterOffsetForItem(position);
+ return result.mAdapter.isEnabled(result.mLocalPosition);
+ }
+
+} \ No newline at end of file
diff --git a/src-mailcommon/com/android/mailcommon/MultiAdapterSpinner.java b/src-mailcommon/com/android/mailcommon/MultiAdapterSpinner.java
new file mode 100644
index 000000000..cd58f3ea3
--- /dev/null
+++ b/src-mailcommon/com/android/mailcommon/MultiAdapterSpinner.java
@@ -0,0 +1,186 @@
+/*
+ * Copyright (C) 2011 Google Inc.
+ * Licensed to The Android Open Source Project.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package com.android.mailcommon;
+
+import com.android.mailcommon.MergedAdapter.ListSpinnerAdapter;
+import com.android.mailcommon.MergedAdapter.LocalAdapterPosition;
+
+import android.content.Context;
+import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
+import android.util.AttributeSet;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.AdapterView;
+import android.widget.FrameLayout;
+import android.widget.ListPopupWindow;
+import android.widget.ListView;
+
+
+/**
+ * <p>A spinner-like widget that combines data and views from multiple adapters (via MergedAdapter)
+ * and forwards certain events to those adapters. This widget also supports clickable but
+ * unselectable dropdown items, useful when displaying extra items that should not affect spinner
+ * selection state.</p>
+ *
+ * <p>The framework's Spinner widget can't be extended for this task because it uses a private list
+ * adapter (which prevents setting multiple item types) and hides access to its popup, which is
+ * useful for clients to know about (like when it's been opened).</p>
+ *
+ * <p>Clients must provide a set of adapters which the widget will use to load dropdown views,
+ * receive callbacks, and load the selected item's view.</p>
+ *
+ * <p>Apps incorporating this widget must declare a custom attribute: "dropDownWidth" under the
+ * "MultiAdapterSpinner" name as a "reference" format (see Gmail's attrs.xml file for an
+ * example). This attribute controls the width of the dropdown, similar to the attribute in the
+ * framework's Spinner widget.</p>
+ *
+ */
+public class MultiAdapterSpinner extends FrameLayout
+ implements AdapterView.OnItemClickListener, View.OnClickListener {
+
+ protected MergedAdapter<FancySpinnerAdapter> mAdapter;
+ protected ListPopupWindow mPopup;
+
+ private int mSelectedPosition = -1;
+ private Rect mTempRect = new Rect();
+
+ /**
+ * A basic adapter with some callbacks added so clients can be involved in spinner behavior.
+ */
+ public interface FancySpinnerAdapter extends ListSpinnerAdapter {
+ /**
+ * Whether or not an item at position should become the new selected spinner item and change
+ * the spinner item view.
+ */
+ boolean canSelect(int position);
+ /**
+ * Handle a click on an enabled item.
+ */
+ void onClick(int position);
+ /**
+ * Fired when the popup window is about to be displayed.
+ */
+ void onShowPopup();
+ }
+
+ private static class MergedSpinnerAdapter extends MergedAdapter<FancySpinnerAdapter> {
+ /**
+ * ListPopupWindow uses getView() but spinners return dropdown views in getDropDownView().
+ */
+ @Override
+ public View getView(int position, View convertView, ViewGroup parent) {
+ return super.getDropDownView(position, convertView, parent);
+ }
+ }
+
+ public MultiAdapterSpinner(Context context) {
+ this(context, null);
+ }
+
+ public MultiAdapterSpinner(Context context, AttributeSet attrs) {
+ super(context, attrs);
+
+ mAdapter = new MergedSpinnerAdapter();
+ mPopup = new ListPopupWindow(context, attrs);
+ mPopup.setAnchorView(this);
+ mPopup.setOnItemClickListener(this);
+ mPopup.setModal(true);
+ mPopup.setAdapter(mAdapter);
+ }
+
+ public void setAdapters(FancySpinnerAdapter... adapters) {
+ mAdapter.setAdapters(adapters);
+ }
+
+ public void setSelectedItem(final FancySpinnerAdapter adapter, final int position) {
+ int globalPosition = 0;
+ boolean found = false;
+
+ for (int i = 0, count = mAdapter.getSubAdapterCount(); i < count; i++) {
+ ListSpinnerAdapter a = mAdapter.getSubAdapter(i);
+ if (a == adapter) {
+ globalPosition += position;
+ found = true;
+ break;
+ }
+ globalPosition += a.getCount();
+ }
+ if (found) {
+ if (adapter.canSelect(position)) {
+ removeAllViews();
+ View itemView = adapter.getView(position, null, this);
+ itemView.setClickable(true);
+ itemView.setOnClickListener(this);
+ addView(itemView);
+
+ if (position < adapter.getCount()) {
+ mSelectedPosition = globalPosition;
+ }
+ }
+ }
+ }
+
+ @Override
+ public void onClick(View v) {
+ if (!mPopup.isShowing()) {
+
+ for (int i = 0, count = mAdapter.getSubAdapterCount(); i < count; i++) {
+ mAdapter.getSubAdapter(i).onShowPopup();
+ }
+
+ final int spinnerPaddingLeft = getPaddingLeft();
+ final Drawable background = mPopup.getBackground();
+ int bgOffset = 0;
+ if (background != null) {
+ background.getPadding(mTempRect);
+ bgOffset = -mTempRect.left;
+ }
+ mPopup.setHorizontalOffset(bgOffset + spinnerPaddingLeft);
+ mPopup.show();
+ mPopup.getListView().setChoiceMode(ListView.CHOICE_MODE_SINGLE);
+ mPopup.setSelection(mSelectedPosition);
+ }
+ }
+
+ @Override
+ public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
+
+ if (position != mSelectedPosition) {
+ final LocalAdapterPosition<FancySpinnerAdapter> result =
+ mAdapter.getAdapterOffsetForItem(position);
+
+ if (result.mAdapter.canSelect(result.mLocalPosition)) {
+ mSelectedPosition = position;
+ } else {
+ mPopup.clearListSelection();
+ }
+
+ post(new Runnable() {
+ @Override
+ public void run() {
+ result.mAdapter.onClick(result.mLocalPosition);
+ }
+ });
+ }
+
+ mPopup.dismiss();
+ }
+
+}
diff --git a/src-mailcommon/com/android/mailcommon/WebViewContextMenu.java b/src-mailcommon/com/android/mailcommon/WebViewContextMenu.java
new file mode 100644
index 000000000..0de87d60d
--- /dev/null
+++ b/src-mailcommon/com/android/mailcommon/WebViewContextMenu.java
@@ -0,0 +1,349 @@
+/*
+ * Copyright (C) 2011 Google Inc.
+ * Licensed to The Android Open Source Project.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.mailcommon;
+
+import android.app.Activity;
+import android.content.ClipData;
+import android.content.ClipboardManager;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.net.Uri;
+import android.provider.ContactsContract;
+import android.view.ContextMenu;
+import android.view.ContextMenu.ContextMenuInfo;
+import android.view.LayoutInflater;
+import android.view.MenuInflater;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.View.OnCreateContextMenuListener;
+import android.webkit.WebView;
+import android.widget.TextView;
+
+import java.io.UnsupportedEncodingException;
+import java.net.URLDecoder;
+import java.net.URLEncoder;
+import java.nio.charset.Charset;
+
+/**
+ * <p>Handles display and behavior of the context menu for known actionable content in WebViews.
+ * Requires an Activity to bind to for Context resolution and to start other activites.</p>
+ * <br>
+ * <p>Activity/Fragment clients must forward the 'onContextItemSelected' method.</p>
+ * <br>
+ * Dependencies:
+ * <ul>
+ * <li>res/menu/webview_context_menu.xml</li>
+ * <li>res/values/webview_context_menu_strings.xml</li>
+ * </ul>
+ */
+public abstract class WebViewContextMenu implements OnCreateContextMenuListener,
+ MenuItem.OnMenuItemClickListener {
+
+ private Activity mActivity;
+
+ protected static enum MenuType {
+ OPEN_MENU,
+ COPY_LINK_MENU,
+ SHARE_LINK_MENU,
+ DIAL_MENU,
+ SMS_MENU,
+ ADD_CONTACT_MENU,
+ COPY_PHONE_MENU,
+ EMAIL_CONTACT_MENU,
+ COPY_MAIL_MENU,
+ MAP_MENU,
+ COPY_GEO_MENU,
+ }
+
+ protected static enum MenuGroupType {
+ PHONE_GROUP,
+ EMAIL_GROUP,
+ GEO_GROUP,
+ ANCHOR_GROUP,
+ }
+
+ public WebViewContextMenu(Activity host) {
+ this.mActivity = host;
+ }
+
+ // For our copy menu items.
+ private class Copy implements MenuItem.OnMenuItemClickListener {
+ private final CharSequence mText;
+
+ public Copy(CharSequence text) {
+ mText = text;
+ }
+
+ @Override
+ public boolean onMenuItemClick(MenuItem item) {
+ copy(mText);
+ return true;
+ }
+ }
+
+ // For our share menu items.
+ private class Share implements MenuItem.OnMenuItemClickListener {
+ private final String mUri;
+
+ public Share(String text) {
+ mUri = text;
+ }
+
+ @Override
+ public boolean onMenuItemClick(MenuItem item) {
+ shareLink(mUri);
+ return true;
+ }
+ }
+
+ private boolean showShareLinkMenuItem() {
+ PackageManager pm = mActivity.getPackageManager();
+ Intent send = new Intent(Intent.ACTION_SEND);
+ send.setType("text/plain");
+ ResolveInfo ri = pm.resolveActivity(send, PackageManager.MATCH_DEFAULT_ONLY);
+ return ri != null;
+ }
+
+ private void shareLink(String url) {
+ Intent send = new Intent(Intent.ACTION_SEND);
+ send.setType("text/plain");
+ send.putExtra(Intent.EXTRA_TEXT, url);
+
+ try {
+ mActivity.startActivity(Intent.createChooser(send, mActivity.getText(
+ getChooserTitleStringResIdForMenuType(MenuType.SHARE_LINK_MENU))));
+ } catch(android.content.ActivityNotFoundException ex) {
+ // if no app handles it, do nothing
+ }
+ }
+
+ private void copy(CharSequence text) {
+ ClipboardManager clipboard =
+ (ClipboardManager) mActivity.getSystemService(Context.CLIPBOARD_SERVICE);
+ clipboard.setPrimaryClip(ClipData.newPlainText(null, text));
+ }
+
+ public void onCreateContextMenu(ContextMenu menu, View v,
+ ContextMenuInfo info) {
+ // FIXME: This is copied over almost directly from BrowserActivity.
+ // Would like to find a way to combine the two (Bug 1251210).
+
+ WebView webview = (WebView) v;
+ WebView.HitTestResult result = webview.getHitTestResult();
+ if (result == null) {
+ return;
+ }
+
+ int type = result.getType();
+ switch (type) {
+ case WebView.HitTestResult.UNKNOWN_TYPE:
+ case WebView.HitTestResult.EDIT_TEXT_TYPE:
+ return;
+ default:
+ break;
+ }
+
+ // Note, http://b/issue?id=1106666 is requesting that
+ // an inflated menu can be used again. This is not available
+ // yet, so inflate each time (yuk!)
+ MenuInflater inflater = mActivity.getMenuInflater();
+ // Also, we are copying the menu file from browser until
+ // 1251210 is fixed.
+ inflater.inflate(getMenuResourceId(), menu);
+
+ // Initially make set the menu item handler this WebViewContextMenu, which will default to
+ // calling the non-abstract subclass's implementation.
+ for (int i = 0; i < menu.size(); i++) {
+ final MenuItem menuItem = menu.getItem(i);
+ menuItem.setOnMenuItemClickListener(this);
+ }
+
+
+ // Show the correct menu group
+ String extra = result.getExtra();
+ menu.setGroupVisible(getMenuGroupResId(MenuGroupType.PHONE_GROUP),
+ type == WebView.HitTestResult.PHONE_TYPE);
+ menu.setGroupVisible(getMenuGroupResId(MenuGroupType.EMAIL_GROUP),
+ type == WebView.HitTestResult.EMAIL_TYPE);
+ menu.setGroupVisible(getMenuGroupResId(MenuGroupType.GEO_GROUP),
+ type == WebView.HitTestResult.GEO_TYPE);
+ menu.setGroupVisible(getMenuGroupResId(MenuGroupType.ANCHOR_GROUP),
+ type == WebView.HitTestResult.SRC_ANCHOR_TYPE
+ || type == WebView.HitTestResult.SRC_IMAGE_ANCHOR_TYPE);
+
+ // Setup custom handling depending on the type
+ switch (type) {
+ case WebView.HitTestResult.PHONE_TYPE:
+ String decodedPhoneExtra;
+ try {
+ decodedPhoneExtra = URLDecoder.decode(extra, Charset.defaultCharset().name());
+ }
+ catch (UnsupportedEncodingException ignore) {
+ // Should never happen; default charset is UTF-8
+ decodedPhoneExtra = extra;
+ }
+
+ menu.setHeaderTitle(decodedPhoneExtra);
+ // Dial
+ final MenuItem dialMenuItem =
+ menu.findItem(getMenuResIdForMenuType(MenuType.DIAL_MENU));
+ // remove the on click listener
+ dialMenuItem.setOnMenuItemClickListener(null);
+ dialMenuItem.setIntent(new Intent(Intent.ACTION_VIEW,
+ Uri.parse(WebView.SCHEME_TEL + extra)));
+
+ // Send SMS
+ final MenuItem sendSmsMenuItem =
+ menu.findItem(getMenuResIdForMenuType(MenuType.SMS_MENU));
+ // remove the on click listener
+ sendSmsMenuItem.setOnMenuItemClickListener(null);
+ sendSmsMenuItem.setIntent(new Intent(Intent.ACTION_SENDTO,
+ Uri.parse("smsto:" + extra)));
+
+ // Add to contacts
+ final Intent addIntent = new Intent(Intent.ACTION_INSERT_OR_EDIT);
+ addIntent.setType(ContactsContract.Contacts.CONTENT_ITEM_TYPE);
+
+ addIntent.putExtra(ContactsContract.Intents.Insert.PHONE, decodedPhoneExtra);
+ final MenuItem addToContactsMenuItem =
+ menu.findItem(getMenuResIdForMenuType(MenuType.ADD_CONTACT_MENU));
+ // remove the on click listener
+ addToContactsMenuItem.setOnMenuItemClickListener(null);
+ addToContactsMenuItem.setIntent(addIntent);
+
+ // Copy
+ menu.findItem(getMenuResIdForMenuType(MenuType.COPY_PHONE_MENU)).
+ setOnMenuItemClickListener(new Copy(extra));
+ break;
+
+ case WebView.HitTestResult.EMAIL_TYPE:
+ menu.setHeaderTitle(extra);
+ menu.findItem(getMenuResIdForMenuType(MenuType.EMAIL_CONTACT_MENU)).setIntent(
+ new Intent(Intent.ACTION_VIEW, Uri
+ .parse(WebView.SCHEME_MAILTO + extra)));
+ menu.findItem(getMenuResIdForMenuType(MenuType.COPY_MAIL_MENU)).
+ setOnMenuItemClickListener(new Copy(extra));
+ break;
+
+ case WebView.HitTestResult.GEO_TYPE:
+ menu.setHeaderTitle(extra);
+ String geoExtra = "";
+ try {
+ geoExtra = URLEncoder.encode(extra, Charset.defaultCharset().name());
+ }
+ catch (UnsupportedEncodingException ignore) {
+ // Should never happen; default charset is UTF-8
+ }
+ final MenuItem viewMapMenuItem =
+ menu.findItem(getMenuResIdForMenuType(MenuType.MAP_MENU));
+ // remove the on click listener
+ viewMapMenuItem.setOnMenuItemClickListener(null);
+ viewMapMenuItem.setIntent(new Intent(Intent.ACTION_VIEW,
+ Uri.parse(WebView.SCHEME_GEO + geoExtra)));
+ menu.findItem(getMenuResIdForMenuType(MenuType.COPY_GEO_MENU)).
+ setOnMenuItemClickListener(new Copy(extra));
+ break;
+
+ case WebView.HitTestResult.SRC_ANCHOR_TYPE:
+ case WebView.HitTestResult.SRC_IMAGE_ANCHOR_TYPE:
+ // FIXME: Make this look like the normal menu header
+ // We cannot use the normal menu header because we need to
+ // edit the ContextMenu after it has been created.
+ final TextView titleView = (TextView) LayoutInflater.from(mActivity)
+ .inflate(getTitleViewLayoutResId(MenuType.SHARE_LINK_MENU), null);
+ menu.setHeaderView(titleView);
+
+ menu.findItem(getMenuResIdForMenuType(MenuType.SHARE_LINK_MENU)).setVisible(
+ showShareLinkMenuItem());
+
+ // The documentation for WebView indicates that if the HitTestResult is
+ // SRC_ANCHOR_TYPE or the url would be specified in the extra. We don't need to
+ // call requestFocusNodeHref(). If we wanted to handle UNKNOWN HitTestResults, we
+ // would. With this knowledge, we can just set the title
+ titleView.setText(extra);
+
+ menu.findItem(getMenuResIdForMenuType(MenuType.COPY_LINK_MENU)).
+ setOnMenuItemClickListener(new Copy(extra));
+
+ final MenuItem openLinkMenuItem =
+ menu.findItem(getMenuResIdForMenuType(MenuType.OPEN_MENU));
+ // remove the on click listener
+ openLinkMenuItem.setOnMenuItemClickListener(null);
+ openLinkMenuItem.setIntent(new Intent(Intent.ACTION_VIEW, Uri.parse(extra)));
+
+ menu.findItem(getMenuResIdForMenuType(MenuType.SHARE_LINK_MENU)).
+ setOnMenuItemClickListener(new Share(extra));
+ break;
+ default:
+ break;
+ }
+ }
+
+ @Override
+ public boolean onMenuItemClick(MenuItem item) {
+ return onMenuItemSelected(item);
+ }
+
+ /**
+ * Returns the menu type from the given resource id
+ * @param menuResId resource id of the menu
+ * @return MenuType for the specified menu resource id
+ */
+ abstract protected MenuType getMenuTypeFromResId(int menuResId);
+
+ /**
+ * Returns the menu resource id for the specified menu type
+ * @param menuType type of the specified menu
+ * @return menu resource id
+ */
+ abstract protected int getMenuResIdForMenuType(MenuType menuType);
+
+ /**
+ * Returns the resource id of the string to be used when showing a chooser for a menu
+ * @param menuType type of the specified menu
+ * @return string resource id
+ */
+ abstract protected int getChooserTitleStringResIdForMenuType(MenuType menuType);
+
+ /**
+ * Returns the resource id of the layout to be used for the title of the specified menu
+ * @param menuType type of the specified menu
+ * @return layout resource id
+ */
+ abstract protected int getTitleViewLayoutResId(MenuType menuType);
+
+ /**
+ * Returns the menu group resource id for the specified menu group type.
+ * @param menuGroupType menu group type
+ * @return menu group resource id
+ */
+ abstract protected int getMenuGroupResId(MenuGroupType menuGroupType);
+
+ /**
+ * Returns the resource id for the web view context menu
+ */
+ abstract protected int getMenuResourceId();
+
+
+ /**
+ * Called when a menu item is not handled by the context menu.
+ */
+ abstract protected boolean onMenuItemSelected(MenuItem menuItem);
+}