| Index: chrome/android/java/src/org/chromium/chrome/browser/WebsiteSettingsPopup.java
|
| diff --git a/chrome/android/java/src/org/chromium/chrome/browser/WebsiteSettingsPopup.java b/chrome/android/java/src/org/chromium/chrome/browser/WebsiteSettingsPopup.java
|
| index 9b1311ad1f59e568ba1ef2f4b01e18d6dc971c65..7448ff35ee5238528fecc8c73083440f3a841105 100644
|
| --- a/chrome/android/java/src/org/chromium/chrome/browser/WebsiteSettingsPopup.java
|
| +++ b/chrome/android/java/src/org/chromium/chrome/browser/WebsiteSettingsPopup.java
|
| @@ -12,26 +12,66 @@ import android.graphics.drawable.ColorDrawable;
|
| import android.text.Spannable;
|
| import android.text.SpannableStringBuilder;
|
| import android.text.style.ForegroundColorSpan;
|
| +import android.text.style.StrikethroughSpan;
|
| import android.text.style.StyleSpan;
|
| import android.view.Gravity;
|
| import android.view.LayoutInflater;
|
| +import android.view.View;
|
| +import android.view.View.OnClickListener;
|
| import android.view.ViewGroup;
|
| import android.view.Window;
|
| +import android.widget.AdapterView;
|
| +import android.widget.AdapterView.OnItemSelectedListener;
|
| +import android.widget.ArrayAdapter;
|
| +import android.widget.Button;
|
| +import android.widget.ImageView;
|
| import android.widget.LinearLayout;
|
| import android.widget.ScrollView;
|
| +import android.widget.Spinner;
|
| import android.widget.TextView;
|
|
|
| import org.chromium.base.CalledByNative;
|
| import org.chromium.base.CommandLine;
|
| import org.chromium.chrome.ChromeSwitches;
|
| import org.chromium.chrome.R;
|
| -import org.chromium.content.browser.WebContentsObserver;
|
| +import org.chromium.chrome.browser.toolbar.ToolbarModel;
|
| +import org.chromium.chrome.browser.ui.toolbar.ToolbarModelSecurityLevel;
|
| +import org.chromium.content.browser.WebContentsObserverAndroid;
|
| import org.chromium.content_public.browser.WebContents;
|
| +import org.chromium.ui.base.Clipboard;
|
| +
|
| +import java.net.URI;
|
| +import java.net.URISyntaxException;
|
| +import java.util.Arrays;
|
| +import java.util.List;
|
|
|
| /**
|
| * Java side of Android implementation of the website settings UI.
|
| + * TODO(sashab): Rename this, and all its resources, to PageInfo* and page_info_* instead of
|
| + * WebsiteSettings* and website_settings_*. Do this on the C++ side as well.
|
| */
|
| -public class WebsiteSettingsPopup {
|
| +public class WebsiteSettingsPopup implements OnClickListener, OnItemSelectedListener {
|
| + /**
|
| + * An entry in the settings dropdown for a given permission. There are two options for each
|
| + * permission: Allow and Block.
|
| + */
|
| + private static final class PageInfoPermissionEntry {
|
| + public final String name;
|
| + public final int type;
|
| + public final int value;
|
| +
|
| + PageInfoPermissionEntry(String name, int type, int value) {
|
| + this.name = name;
|
| + this.type = type;
|
| + this.value = value;
|
| + }
|
| +
|
| + @Override
|
| + public String toString() {
|
| + return name;
|
| + }
|
| + }
|
| +
|
| private final Context mContext;
|
| private final WebContents mWebContents;
|
|
|
| @@ -44,13 +84,26 @@ public class WebsiteSettingsPopup {
|
| // UI elements in the dialog.
|
| private final TextView mUrlTitle;
|
| private final TextView mUrlConnectionMessage;
|
| + private final LinearLayout mPermissionsList;
|
| + private final Button mCopyUrlButton;
|
| + private final Button mSiteSettingsButton;
|
| + private final Button mDoneButton;
|
|
|
| // The dialog the container is placed in.
|
| private final Dialog mDialog;
|
|
|
| + // The full URL from the URL bar, which is copied to the user's clipboard when they select 'Copy
|
| + // URL'.
|
| + private String mFullUrl;
|
| + private URI mUrl;
|
| +
|
| /**
|
| - * Creates the WebsiteSettingsPopup, but does not display it. Also
|
| - * initializes the corresponding C++ object and saves a pointer to it.
|
| + * Creates the WebsiteSettingsPopup, but does not display it. Also initializes the corresponding
|
| + * C++ object and saves a pointer to it.
|
| + *
|
| + * @param context Context which is used for launching a dialog.
|
| + * @param webContents The WebContents for which to show Website information. This information is
|
| + * retrieved for the visible entry.
|
| */
|
| private WebsiteSettingsPopup(Context context, WebContents webContents) {
|
| mContext = context;
|
| @@ -63,7 +116,22 @@ public class WebsiteSettingsPopup {
|
| mUrlTitle = (TextView) mContainer
|
| .findViewById(R.id.website_settings_url);
|
| mUrlConnectionMessage = (TextView) mContainer
|
| - .findViewById(R.id.website_settings_permission_message);
|
| + .findViewById(R.id.website_settings_connection_message);
|
| + mPermissionsList = (LinearLayout) mContainer
|
| + .findViewById(R.id.website_settings_permissions_list);
|
| +
|
| + mCopyUrlButton = (Button) mContainer.findViewById(R.id.website_settings_copy_url_button);
|
| + mCopyUrlButton.setOnClickListener(this);
|
| +
|
| + mSiteSettingsButton = (Button) mContainer
|
| + .findViewById(R.id.website_settings_site_settings_button);
|
| + mSiteSettingsButton.setOnClickListener(this);
|
| + // Hide the Site Settings button until there's a link to take it to.
|
| + // TODO(sashab,finnur): Make this button visible once Site Settings is working.
|
| + mSiteSettingsButton.setVisibility(View.GONE);
|
| +
|
| + mDoneButton = (Button) mContainer.findViewById(R.id.website_settings_done_button);
|
| + mDoneButton.setOnClickListener(this);
|
|
|
| // Create the dialog.
|
| mDialog = new Dialog(mContext);
|
| @@ -77,11 +145,12 @@ public class WebsiteSettingsPopup {
|
|
|
| // This needs to come after other member initialization.
|
| mNativeWebsiteSettingsPopup = nativeInit(this, webContents);
|
| - final WebContentsObserver webContentsObserver = new WebContentsObserver(mWebContents) {
|
| + final WebContentsObserverAndroid webContentsObserver = new WebContentsObserverAndroid(
|
| + mWebContents) {
|
| @Override
|
| public void navigationEntryCommitted() {
|
| - // If a navigation is committed (e.g. from in-page redirect), the data we're
|
| - // showing is stale so dismiss the dialog.
|
| + // If a navigation is committed (e.g. from in-page redirect), the data we're showing
|
| + // is stale so dismiss the dialog.
|
| mDialog.dismiss();
|
| }
|
| };
|
| @@ -96,72 +165,165 @@ public class WebsiteSettingsPopup {
|
| }
|
|
|
| /**
|
| - * Sets the URL in the title to: (scheme)://(domain)(path). Also colors
|
| - * different parts of the URL depending on connectionType.
|
| - * connectionType should be a valid PageInfoConnectionType.
|
| + * Finds the Image resource of the icon to use for the given permission.
|
| + *
|
| + * @param permission A valid ContentSettingsType that can be displayed in the PageInfo dialog to
|
| + * retrieve the image for.
|
| + * @return The resource ID of the icon to use for that permission.
|
| */
|
| - @CalledByNative
|
| - private void setURLTitle(String scheme, String domain, String path, int connectionType) {
|
| - boolean makeDomainBold = false;
|
| - int schemeColorId = R.color.website_settings_popup_url_scheme_broken;
|
| - switch (connectionType) {
|
| - case PageInfoConnectionType.CONNECTION_UNKNOWN:
|
| - schemeColorId = R.color.website_settings_popup_url_scheme_http;
|
| - makeDomainBold = true;
|
| - break;
|
| - case PageInfoConnectionType.CONNECTION_ENCRYPTED:
|
| - schemeColorId = R.color.website_settings_popup_url_scheme_https;
|
| - break;
|
| - case PageInfoConnectionType.CONNECTION_MIXED_CONTENT:
|
| - schemeColorId = R.color.website_settings_popup_url_scheme_mixed;
|
| - makeDomainBold = true;
|
| - break;
|
| - case PageInfoConnectionType.CONNECTION_UNENCRYPTED:
|
| - schemeColorId = R.color.website_settings_popup_url_scheme_http;
|
| - makeDomainBold = true;
|
| - break;
|
| - case PageInfoConnectionType.CONNECTION_ENCRYPTED_ERROR:
|
| - schemeColorId = R.color.website_settings_popup_url_scheme_broken;
|
| - makeDomainBold = true;
|
| - break;
|
| - case PageInfoConnectionType.CONNECTION_INTERNAL_PAGE:
|
| - schemeColorId = R.color.website_settings_popup_url_scheme_http;
|
| - break;
|
| + private int getImageResourceForPermission(int permission) {
|
| + switch (permission) {
|
| + case ContentSettingsType.CONTENT_SETTINGS_TYPE_IMAGES:
|
| + return R.drawable.page_info_image;
|
| + case ContentSettingsType.CONTENT_SETTINGS_TYPE_JAVASCRIPT:
|
| + return R.drawable.page_info_javascript;
|
| + case ContentSettingsType.CONTENT_SETTINGS_TYPE_GEOLOCATION:
|
| + return R.drawable.page_info_location;
|
| + case ContentSettingsType.CONTENT_SETTINGS_TYPE_MEDIASTREAM:
|
| + return R.drawable.page_info_media;
|
| + case ContentSettingsType.CONTENT_SETTINGS_TYPE_NOTIFICATIONS:
|
| + return R.drawable.page_info_notification;
|
| + case ContentSettingsType.CONTENT_SETTINGS_TYPE_POPUPS:
|
| + return R.drawable.page_info_popups;
|
| default:
|
| - assert false : "Unexpected connection type: " + connectionType;
|
| + assert false : "Icon requested for invalid permission: " + permission;
|
| + return -1;
|
| }
|
| + }
|
| +
|
| + @CalledByNative
|
| + private void updatePageDetails(boolean isInternalPage) {
|
| + mFullUrl = mWebContents.getVisibleUrl();
|
| + int securityLevel = ToolbarModel.getSecurityLevelForWebContents(mWebContents);
|
|
|
| - SpannableStringBuilder sb = new SpannableStringBuilder(scheme + "://" + domain + path);
|
| + try {
|
| + mUrl = new URI(mFullUrl);
|
| + } catch (URISyntaxException e) {
|
| + assert false : "Invalid URL specified: " + mFullUrl;
|
| + }
|
|
|
| - ForegroundColorSpan schemeColorSpan = new ForegroundColorSpan(
|
| - mContext.getResources().getColor(schemeColorId));
|
| - sb.setSpan(schemeColorSpan, 0, scheme.length(), Spannable.SPAN_INCLUSIVE_INCLUSIVE);
|
| + int schemeColorId = -1;
|
| + if (securityLevel == ToolbarModelSecurityLevel.SECURITY_ERROR) {
|
| + schemeColorId = R.color.website_settings_popup_url_scheme_broken;
|
|
|
| - int domainStartIndex = scheme.length() + 3;
|
| - ForegroundColorSpan domainColorSpan = new ForegroundColorSpan(
|
| - mContext.getResources().getColor(R.color.website_settings_popup_url_domain));
|
| - sb.setSpan(domainColorSpan, domainStartIndex, domainStartIndex + domain.length(),
|
| - Spannable.SPAN_INCLUSIVE_INCLUSIVE);
|
| + String leadingText = mContext.getResources().getString(
|
| + R.string.page_info_connection_broken_leading_text);
|
| + String followingText = mContext.getResources().getString(
|
| + R.string.page_info_connection_broken_following_text,
|
| + UrlUtilities.getOriginForDisplay(mUrl, false));
|
| + SpannableStringBuilder sb = new SpannableStringBuilder(leadingText + " "
|
| + + followingText);
|
| + final ForegroundColorSpan redSpan = new ForegroundColorSpan(mContext.getResources()
|
| + .getColor(R.color.website_settings_popup_url_scheme_broken));
|
| + final StyleSpan boldSpan = new StyleSpan(android.graphics.Typeface.BOLD);
|
| + sb.setSpan(redSpan, 0, leadingText.length(), Spannable.SPAN_INCLUSIVE_EXCLUSIVE);
|
| + sb.setSpan(boldSpan, 0, leadingText.length(), Spannable.SPAN_INCLUSIVE_EXCLUSIVE);
|
| + mUrlConnectionMessage.setText(sb);
|
| + } else {
|
| + int connectionMessageId = 0;
|
| + if (isInternalPage) {
|
| + schemeColorId = R.color.website_settings_popup_url_scheme_http;
|
| + connectionMessageId = R.string.page_info_connection_internal_page;
|
| + } else {
|
| + switch (securityLevel) {
|
| + case ToolbarModelSecurityLevel.NONE:
|
| + schemeColorId = R.color.website_settings_popup_url_scheme_http;
|
| + connectionMessageId = R.string.page_info_connection_http;
|
| + break;
|
| + case ToolbarModelSecurityLevel.SECURE:
|
| + case ToolbarModelSecurityLevel.EV_SECURE:
|
| + schemeColorId = R.color.website_settings_popup_url_scheme_https;
|
| + connectionMessageId = R.string.page_info_connection_https;
|
| + break;
|
| + case ToolbarModelSecurityLevel.SECURITY_WARNING:
|
| + case ToolbarModelSecurityLevel.SECURITY_POLICY_WARNING:
|
| + schemeColorId = R.color.website_settings_popup_url_scheme_mixed;
|
| + connectionMessageId = R.string.page_info_connection_mixed;
|
| + break;
|
| + default:
|
| + assert false : "Invalid security level specified: " + securityLevel;
|
| + schemeColorId = R.color.website_settings_popup_url_scheme_http;
|
| + connectionMessageId = R.string.page_info_connection_http;
|
| + }
|
| + }
|
| + mUrlConnectionMessage.setText(mContext.getResources().getString(connectionMessageId));
|
| + }
|
|
|
| - if (makeDomainBold) {
|
| - StyleSpan boldStyleSpan = new StyleSpan(android.graphics.Typeface.BOLD);
|
| - sb.setSpan(boldStyleSpan, domainStartIndex, domainStartIndex + domain.length(),
|
| - Spannable.SPAN_INCLUSIVE_INCLUSIVE);
|
| + // Color the URI-parsed version of the URL.
|
| + SpannableStringBuilder sb = new SpannableStringBuilder(mUrl.toString());
|
| + final ForegroundColorSpan schemeColorSpan = new ForegroundColorSpan(mContext.getResources()
|
| + .getColor(schemeColorId));
|
| + sb.setSpan(schemeColorSpan, 0, mUrl.getScheme().length(),
|
| + Spannable.SPAN_INCLUSIVE_EXCLUSIVE);
|
| + if (securityLevel == ToolbarModelSecurityLevel.SECURITY_ERROR) {
|
| + sb.setSpan(new StrikethroughSpan(), 0, mUrl.getScheme().length(),
|
| + Spannable.SPAN_INCLUSIVE_EXCLUSIVE);
|
| }
|
|
|
| + // The domain is everything after the scheme until the end of the
|
| + // origin.
|
| + final ForegroundColorSpan domainColorSpan = new ForegroundColorSpan(
|
| + mContext.getResources().getColor(R.color.website_settings_popup_url_domain));
|
| + sb.setSpan(domainColorSpan, mUrl.getScheme().length(),
|
| + UrlUtilities.getOriginForDisplay(mUrl, true).length(),
|
| + Spannable.SPAN_INCLUSIVE_EXCLUSIVE);
|
| mUrlTitle.setText(sb);
|
| }
|
|
|
| /**
|
| - * Sets the connection message displayed at the top of the dialog to
|
| - * connectionMessage (e.g. "Could not securely connect to this site").
|
| + * Adds a new row for the given permission.
|
| + *
|
| + * @param name The title of the permission to display to the user.
|
| + * @param type The ContentSettingsType of the permission.
|
| + * @param currentSetting The ContentSetting of the currently selected setting.
|
| */
|
| @CalledByNative
|
| - private void setConnectionMessage(String connectionMessage) {
|
| - mUrlConnectionMessage.setText(connectionMessage);
|
| + private void addPermissionSection(String name, int type, int currentSetting) {
|
| + LinearLayout permissionRow = (LinearLayout) LayoutInflater.from(mContext).inflate(
|
| + R.layout.website_settings_permission_row, null);
|
| +
|
| + ImageView permission_icon = (ImageView) permissionRow.findViewById(
|
| + R.id.website_settings_permission_icon);
|
| + permission_icon.setImageResource(getImageResourceForPermission(type));
|
| +
|
| + TextView permission_name = (TextView) permissionRow.findViewById(
|
| + R.id.website_settings_permission_name);
|
| + permission_name.setText(name);
|
| +
|
| + Spinner permission_spinner = (Spinner) permissionRow.findViewById(
|
| + R.id.website_settings_permission_spinner);
|
| +
|
| + // Work out the index of the currently selected setting.
|
| + int selectedSettingIndex = -1;
|
| + switch (currentSetting) {
|
| + case ContentSetting.ALLOW:
|
| + selectedSettingIndex = 0;
|
| + break;
|
| + case ContentSetting.BLOCK:
|
| + selectedSettingIndex = 1;
|
| + break;
|
| + default:
|
| + assert false : "Invalid setting " + currentSetting + " for permission " + type;
|
| + }
|
| +
|
| + List<PageInfoPermissionEntry> settingsChoices = Arrays.asList(
|
| + new PageInfoPermissionEntry(mContext.getResources().getString(
|
| + R.string.page_info_permission_allow), type, ContentSetting.ALLOW),
|
| + new PageInfoPermissionEntry(mContext.getResources().getString(
|
| + R.string.page_info_permission_block), type, ContentSetting.BLOCK));
|
| + ArrayAdapter<PageInfoPermissionEntry> adapter = new ArrayAdapter<PageInfoPermissionEntry>(
|
| + mContext, R.drawable.website_settings_permission_spinner_item, settingsChoices);
|
| + adapter.setDropDownViewResource(
|
| + R.drawable.website_settings_permission_spinner_dropdown_item);
|
| + permission_spinner.setAdapter(adapter);
|
| + permission_spinner.setSelection(selectedSettingIndex, false);
|
| + permission_spinner.setOnItemSelectedListener(this);
|
| + mPermissionsList.addView(permissionRow);
|
| }
|
|
|
| - /** Displays the WebsiteSettingsPopup. */
|
| + /**
|
| + * Displays the WebsiteSettingsPopup.
|
| + */
|
| @CalledByNative
|
| private void showDialog() {
|
| // Wrap the dialog in a ScrollView in case the content is too long.
|
| @@ -171,8 +333,8 @@ public class WebsiteSettingsPopup {
|
| LinearLayout.LayoutParams.MATCH_PARENT,
|
| LinearLayout.LayoutParams.MATCH_PARENT));
|
|
|
| - // Make the dialog fill the width of the screen. This must be called
|
| - // after addContentView, or it won't fully fill to the edge.
|
| + // Make the dialog fill the width of the screen. This must be called after addContentView,
|
| + // or it won't fully fill to the edge.
|
| Window window = mDialog.getWindow();
|
| window.setLayout(ViewGroup.LayoutParams.MATCH_PARENT,
|
| ViewGroup.LayoutParams.WRAP_CONTENT);
|
| @@ -180,14 +342,38 @@ public class WebsiteSettingsPopup {
|
| mDialog.show();
|
| }
|
|
|
| + @Override
|
| + public void onItemSelected(AdapterView<?> parent, View view, int pos, long id) {
|
| + PageInfoPermissionEntry entry = (PageInfoPermissionEntry) parent.getItemAtPosition(pos);
|
| + nativeOnPermissionSettingChanged(mNativeWebsiteSettingsPopup, entry.type, entry.value);
|
| + }
|
| +
|
| + @Override
|
| + public void onNothingSelected(AdapterView<?> parent) {
|
| + // Do nothing intentionally.
|
| + }
|
| +
|
| + @Override
|
| + public void onClick(View view) {
|
| + if (view == mCopyUrlButton) {
|
| + new Clipboard(mContext).setText(mFullUrl, mFullUrl);
|
| + mDialog.dismiss();
|
| + } else if (view == mSiteSettingsButton) {
|
| + // TODO(sashab,finnur): Make this open the Website Settings dialog.
|
| + assert false : "No Website Settings here!";
|
| + mDialog.dismiss();
|
| + } else if (view == mDoneButton) {
|
| + mDialog.dismiss();
|
| + }
|
| + }
|
| +
|
| /**
|
| - * Shows a WebsiteSettings dialog for the provided WebContents. The popup
|
| - * adds itself to the view hierarchy which owns the reference while it's
|
| - * visible.
|
| + * Shows a WebsiteSettings dialog for the provided WebContents. The popup adds itself to the
|
| + * view hierarchy which owns the reference while it's visible.
|
| *
|
| * @param context Context which is used for launching a dialog.
|
| - * @param webContents The WebContents for which to show Website information.
|
| - * This information is retrieved for the visible entry.
|
| + * @param webContents The WebContents for which to show Website information. This information is
|
| + * retrieved for the visible entry.
|
| */
|
| @SuppressWarnings("unused")
|
| public static void show(Context context, WebContents webContents) {
|
| @@ -198,8 +384,10 @@ public class WebsiteSettingsPopup {
|
| }
|
| }
|
|
|
| - private static native long nativeInit(WebsiteSettingsPopup popup,
|
| - WebContents webContents);
|
| + private static native long nativeInit(WebsiteSettingsPopup popup, WebContents webContents);
|
|
|
| private native void nativeDestroy(long nativeWebsiteSettingsPopupAndroid);
|
| +
|
| + private native void nativeOnPermissionSettingChanged(long nativeWebsiteSettingsPopupAndroid,
|
| + int type, int setting);
|
| }
|
|
|