| OLD | NEW |
| 1 // Copyright 2013 The Chromium Authors. All rights reserved. | 1 // Copyright 2013 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 package org.chromium.chrome.browser; | 5 package org.chromium.chrome.browser; |
| 6 | 6 |
| 7 import android.app.Dialog; | 7 import android.app.Dialog; |
| 8 import android.content.Context; | 8 import android.content.Context; |
| 9 import android.content.DialogInterface; | 9 import android.content.DialogInterface; |
| 10 import android.content.Intent; | |
| 11 import android.graphics.Color; | 10 import android.graphics.Color; |
| 12 import android.provider.Browser; | 11 import android.graphics.drawable.ColorDrawable; |
| 13 import android.text.TextUtils; | 12 import android.text.Spannable; |
| 13 import android.text.SpannableStringBuilder; |
| 14 import android.text.style.ForegroundColorSpan; |
| 15 import android.text.style.StyleSpan; |
| 16 import android.view.Gravity; |
| 14 import android.view.LayoutInflater; | 17 import android.view.LayoutInflater; |
| 15 import android.view.View; | |
| 16 import android.view.View.OnClickListener; | |
| 17 import android.view.ViewGroup; | 18 import android.view.ViewGroup; |
| 18 import android.view.Window; | 19 import android.view.Window; |
| 19 import android.widget.Button; | |
| 20 import android.widget.ImageView; | |
| 21 import android.widget.LinearLayout; | 20 import android.widget.LinearLayout; |
| 22 import android.widget.ScrollView; | 21 import android.widget.ScrollView; |
| 23 import android.widget.TextView; | 22 import android.widget.TextView; |
| 24 | 23 |
| 25 import org.chromium.base.ApiCompatibilityUtils; | |
| 26 import org.chromium.base.CalledByNative; | 24 import org.chromium.base.CalledByNative; |
| 25 import org.chromium.base.CommandLine; |
| 27 import org.chromium.chrome.R; | 26 import org.chromium.chrome.R; |
| 28 import org.chromium.content.browser.WebContentsObserverAndroid; | 27 import org.chromium.content.browser.WebContentsObserverAndroid; |
| 28 import org.chromium.content.common.ContentSwitches; |
| 29 import org.chromium.content_public.browser.WebContents; | 29 import org.chromium.content_public.browser.WebContents; |
| 30 | 30 |
| 31 import java.net.URISyntaxException; | |
| 32 | |
| 33 /** | 31 /** |
| 34 * Java side of Android implementation of the website settings UI. | 32 * Java side of Android implementation of the website settings UI. |
| 35 */ | 33 */ |
| 36 public class WebsiteSettingsPopup implements OnClickListener { | 34 public class WebsiteSettingsPopup { |
| 37 private static final String HELP_URL = | |
| 38 "http://www.google.com/support/chrome/bin/answer.py?answer=95617"; | |
| 39 private static final int DESCRIPTION_TEXT_SIZE_SP = 12; | |
| 40 private final Context mContext; | 35 private final Context mContext; |
| 36 private final WebContents mWebContents; |
| 37 |
| 38 // A pointer to the C++ object for this UI. |
| 39 private final long mNativeWebsiteSettingsPopup; |
| 40 |
| 41 // The outer container, filled with the layout from website_settings.xml. |
| 42 private final LinearLayout mContainer; |
| 43 |
| 44 // UI elements in the dialog. |
| 45 private final TextView mUrlTitle; |
| 46 private final TextView mUrlConnectionMessage; |
| 47 |
| 48 // The dialog the container is placed in. |
| 41 private final Dialog mDialog; | 49 private final Dialog mDialog; |
| 42 private final LinearLayout mContainer; | |
| 43 private final WebContents mWebContents; | |
| 44 private final int mPaddingWide, mPaddingThin; | |
| 45 private final long mNativeWebsiteSettingsPopup; | |
| 46 private TextView mCertificateViewer, mMoreInfoLink; | |
| 47 private ViewGroup mCertificateLayout, mDescriptionLayout; | |
| 48 private Button mResetCertDecisionsButton; | |
| 49 private String mLinkUrl; | |
| 50 | 50 |
| 51 /** |
| 52 * Creates the WebsiteSettingsPopup, but does not display it. Also |
| 53 * initializes the corresponding C++ object and saves a pointer to it. |
| 54 */ |
| 51 private WebsiteSettingsPopup(Context context, WebContents webContents) { | 55 private WebsiteSettingsPopup(Context context, WebContents webContents) { |
| 52 mContext = context; | 56 mContext = context; |
| 53 mWebContents = webContents; | 57 mWebContents = webContents; |
| 54 | 58 |
| 55 mContainer = new LinearLayout(mContext); | 59 // Find the container and all it's important subviews. |
| 56 mContainer.setOrientation(LinearLayout.VERTICAL); | 60 mContainer = (LinearLayout) LayoutInflater.from(mContext).inflate( |
| 57 mContainer.setBackgroundColor(Color.WHITE); | 61 R.layout.website_settings, null); |
| 58 mPaddingWide = (int) context.getResources().getDimension( | |
| 59 R.dimen.certificate_viewer_padding_wide); | |
| 60 mPaddingThin = (int) context.getResources().getDimension( | |
| 61 R.dimen.certificate_viewer_padding_thin); | |
| 62 mContainer.setPadding(mPaddingWide, mPaddingWide + mPaddingThin, mPaddin
gWide, | |
| 63 mPaddingWide); | |
| 64 | 62 |
| 63 mUrlTitle = (TextView) mContainer |
| 64 .findViewById(R.id.website_settings_url); |
| 65 mUrlConnectionMessage = (TextView) mContainer |
| 66 .findViewById(R.id.website_settings_permission_message); |
| 67 |
| 68 // Create the dialog. |
| 65 mDialog = new Dialog(mContext); | 69 mDialog = new Dialog(mContext); |
| 66 mDialog.requestWindowFeature(Window.FEATURE_NO_TITLE); | 70 mDialog.requestWindowFeature(Window.FEATURE_NO_TITLE); |
| 67 mDialog.setCanceledOnTouchOutside(true); | 71 mDialog.setCanceledOnTouchOutside(true); |
| 72 |
| 73 // Place the dialog at the top of the screen, and remove its border. |
| 74 Window window = mDialog.getWindow(); |
| 75 window.setGravity(Gravity.TOP); |
| 76 window.setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT)); |
| 77 |
| 68 // This needs to come after other member initialization. | 78 // This needs to come after other member initialization. |
| 69 mNativeWebsiteSettingsPopup = nativeInit(this, webContents); | 79 mNativeWebsiteSettingsPopup = nativeInit(this, webContents); |
| 70 final WebContentsObserverAndroid webContentsObserver = | 80 final WebContentsObserverAndroid webContentsObserver = new WebContentsOb
serverAndroid( |
| 71 new WebContentsObserverAndroid(mWebContents) { | 81 mWebContents) { |
| 72 @Override | 82 @Override |
| 73 public void navigationEntryCommitted() { | 83 public void navigationEntryCommitted() { |
| 74 // If a navigation is committed (e.g. from in-page redirect), th
e data we're | 84 // If a navigation is committed (e.g. from in-page redirect), th
e data we're |
| 75 // showing is stale so dismiss the dialog. | 85 // showing is stale so dismiss the dialog. |
| 76 mDialog.dismiss(); | 86 mDialog.dismiss(); |
| 77 } | 87 } |
| 78 }; | 88 }; |
| 79 mDialog.setOnDismissListener(new DialogInterface.OnDismissListener() { | 89 mDialog.setOnDismissListener(new DialogInterface.OnDismissListener() { |
| 80 @Override | 90 @Override |
| 81 public void onDismiss(DialogInterface dialog) { | 91 public void onDismiss(DialogInterface dialog) { |
| 82 assert mNativeWebsiteSettingsPopup != 0; | 92 assert mNativeWebsiteSettingsPopup != 0; |
| 83 webContentsObserver.detachFromWebContents(); | 93 webContentsObserver.detachFromWebContents(); |
| 84 nativeDestroy(mNativeWebsiteSettingsPopup); | 94 nativeDestroy(mNativeWebsiteSettingsPopup); |
| 85 } | 95 } |
| 86 }); | 96 }); |
| 87 } | 97 } |
| 88 | 98 |
| 89 /** | 99 /** |
| 90 * Adds certificate section, which contains an icon, a headline, a | 100 * Sets the URL in the title to: (scheme)://(domain)(path). Also colors |
| 91 * description and a label for certificate info link. | 101 * different parts of the URL depending on connectionType. |
| 102 * connectionType should be a valid PageInfoConnectionType. |
| 92 */ | 103 */ |
| 93 @CalledByNative | 104 @CalledByNative |
| 94 private void addCertificateSection(int enumeratedIconId, String headline, St
ring description, | 105 private void setURLTitle(String scheme, String domain, String path, int conn
ectionType) { |
| 95 String label) { | 106 boolean makeDomainBold = false; |
| 96 View section = addSection(enumeratedIconId, headline, description); | 107 int schemeColorId = R.color.website_settings_popup_url_scheme_broken; |
| 97 assert mCertificateLayout == null; | 108 switch (connectionType) { |
| 98 mCertificateLayout = (ViewGroup) section.findViewById(R.id.website_setti
ngs_text_layout); | 109 case PageInfoConnectionType.CONNECTION_UNKNOWN: |
| 99 if (label != null && !label.isEmpty()) { | 110 schemeColorId = R.color.website_settings_popup_url_scheme_http; |
| 100 setCertificateViewer(label); | 111 makeDomainBold = true; |
| 112 break; |
| 113 case PageInfoConnectionType.CONNECTION_ENCRYPTED: |
| 114 schemeColorId = R.color.website_settings_popup_url_scheme_https; |
| 115 break; |
| 116 case PageInfoConnectionType.CONNECTION_MIXED_CONTENT: |
| 117 schemeColorId = R.color.website_settings_popup_url_scheme_mixed; |
| 118 makeDomainBold = true; |
| 119 break; |
| 120 case PageInfoConnectionType.CONNECTION_UNENCRYPTED: |
| 121 schemeColorId = R.color.website_settings_popup_url_scheme_http; |
| 122 makeDomainBold = true; |
| 123 break; |
| 124 case PageInfoConnectionType.CONNECTION_ENCRYPTED_ERROR: |
| 125 schemeColorId = R.color.website_settings_popup_url_scheme_broken
; |
| 126 makeDomainBold = true; |
| 127 break; |
| 128 case PageInfoConnectionType.CONNECTION_INTERNAL_PAGE: |
| 129 schemeColorId = R.color.website_settings_popup_url_scheme_http; |
| 130 break; |
| 131 default: |
| 132 assert false : "Unexpected connection type: " + connectionType; |
| 101 } | 133 } |
| 134 |
| 135 SpannableStringBuilder sb = new SpannableStringBuilder(scheme + "://" +
domain + path); |
| 136 |
| 137 ForegroundColorSpan schemeColorSpan = new ForegroundColorSpan( |
| 138 mContext.getResources().getColor(schemeColorId)); |
| 139 sb.setSpan(schemeColorSpan, 0, scheme.length(), Spannable.SPAN_INCLUSIVE
_INCLUSIVE); |
| 140 |
| 141 int domainStartIndex = scheme.length() + 3; |
| 142 ForegroundColorSpan domainColorSpan = new ForegroundColorSpan( |
| 143 mContext.getResources().getColor(R.color.website_settings_popup_
url_domain)); |
| 144 sb.setSpan(domainColorSpan, domainStartIndex, domainStartIndex + domain.
length(), |
| 145 Spannable.SPAN_INCLUSIVE_INCLUSIVE); |
| 146 |
| 147 if (makeDomainBold) { |
| 148 StyleSpan boldStyleSpan = new StyleSpan(android.graphics.Typeface.BO
LD); |
| 149 sb.setSpan(boldStyleSpan, domainStartIndex, domainStartIndex + domai
n.length(), |
| 150 Spannable.SPAN_INCLUSIVE_INCLUSIVE); |
| 151 } |
| 152 |
| 153 mUrlTitle.setText(sb); |
| 102 } | 154 } |
| 103 | 155 |
| 104 /** | 156 /** |
| 105 * Adds Description section, which contains an icon, a headline, and a | 157 * Sets the connection message displayed at the top of the dialog to |
| 106 * description. Most likely headline for description is empty | 158 * connectionMessage (e.g. "Could not securely connect to this site"). |
| 107 */ | 159 */ |
| 108 @CalledByNative | 160 @CalledByNative |
| 109 private void addDescriptionSection(int enumeratedIconId, String headline, St
ring description) { | 161 private void setConnectionMessage(String connectionMessage) { |
| 110 View section = addSection(enumeratedIconId, headline, description); | 162 mUrlConnectionMessage.setText(connectionMessage); |
| 111 assert mDescriptionLayout == null; | |
| 112 mDescriptionLayout = (ViewGroup) section.findViewById(R.id.website_setti
ngs_text_layout); | |
| 113 } | |
| 114 | |
| 115 private View addSection(int enumeratedIconId, String headline, String descri
ption) { | |
| 116 View section = LayoutInflater.from(mContext).inflate(R.layout.website_se
ttings, null); | |
| 117 ImageView i = (ImageView) section.findViewById(R.id.website_settings_ico
n); | |
| 118 int drawableId = ResourceId.mapToDrawableId(enumeratedIconId); | |
| 119 i.setImageResource(drawableId); | |
| 120 | |
| 121 TextView h = (TextView) section.findViewById(R.id.website_settings_headl
ine); | |
| 122 h.setText(headline); | |
| 123 if (TextUtils.isEmpty(headline)) h.setVisibility(View.GONE); | |
| 124 | |
| 125 TextView d = (TextView) section.findViewById(R.id.website_settings_descr
iption); | |
| 126 d.setText(description); | |
| 127 d.setTextSize(DESCRIPTION_TEXT_SIZE_SP); | |
| 128 if (TextUtils.isEmpty(description)) d.setVisibility(View.GONE); | |
| 129 | |
| 130 mContainer.addView(section); | |
| 131 return section; | |
| 132 } | |
| 133 | |
| 134 private void setCertificateViewer(String label) { | |
| 135 assert mCertificateViewer == null; | |
| 136 mCertificateViewer = new TextView(mContext); | |
| 137 mCertificateViewer.setText(label); | |
| 138 mCertificateViewer.setTextColor( | |
| 139 mContext.getResources().getColor(R.color.website_settings_popup_
text_link)); | |
| 140 mCertificateViewer.setTextSize(DESCRIPTION_TEXT_SIZE_SP); | |
| 141 mCertificateViewer.setOnClickListener(this); | |
| 142 mCertificateViewer.setPadding(0, mPaddingWide, 0, mPaddingWide); | |
| 143 mCertificateLayout.addView(mCertificateViewer); | |
| 144 } | |
| 145 | |
| 146 @CalledByNative | |
| 147 private void addResetCertDecisionsButton(String label) { | |
| 148 assert mNativeWebsiteSettingsPopup != 0; | |
| 149 assert mResetCertDecisionsButton == null; | |
| 150 | |
| 151 mResetCertDecisionsButton = new Button(mContext); | |
| 152 mResetCertDecisionsButton.setText(label); | |
| 153 ApiCompatibilityUtils.setBackgroundForView(mResetCertDecisionsButton, | |
| 154 mContext.getResources().getDrawable( | |
| 155 R.drawable.website_settings_reset_cert_decisions)); | |
| 156 mResetCertDecisionsButton.setTextColor( | |
| 157 mContext.getResources().getColor( | |
| 158 R.color.website_settings_popup_reset_cert_decisions_button)); | |
| 159 mResetCertDecisionsButton.setTextSize(DESCRIPTION_TEXT_SIZE_SP); | |
| 160 mResetCertDecisionsButton.setOnClickListener(this); | |
| 161 | |
| 162 LinearLayout container = new LinearLayout(mContext); | |
| 163 container.setOrientation(LinearLayout.VERTICAL); | |
| 164 container.addView(mResetCertDecisionsButton); | |
| 165 container.setPadding(0, 0, 0, mPaddingWide); | |
| 166 mContainer.addView(container); | |
| 167 } | |
| 168 | |
| 169 @CalledByNative | |
| 170 private void addMoreInfoLink(String linkText) { | |
| 171 addUrl(linkText, HELP_URL); | |
| 172 } | |
| 173 | |
| 174 /** Adds a section containing a description and a hyperlink. */ | |
| 175 private void addUrl(String label, String url) { | |
| 176 mMoreInfoLink = new TextView(mContext); | |
| 177 mLinkUrl = url; | |
| 178 mMoreInfoLink.setText(label); | |
| 179 mMoreInfoLink.setTextColor( | |
| 180 mContext.getResources().getColor(R.color.website_settings_popup_
text_link)); | |
| 181 mMoreInfoLink.setTextSize(DESCRIPTION_TEXT_SIZE_SP); | |
| 182 mMoreInfoLink.setPadding(0, mPaddingWide + mPaddingThin, 0, mPaddingWide
); | |
| 183 mMoreInfoLink.setOnClickListener(this); | |
| 184 mDescriptionLayout.addView(mMoreInfoLink); | |
| 185 } | 163 } |
| 186 | 164 |
| 187 /** Displays the WebsiteSettingsPopup. */ | 165 /** Displays the WebsiteSettingsPopup. */ |
| 188 @CalledByNative | 166 @CalledByNative |
| 189 private void showDialog() { | 167 private void showDialog() { |
| 168 // Wrap the dialog in a ScrollView in case the content is too long. |
| 190 ScrollView scrollView = new ScrollView(mContext); | 169 ScrollView scrollView = new ScrollView(mContext); |
| 191 scrollView.addView(mContainer); | 170 scrollView.addView(mContainer); |
| 192 mDialog.addContentView(scrollView, | 171 mDialog.addContentView(scrollView, new LinearLayout.LayoutParams( |
| 193 new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PA
RENT, | 172 LinearLayout.LayoutParams.MATCH_PARENT, |
| 194 LinearLayout.LayoutParams.MATCH_PARENT)); | 173 LinearLayout.LayoutParams.MATCH_PARENT)); |
| 174 |
| 175 // Make the dialog fill the width of the screen. This must be called |
| 176 // after addContentView, or it won't fully fill to the edge. |
| 177 Window window = mDialog.getWindow(); |
| 178 window.setLayout(ViewGroup.LayoutParams.MATCH_PARENT, |
| 179 ViewGroup.LayoutParams.WRAP_CONTENT); |
| 180 |
| 195 mDialog.show(); | 181 mDialog.show(); |
| 196 } | 182 } |
| 197 | 183 |
| 198 @Override | 184 /** |
| 199 public void onClick(View v) { | 185 * Shows a WebsiteSettings dialog for the provided WebContents. The popup |
| 200 mDialog.dismiss(); | 186 * adds itself to the view hierarchy which owns the reference while it's |
| 201 if (mResetCertDecisionsButton == v) { | 187 * visible. |
| 202 nativeResetCertDecisions(mNativeWebsiteSettingsPopup, mWebContents); | 188 * |
| 203 } else if (mCertificateViewer == v) { | 189 * @param context Context which is used for launching a dialog. |
| 204 byte[][] certChain = nativeGetCertificateChain(mWebContents); | 190 * @param webContents The WebContents for which to show Website information. |
| 205 if (certChain == null) { | 191 * This information is retrieved for the visible entry. |
| 206 // The WebContents may have been destroyed/invalidated. If so, | 192 */ |
| 207 // ignore this request. | 193 @SuppressWarnings("unused") |
| 208 return; | 194 public static void show(Context context, WebContents webContents) { |
| 209 } | 195 if (CommandLine.getInstance().hasSwitch( |
| 210 CertificateViewer.showCertificateChain(mContext, certChain); | 196 ContentSwitches.ENABLE_NEW_WEBSITE_SETTINGS)) { |
| 211 } else if (mMoreInfoLink == v) { | 197 new WebsiteSettingsPopup(context, webContents); |
| 212 try { | 198 } else { |
| 213 Intent i = Intent.parseUri(mLinkUrl, Intent.URI_INTENT_SCHEME); | 199 WebsiteSettingsPopupLegacy.show(context, webContents); |
| 214 i.putExtra(Browser.EXTRA_CREATE_NEW_TAB, true); | |
| 215 i.putExtra(Browser.EXTRA_APPLICATION_ID, mContext.getPackageName
()); | |
| 216 mContext.startActivity(i); | |
| 217 } catch (URISyntaxException ex) { | |
| 218 // Do nothing intentionally. | |
| 219 } | |
| 220 } | 200 } |
| 221 } | 201 } |
| 222 | 202 |
| 223 /** | 203 private static native long nativeInit(WebsiteSettingsPopup popup, |
| 224 * Shows a WebsiteSettings dialog for the provided WebContents. | 204 WebContents webContents); |
| 225 * | |
| 226 * The popup adds itself to the view hierarchy which owns the reference whil
e it's | |
| 227 * visible. | |
| 228 * | |
| 229 * @param context Context which is used for launching a dialog. | |
| 230 * @param webContents The WebContents for which to show Website information.
This | |
| 231 * information is retrieved for the visible entry. | |
| 232 */ | |
| 233 @SuppressWarnings("unused") | |
| 234 public static void show(Context context, WebContents webContents) { | |
| 235 new WebsiteSettingsPopup(context, webContents); | |
| 236 } | |
| 237 | 205 |
| 238 private static native long nativeInit(WebsiteSettingsPopup popup, WebContent
s webContents); | |
| 239 private native void nativeDestroy(long nativeWebsiteSettingsPopupAndroid); | 206 private native void nativeDestroy(long nativeWebsiteSettingsPopupAndroid); |
| 240 private native void nativeResetCertDecisions( | |
| 241 long nativeWebsiteSettingsPopupAndroid, WebContents webContents); | |
| 242 private native byte[][] nativeGetCertificateChain(WebContents webContents); | |
| 243 } | 207 } |
| OLD | NEW |