blob: 3366b4e5316a47ef92bb1cf12a067c2fac40d57a [file] [log] [blame]
// Copyright 2013 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.chrome.browser;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.app.Dialog;
import android.content.Context;
import android.content.DialogInterface;
import android.graphics.Color;
import android.graphics.drawable.ColorDrawable;
import android.text.Layout;
import android.text.Spannable;
import android.text.SpannableString;
import android.text.SpannableStringBuilder;
import android.text.style.ForegroundColorSpan;
import android.text.style.StyleSpan;
import android.util.AttributeSet;
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.chrome.R;
import org.chromium.chrome.browser.omnibox.OmniboxUrlEmphasizer;
import org.chromium.chrome.browser.profiles.Profile;
import org.chromium.chrome.browser.toolbar.ToolbarModel;
import org.chromium.chrome.browser.ui.toolbar.ToolbarModelSecurityLevel;
import org.chromium.content_public.browser.WebContents;
import org.chromium.content_public.browser.WebContentsObserver;
import org.chromium.ui.base.Clipboard;
import org.chromium.ui.base.DeviceFormFactor;
import org.chromium.ui.interpolators.BakedBezierInterpolator;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.ArrayList;
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 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;
}
}
/**
* A TextView which truncates and displays a URL such that the origin is always visible.
* The URL can be expanded by clicking on the it.
*/
public static class ElidedUrlTextView extends TextView {
// The number of lines to display when the URL is truncated. This number
// should still allow the origin to be displayed. NULL before
// setUrlAfterLayout() is called.
private Integer mTruncatedUrlLinesToDisplay;
// The number of lines to display when the URL is expanded. This should be enough to display
// at most two lines of the fragment if there is one in the URL.
private Integer mFullLinesToDisplay;
// If true, the text view will show the truncated text. If false, it
// will show the full, expanded text.
private boolean mIsShowingTruncatedText = true;
// The profile to use when getting the end index for the origin.
private Profile mProfile = null;
// The maximum number of lines currently shown in the view
private int mCurrentMaxLines = Integer.MAX_VALUE;
/** Constructor for inflating from XML. */
public ElidedUrlTextView(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
public void setMaxLines(int maxlines) {
super.setMaxLines(maxlines);
mCurrentMaxLines = maxlines;
}
/**
* Find the number of lines of text which must be shown in order to display the character at
* a given index.
*/
private int getLineForIndex(int index) {
Layout layout = getLayout();
int endLine = 0;
while (endLine < layout.getLineCount() && layout.getLineEnd(endLine) < index) {
endLine++;
}
// Since endLine is an index, add 1 to get the number of lines.
return endLine + 1;
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
setMaxLines(Integer.MAX_VALUE);
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
assert mProfile != null : "setProfile() must be called before layout.";
String urlText = getText().toString();
// Lay out the URL in a StaticLayout that is the same size as our final
// container.
int originEndIndex = OmniboxUrlEmphasizer.getOriginEndIndex(urlText, mProfile);
// Find the range of lines containing the origin.
int originEndLine = getLineForIndex(originEndIndex);
// Display an extra line so we don't accidentally hide the origin with
// ellipses
mTruncatedUrlLinesToDisplay = originEndLine + 1;
// Find the line where the fragment starts. Since # is a reserved character, it is safe
// to just search for the first # to appear in the url.
int fragmentStartIndex = urlText.indexOf('#');
if (fragmentStartIndex == -1) fragmentStartIndex = urlText.length();
int fragmentStartLine = getLineForIndex(fragmentStartIndex);
mFullLinesToDisplay = fragmentStartLine + 1;
// If there is no origin (according to OmniboxUrlEmphasizer), make sure the fragment is
// still hidden correctly.
if (mFullLinesToDisplay < mTruncatedUrlLinesToDisplay) {
mTruncatedUrlLinesToDisplay = mFullLinesToDisplay;
}
if (updateMaxLines()) super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
/**
* Sets the profile to use when calculating the end index of the origin.
* Must be called before layout.
*
* @param profile The profile to use when coloring the URL.
*/
public void setProfile(Profile profile) {
mProfile = profile;
}
/**
* Toggles truncating/expanding the URL text. If the URL text is not
* truncated, has no effect.
*/
public void toggleTruncation() {
mIsShowingTruncatedText = !mIsShowingTruncatedText;
updateMaxLines();
}
private boolean updateMaxLines() {
int maxLines = mFullLinesToDisplay;
if (mIsShowingTruncatedText) maxLines = mTruncatedUrlLinesToDisplay;
if (maxLines != mCurrentMaxLines) {
setMaxLines(maxLines);
return true;
}
return false;
}
}
// Delay enter to allow the triggering button to animate before we cover it.
private static final int ENTER_START_DELAY = 100;
private static final int FADE_DURATION = 200;
private static final int FADE_IN_BASE_DELAY = 150;
private static final int FADE_IN_DELAY_OFFSET = 20;
private static final int CLOSE_CLEANUP_DELAY = 10;
private static final int MAX_TABLET_DIALOG_WIDTH_DP = 400;
private final Context mContext;
private final Profile mProfile;
private final WebContents mWebContents;
// A pointer to the C++ object for this UI.
private final long mNativeWebsiteSettingsPopup;
// The outer container, filled with the layout from website_settings.xml.
private final LinearLayout mContainer;
// UI elements in the dialog.
private final ElidedUrlTextView mUrlTitle;
private final TextView mUrlConnectionMessage;
private final LinearLayout mPermissionsList;
private final Button mCopyUrlButton;
private final Button mSiteSettingsButton;
private final View mHorizontalSeparator;
private final View mLowerDialogArea;
// The dialog the container is placed in.
private final Dialog mDialog;
// Animation which is currently running, if there is one.
private AnimatorSet mCurrentAnimation = null;
// The full URL from the URL bar, which is copied to the user's clipboard when they select 'Copy
// URL'.
private String mFullUrl;
// A parsed version of mFullUrl. Is null if the URL is invalid/cannot be
// parsed.
private URI mParsedUrl;
// Whether or not this page is an internal chrome page (e.g. the
// chrome://settings page).
private boolean mIsInternalPage;
// The security level of the page (a valid ToolbarModelSecurityLevel).
private int mSecurityLevel;
// Whether the security level of the page was deprecated due to SHA-1.
private boolean mDeprecatedSHA1Present;
/**
* 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, Profile profile, WebContents webContents) {
mContext = context;
mProfile = profile;
mWebContents = webContents;
// Find the container and all it's important subviews.
mContainer = (LinearLayout) LayoutInflater.from(mContext).inflate(
R.layout.website_settings, null);
mContainer.setVisibility(View.INVISIBLE);
mContainer.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
public void onLayoutChange(
View v, int l, int t, int r, int b, int ol, int ot, int or, int ob) {
// Trigger the entrance animations once the main container has been laid out and has
// a height.
mContainer.removeOnLayoutChangeListener(this);
mContainer.setVisibility(View.VISIBLE);
createAllAnimations(true).start();
}
});
mUrlTitle = (ElidedUrlTextView) mContainer.findViewById(R.id.website_settings_url);
mUrlTitle.setProfile(mProfile);
mUrlTitle.setOnClickListener(this);
mUrlConnectionMessage = (TextView) mContainer
.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 for well-formed, non-internal URLs.
mSiteSettingsButton.setVisibility(View.GONE);
mHorizontalSeparator = mContainer
.findViewById(R.id.website_settings_horizontal_separator);
mLowerDialogArea = mContainer.findViewById(R.id.website_settings_lower_dialog_area);
// Hide the horizontal separator for sites with no permissions.
// TODO(sashab,finnur): Show this for all sites with either the site settings button or
// permissions (ie when the bottom area of the dialog is not empty).
setVisibilityOfLowerDialogArea(false);
// Create the dialog.
mDialog = new Dialog(mContext) {
private void superDismiss() {
super.dismiss();
}
@Override
public void dismiss() {
if (DeviceFormFactor.isTablet(mContext)) {
// Dismiss the dialog without any custom animations on tablet.
super.dismiss();
} else {
Animator animator = createAllAnimations(false);
animator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
// onAnimationEnd is called during the final frame of the animation.
// Delay the cleanup by a tiny amount to give this frame a chance to be
// displayed before we destroy the dialog.
mContainer.postDelayed(new Runnable() {
@Override
public void run() {
superDismiss();
}
}, CLOSE_CLEANUP_DELAY);
}
});
animator.start();
}
}
};
mDialog.requestWindowFeature(Window.FEATURE_NO_TITLE);
mDialog.setCanceledOnTouchOutside(true);
// On smaller screens, place the dialog at the top of the screen, and remove its border.
if (!DeviceFormFactor.isTablet(mContext)) {
Window window = mDialog.getWindow();
window.setGravity(Gravity.TOP);
window.setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
}
// This needs to come after other member initialization.
mNativeWebsiteSettingsPopup = nativeInit(this, webContents);
final WebContentsObserver webContentsObserver = new WebContentsObserver(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.
mDialog.dismiss();
}
};
mDialog.setOnDismissListener(new DialogInterface.OnDismissListener() {
@Override
public void onDismiss(DialogInterface dialog) {
assert mNativeWebsiteSettingsPopup != 0;
webContentsObserver.destroy();
nativeDestroy(mNativeWebsiteSettingsPopup);
}
});
// Work out the URL and connection message.
mFullUrl = mWebContents.getVisibleUrl();
try {
mParsedUrl = new URI(mFullUrl);
mIsInternalPage = UrlUtilities.isInternalScheme(mParsedUrl);
} catch (URISyntaxException e) {
mParsedUrl = null;
mIsInternalPage = false;
}
mSecurityLevel = ToolbarModel.getSecurityLevelForWebContents(mWebContents);
mDeprecatedSHA1Present = ToolbarModel.isDeprecatedSHA1Present(mWebContents);
SpannableStringBuilder urlBuilder = new SpannableStringBuilder(mFullUrl);
OmniboxUrlEmphasizer.emphasizeUrl(urlBuilder, mContext.getResources(), mProfile,
mSecurityLevel, mIsInternalPage, true);
mUrlTitle.setText(urlBuilder);
// Set the URL connection message now, and the URL after layout (so it
// can calculate its ideal height).
mUrlConnectionMessage.setText(getUrlConnectionMessage());
if (isConnectionDetailsLinkVisible()) mUrlConnectionMessage.setOnClickListener(this);
}
/**
* Sets the visibility of the lower area of the dialog (containing the permissions and 'Site
* Settings' button).
*
* @param isVisible Whether to show or hide the dialog area.
*/
private void setVisibilityOfLowerDialogArea(boolean isVisible) {
mHorizontalSeparator.setVisibility(isVisible ? View.VISIBLE : View.GONE);
mLowerDialogArea.setVisibility(isVisible ? View.VISIBLE : View.GONE);
}
/**
* 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.
*/
private int getImageResourceForPermission(int permission) {
switch (permission) {
case ContentSettingsType.CONTENT_SETTINGS_TYPE_IMAGES:
return R.drawable.permission_images;
case ContentSettingsType.CONTENT_SETTINGS_TYPE_JAVASCRIPT:
return R.drawable.permission_javascript;
case ContentSettingsType.CONTENT_SETTINGS_TYPE_GEOLOCATION:
return R.drawable.permission_location;
case ContentSettingsType.CONTENT_SETTINGS_TYPE_MEDIASTREAM_CAMERA:
return R.drawable.permission_camera;
case ContentSettingsType.CONTENT_SETTINGS_TYPE_MEDIASTREAM_MIC:
return R.drawable.permission_mic;
case ContentSettingsType.CONTENT_SETTINGS_TYPE_NOTIFICATIONS:
return R.drawable.permission_push_notification;
case ContentSettingsType.CONTENT_SETTINGS_TYPE_POPUPS:
return R.drawable.permission_popups;
default:
assert false : "Icon requested for invalid permission: " + permission;
return -1;
}
}
/**
* Gets the message to display in the connection message box for the given security level. Does
* not apply to SECURITY_ERROR pages, since these have their own coloured/formatted message.
*
* @param toolbarModelSecurityLevel A valid ToolbarModelSecurityLevel, which is the security
* level of the page.
* @param isInternalPage Whether or not this page is an internal chrome page (e.g. the
* chrome://settings page).
* @return The ID of the message to display in the connection message box.
*/
private int getConnectionMessageId(int toolbarModelSecurityLevel, boolean isInternalPage) {
if (isInternalPage) return R.string.page_info_connection_internal_page;
switch (toolbarModelSecurityLevel) {
case ToolbarModelSecurityLevel.NONE:
return R.string.page_info_connection_http;
case ToolbarModelSecurityLevel.SECURE:
case ToolbarModelSecurityLevel.EV_SECURE:
return R.string.page_info_connection_https;
case ToolbarModelSecurityLevel.SECURITY_WARNING:
case ToolbarModelSecurityLevel.SECURITY_POLICY_WARNING:
return R.string.page_info_connection_mixed;
default:
assert false : "Invalid security level specified: " + toolbarModelSecurityLevel;
return R.string.page_info_connection_http;
}
}
/**
* Whether to show a 'Details' link to the connection info popup. The link is only shown for
* HTTPS connections.
*/
private boolean isConnectionDetailsLinkVisible() {
return !mIsInternalPage && mSecurityLevel != ToolbarModelSecurityLevel.NONE;
}
/**
* Gets the styled connection message to display below the URL.
*/
private Spannable getUrlConnectionMessage() {
// Display the appropriate connection message.
SpannableStringBuilder messageBuilder = new SpannableStringBuilder();
if (mDeprecatedSHA1Present) {
messageBuilder.append(
mContext.getResources().getString(R.string.page_info_connection_sha1));
} else if (mSecurityLevel != ToolbarModelSecurityLevel.SECURITY_ERROR) {
messageBuilder.append(mContext.getResources().getString(
getConnectionMessageId(mSecurityLevel, mIsInternalPage)));
} else {
String originToDisplay;
try {
URI parsedUrl = new URI(mFullUrl);
originToDisplay = UrlUtilities.getOriginForDisplay(parsedUrl, false);
} catch (URISyntaxException e) {
// The URL is invalid - just display the full URL.
originToDisplay = mFullUrl;
}
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, originToDisplay);
messageBuilder.append(leadingText + " " + followingText);
final ForegroundColorSpan redSpan = new ForegroundColorSpan(mContext.getResources()
.getColor(R.color.website_settings_connection_broken_leading_text));
final StyleSpan boldSpan = new StyleSpan(android.graphics.Typeface.BOLD);
messageBuilder.setSpan(redSpan, 0, leadingText.length(),
Spannable.SPAN_INCLUSIVE_EXCLUSIVE);
messageBuilder.setSpan(boldSpan, 0, leadingText.length(),
Spannable.SPAN_INCLUSIVE_EXCLUSIVE);
}
if (isConnectionDetailsLinkVisible()) {
messageBuilder.append(" ");
SpannableString detailsText = new SpannableString(
mContext.getResources().getString(R.string.page_info_details_link));
final ForegroundColorSpan blueSpan = new ForegroundColorSpan(
mContext.getResources().getColor(R.color.website_settings_popup_text_link));
detailsText.setSpan(
blueSpan, 0, detailsText.length(), Spannable.SPAN_INCLUSIVE_EXCLUSIVE);
messageBuilder.append(detailsText);
}
return messageBuilder;
}
/**
* 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 addPermissionSection(String name, int type, int currentSetting) {
// We have at least one permission, so show the lower permissions area.
setVisibilityOfLowerDialogArea(true);
View permissionRow = 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.
*/
@CalledByNative
private void showDialog() {
if (!DeviceFormFactor.isTablet(mContext)) {
// On smaller screens, make the dialog fill the width of the screen.
ScrollView scrollView = new ScrollView(mContext);
scrollView.addView(mContainer);
mDialog.addContentView(scrollView, new LinearLayout.LayoutParams(
LinearLayout.LayoutParams.MATCH_PARENT,
LinearLayout.LayoutParams.MATCH_PARENT));
// 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);
} else {
// On larger screens, make the dialog centered in the screen and have a maximum width.
ScrollView scrollView = new ScrollView(mContext) {
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
final int maxDialogWidthInPx = (int) (MAX_TABLET_DIALOG_WIDTH_DP
* mContext.getResources().getDisplayMetrics().density);
if (MeasureSpec.getSize(widthMeasureSpec) > maxDialogWidthInPx) {
widthMeasureSpec = MeasureSpec.makeMeasureSpec(maxDialogWidthInPx,
MeasureSpec.EXACTLY);
}
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
};
scrollView.addView(mContainer);
mDialog.addContentView(scrollView, new LinearLayout.LayoutParams(
LinearLayout.LayoutParams.WRAP_CONTENT,
LinearLayout.LayoutParams.MATCH_PARENT));
}
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 == mUrlTitle) {
// Expand/collapse the displayed URL title.
mUrlTitle.toggleTruncation();
} else if (view == mUrlConnectionMessage) {
if (DeviceFormFactor.isTablet(mContext)) {
ConnectionInfoPopup.show(mContext, mWebContents);
} else {
// Delay while the WebsiteSettingsPopup closes.
mContainer.postDelayed(new Runnable() {
@Override
public void run() {
ConnectionInfoPopup.show(mContext, mWebContents);
}
}, FADE_DURATION + CLOSE_CLEANUP_DELAY);
}
mDialog.dismiss();
}
}
/**
* Create a list of all the views which we want to individually fade in.
*/
private List<View> collectAnimatableViews() {
List<View> animatableViews = new ArrayList<View>();
animatableViews.add(mUrlTitle);
animatableViews.add(mUrlConnectionMessage);
animatableViews.add(mCopyUrlButton);
animatableViews.add(mHorizontalSeparator);
for (int i = 0; i < mPermissionsList.getChildCount(); i++) {
animatableViews.add(mPermissionsList.getChildAt(i));
}
return animatableViews;
}
/**
* Create an animator to fade an individual dialog element.
*/
private Animator createInnerFadeAnimator(final View view, int position, boolean isEnter) {
ObjectAnimator alphaAnim;
if (isEnter) {
view.setAlpha(0f);
alphaAnim = ObjectAnimator.ofFloat(view, View.ALPHA, 1f);
alphaAnim.setStartDelay(FADE_IN_BASE_DELAY + FADE_IN_DELAY_OFFSET * position);
} else {
alphaAnim = ObjectAnimator.ofFloat(view, View.ALPHA, 0f);
}
alphaAnim.setDuration(FADE_DURATION);
return alphaAnim;
}
/**
* Create an animator to slide in the entire dialog from the top of the screen.
*/
private Animator createDialogSlideAnimator(boolean isEnter) {
final float animHeight = -1f * mContainer.getHeight();
ObjectAnimator translateAnim;
if (isEnter) {
mContainer.setTranslationY(animHeight);
translateAnim = ObjectAnimator.ofFloat(mContainer, View.TRANSLATION_Y, 0f);
translateAnim.setInterpolator(BakedBezierInterpolator.FADE_IN_CURVE);
} else {
translateAnim = ObjectAnimator.ofFloat(mContainer, View.TRANSLATION_Y, animHeight);
translateAnim.setInterpolator(BakedBezierInterpolator.FADE_OUT_CURVE);
}
translateAnim.setDuration(FADE_DURATION);
return translateAnim;
}
/**
* Create animations for showing/hiding the popup.
*
* Tablets use the default Dialog fade-in instead of sliding in manually.
*/
private Animator createAllAnimations(boolean isEnter) {
AnimatorSet animation = new AnimatorSet();
AnimatorSet.Builder builder = null;
Animator startAnim;
if (DeviceFormFactor.isTablet(mContext)) {
// The start time of the entire AnimatorSet is the start time of the first animation
// added to the Builder. We use a blank AnimatorSet on tablet as an easy way to
// co-ordinate this start time.
startAnim = new AnimatorSet();
} else {
startAnim = createDialogSlideAnimator(isEnter);
}
if (isEnter) startAnim.setStartDelay(ENTER_START_DELAY);
builder = animation.play(startAnim);
List<View> animatableViews = collectAnimatableViews();
for (int i = 0; i < animatableViews.size(); i++) {
View view = animatableViews.get(i);
Animator anim = createInnerFadeAnimator(view, i, isEnter);
builder.with(anim);
}
animation.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
mCurrentAnimation = null;
}
});
if (mCurrentAnimation != null) mCurrentAnimation.cancel();
mCurrentAnimation = animation;
return animation;
}
/**
* 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.
*/
@SuppressWarnings("unused")
public static void show(Context context, Profile profile, WebContents webContents) {
new WebsiteSettingsPopup(context, profile, 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);
}