Bug 1173887 - Only display one title + favicon for the first doorhanger in a popup. r=ally, a=Kwierso

This commit is contained in:
Chenxia Liu 2015-06-16 14:03:09 -07:00
parent b0d5decf79
commit 080b279523
13 changed files with 71 additions and 125 deletions

View File

@ -277,15 +277,21 @@ public class DoorHangerPopup extends AnchoredPopup
// Show doorhangers for the selected tab
int tabId = tab.getId();
boolean shouldShowPopup = false;
DoorHanger firstDoorhanger = null;
for (DoorHanger dh : mDoorHangers) {
if (dh.getTabId() == tabId) {
dh.setVisibility(View.VISIBLE);
shouldShowPopup = true;
if (firstDoorhanger == null) {
firstDoorhanger = dh;
} else {
dh.hideTitle();
}
} else {
dh.setVisibility(View.GONE);
}
}
// Dismiss the popup if there are no doorhangers to show for this tab
if (!shouldShowPopup) {
dismiss();
@ -298,6 +304,8 @@ public class DoorHangerPopup extends AnchoredPopup
return;
}
firstDoorhanger.showTitle(tab.getFavicon(), tab.getBaseDomain());
// Make the popup focusable for accessibility. This gets done here
// so the node can be accessibility focused, but on pre-ICS devices this
// causes crashes, so it is done after the popup is shown.

View File

@ -8,7 +8,7 @@
<LinearLayout android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:padding="@dimen/doorhanger_padding">
android:padding="@dimen/doorhanger_section_padding_small">
<ImageView android:id="@+id/doorhanger_icon"
android:layout_width="@dimen/doorhanger_icon_size"
@ -17,9 +17,22 @@
android:paddingRight="@dimen/doorhanger_section_padding_small"
android:visibility="gone"/>
<ViewStub android:id="@+id/content"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
<LinearLayout android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView android:id="@+id/doorhanger_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="@dimen/doorhanger_section_padding_small"
android:textAppearance="@style/TextAppearance.DoorHanger.Medium.Light"
android:visibility="gone"/>
<ViewStub android:id="@+id/content"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
</LinearLayout>
</LinearLayout>

View File

@ -8,12 +8,6 @@
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView android:id="@+id/doorhanger_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="@dimen/doorhanger_section_padding_small"
android:textAppearance="@style/TextAppearance.DoorHanger.Medium.Light"/>
<TextView android:id="@+id/doorhanger_message"
android:focusable="true"
android:layout_width="match_parent"

View File

@ -6,7 +6,7 @@
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="@dimen/doorhanger_padding"
android:padding="@dimen/doorhanger_section_padding_small"
android:orientation="vertical">
<EditText android:id="@+id/username_edit"
@ -25,6 +25,6 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/doorhanger_login_edit_toggle"
android:layout_marginTop="@dimen/doorhanger_padding"/>
android:layout_marginTop="@dimen/doorhanger_subsection_padding"/>
</LinearLayout>

View File

@ -11,13 +11,13 @@
<LinearLayout android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:padding="@dimen/doorhanger_padding">
android:padding="@dimen/doorhanger_section_padding_small">
<ImageView android:id="@+id/larry"
android:layout_width="@dimen/doorhanger_icon_size"
android:layout_height="@dimen/doorhanger_icon_size"
android:src="@drawable/larry"
android:paddingRight="@dimen/doorhanger_padding"/>
android:paddingRight="@dimen/doorhanger_section_padding_small"/>
<LinearLayout android:layout_width="0dp"
android:layout_height="wrap_content"
@ -35,7 +35,7 @@
<TextView android:id="@+id/site_identity_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="@dimen/doorhanger_section_padding_small"
android:layout_marginBottom="@dimen/doorhanger_subsection_padding"
android:textAppearance="@style/TextAppearance.DoorHanger.Medium.Light"/>
<TextView android:id="@+id/site_identity_encrypted"
@ -83,7 +83,7 @@
android:textAppearance="@style/TextAppearance.DoorHanger.Medium"
android:textColor="@color/link_blue"
android:layout_marginTop="@dimen/doorhanger_section_padding_large"
android:layout_marginBottom="@dimen/doorhanger_padding"
android:layout_marginBottom="@dimen/doorhanger_section_padding_small"
android:text="@string/contextmenu_site_settings"
android:visibility="gone"/>
</LinearLayout>

View File

@ -102,11 +102,11 @@
<dimen name="doorhanger_width">300dp</dimen>
<dimen name="doorhanger_input_width">250dp</dimen>
<dimen name="doorhanger_spinner_textsize">9sp</dimen>
<dimen name="doorhanger_padding">15dp</dimen>
<dimen name="doorhanger_offsetX">12dp</dimen>
<dimen name="doorhanger_offsetY">67dp</dimen>
<dimen name="doorhanger_GB_offsetY">7dp</dimen>
<dimen name="doorhanger_drawable_padding">5dp</dimen>
<dimen name="doorhanger_subsection_padding">8dp</dimen>
<dimen name="doorhanger_section_padding_small">20dp</dimen>
<dimen name="doorhanger_section_padding_large">30dp</dimen>
<dimen name="doorhanger_icon_size">60dp</dimen>

View File

@ -8,6 +8,8 @@ import android.content.ClipData;
import android.content.ClipboardManager;
import android.graphics.Bitmap;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.widget.ImageView;
import android.widget.Toast;
import org.json.JSONException;
import org.json.JSONArray;
@ -23,8 +25,6 @@ import org.mozilla.gecko.SiteIdentity.MixedMode;
import org.mozilla.gecko.SiteIdentity.TrackingMode;
import org.mozilla.gecko.Tab;
import org.mozilla.gecko.Tabs;
import org.mozilla.gecko.favicons.Favicons;
import org.mozilla.gecko.favicons.OnFaviconLoadedListener;
import org.mozilla.gecko.util.GeckoEventListener;
import org.mozilla.gecko.util.ThreadUtils;
import org.mozilla.gecko.widget.AnchoredPopup;
@ -174,10 +174,9 @@ public class SiteIdentityPopup extends AnchoredPopup implements GeckoEventListen
}
private void addLoginsToTab(JSONObject data) throws JSONException {
final JSONObject titleObj = data.getJSONObject("title");
final JSONArray logins = data.getJSONArray("logins");
final SiteLogins siteLogins = new SiteLogins(titleObj, logins);
final SiteLogins siteLogins = new SiteLogins(logins);
Tabs.getInstance().getSelectedTab().setSiteLogins(siteLogins);
}
@ -241,8 +240,6 @@ public class SiteIdentityPopup extends AnchoredPopup implements GeckoEventListen
// Set options.
final JSONObject options = new JSONObject();
final JSONObject titleObj = siteLogins.getTitle();
options.put("title", titleObj);
// Add action text only if there are other logins to select.
if (logins.length() > 1) {
@ -293,23 +290,6 @@ public class SiteIdentityPopup extends AnchoredPopup implements GeckoEventListen
}
private void updateIdentityInformation(final SiteIdentity siteIdentity) {
final String host = siteIdentity.getHost();
mTitle.setText(host);
final String hostUrl = siteIdentity.getOrigin();
Favicons.getSizedFaviconForPageFromLocal(mContext, hostUrl, 32, new OnFaviconLoadedListener() {
@Override
public void onFaviconLoaded(String url, String faviconURL, Bitmap favicon) {
if (favicon == null) {
// If there is no favicon, clear the compound drawable.
mTitle.setCompoundDrawablesWithIntrinsicBounds(null, null, null, null);
} else {
mTitle.setCompoundDrawablesWithIntrinsicBounds(new BitmapDrawable(mContext.getResources(), favicon), null, null, null);
mTitle.setCompoundDrawablePadding((int) mContext.getResources().getDimension(R.dimen.doorhanger_drawable_padding));
}
}
});
mEncrypted.setVisibility(siteIdentity.getEncrypted() ? View.VISIBLE : View.GONE);
mHost.setText(siteIdentity.getHost());
@ -451,6 +431,13 @@ public class SiteIdentityPopup extends AnchoredPopup implements GeckoEventListen
Log.e(LOGTAG, "Error adding selectLogin doorhanger", e);
}
mTitle.setText(selectedTab.getBaseDomain());
final Bitmap favicon = selectedTab.getFavicon();
if (favicon != null) {
mTitle.setCompoundDrawablesWithIntrinsicBounds(new BitmapDrawable(mContext.getResources(), favicon), null, null, null);
mTitle.setCompoundDrawablePadding((int) mContext.getResources().getDimension(R.dimen.doorhanger_drawable_padding));
}
showDividers();
super.show();
@ -491,6 +478,7 @@ public class SiteIdentityPopup extends AnchoredPopup implements GeckoEventListen
removeMixedContentNotification();
removeTrackingContentNotification();
removeSelectLoginDoorhanger();
mTitle.setCompoundDrawablesWithIntrinsicBounds(null, null, null, null);
mDivider.setVisibility(View.GONE);
}

View File

@ -109,7 +109,7 @@ public class DefaultDoorHanger extends DoorHanger {
PromptInput input = PromptInput.getInput(inputs.getJSONObject(i));
mInputs.add(input);
final int padding = mResources.getDimensionPixelSize(R.dimen.doorhanger_padding);
final int padding = mResources.getDimensionPixelSize(R.dimen.doorhanger_section_padding_small);
View v = input.getView(getContext());
styleInput(input, v);
v.setPadding(0, 0, 0, padding);
@ -198,7 +198,7 @@ public class DefaultDoorHanger extends DoorHanger {
if (input instanceof PromptInput.MenulistInput) {
styleDropdownInputs(input, view);
}
view.setPadding(0, 0, 0, mResources.getDimensionPixelSize(R.dimen.doorhanger_padding));
view.setPadding(0, 0, 0, mResources.getDimensionPixelSize(R.dimen.doorhanger_subsection_padding));
}
private void styleDropdownInputs(PromptInput input, View view) {

View File

@ -7,14 +7,19 @@ package org.mozilla.gecko.widget;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewStub;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
import org.json.JSONObject;
import org.mozilla.gecko.R;
import org.mozilla.gecko.Tabs;
public abstract class DoorHanger extends LinearLayout {
@ -54,6 +59,7 @@ public abstract class DoorHanger extends LinearLayout {
protected final Type mType;
protected final ImageView mIcon;
protected final TextView mDoorhangerTitle;
protected final Context mContext;
protected final Resources mResources;
@ -78,6 +84,7 @@ public abstract class DoorHanger extends LinearLayout {
mDivider = findViewById(R.id.divider_doorhanger);
mIcon = (ImageView) findViewById(R.id.doorhanger_icon);
mDoorhangerTitle = (TextView) findViewById(R.id.doorhanger_title);
mNegativeButton = (Button) findViewById(R.id.doorhanger_button_negative);
mPositiveButton = (Button) findViewById(R.id.doorhanger_button_positive);
@ -175,4 +182,17 @@ public abstract class DoorHanger extends LinearLayout {
return true;
}
public void showTitle(Bitmap favicon, String title) {
mDoorhangerTitle.setText(title);
mDoorhangerTitle.setCompoundDrawablesWithIntrinsicBounds(new BitmapDrawable(getResources(), favicon), null, null, null);
if (favicon != null) {
mDoorhangerTitle.setCompoundDrawablePadding((int) mContext.getResources().getDimension(R.dimen.doorhanger_drawable_padding));
}
mDoorhangerTitle.setVisibility(VISIBLE);
}
public void hideTitle() {
mDoorhangerTitle.setVisibility(GONE);
}
}

View File

@ -35,7 +35,6 @@ public class LoginDoorHanger extends DoorHanger {
private static final String LOGTAG = "LoginDoorHanger";
private enum ActionType { EDIT, SELECT }
private final TextView mTitle;
private final TextView mMessage;
private final TextView mLink;
private final DoorhangerConfig.ButtonConfig mButtonConfig;
@ -43,7 +42,6 @@ public class LoginDoorHanger extends DoorHanger {
public LoginDoorHanger(Context context, DoorhangerConfig config) {
super(context, config, Type.LOGIN);
mTitle = (TextView) findViewById(R.id.doorhanger_title);
mMessage = (TextView) findViewById(R.id.doorhanger_message);
mLink = (TextView) findViewById(R.id.doorhanger_link);
mIcon.setImageResource(R.drawable.icon_key);
@ -76,30 +74,6 @@ public class LoginDoorHanger extends DoorHanger {
protected void setOptions(final JSONObject options) {
super.setOptions(options);
final JSONObject titleObj = options.optJSONObject("title");
if (titleObj != null) {
try {
final String text = titleObj.getString("text");
mTitle.setText(text);
} catch (JSONException e) {
Log.e(LOGTAG, "Error loading title from options JSON", e);
}
final String resource = titleObj.optString("resource");
if (resource != null) {
Favicons.getSizedFaviconForPageFromLocal(mContext, resource, 32, new OnFaviconLoadedListener() {
@Override
public void onFaviconLoaded(String url, String faviconURL, Bitmap favicon) {
if (favicon != null) {
mTitle.setCompoundDrawablesWithIntrinsicBounds(new BitmapDrawable(mResources, favicon), null, null, null);
mTitle.setCompoundDrawablePadding((int) mResources.getDimension(R.dimen.doorhanger_drawable_padding));
}
}
});
}
}
final JSONObject actionText = options.optJSONObject("actionText");
addActionText(actionText);
}

View File

@ -4,19 +4,13 @@ import org.json.JSONArray;
import org.json.JSONObject;
public class SiteLogins {
private final JSONObject titleObj;
private final JSONArray logins;
public SiteLogins(JSONObject titleObj, JSONArray logins) {
public SiteLogins(JSONArray logins) {
this.logins = logins;
this.titleObj = titleObj;
}
public JSONArray getLogins() {
return logins;
}
public JSONObject getTitle() {
return titleObj;
}
}

View File

@ -2330,14 +2330,7 @@ var NativeWindow = {
*
* checkbox: A string to appear next to a checkbox under the notification
* message. The button callback functions will be called with
* the checked state as an argument.
*
* title: An object that specifies text to display as the title, and
* optionally a resource, such as a favicon cache url that can be
* used to fetch a favicon from the FaviconCache. (This can be
* generalized to other resources if the situation arises.)
* { text: <title>,
* resource: <resource_url> }
* the checked state as an argument.
*
* actionText: An object that specifies a clickable string, a type of action,
* and a bundle blob for the consumer to create a click action.

View File

@ -140,8 +140,6 @@ LoginManagerPrompter.prototype = {
* _showLoginNotification
*
* Displays a notification doorhanger.
* @param aTitle
* Object with title and optional resource to display with the title, such as a favicon key
* @param aBody
* String message to be displayed in the doorhanger
* @param aButtons
@ -151,7 +149,7 @@ LoginManagerPrompter.prototype = {
* @param aPassword
* Password string used in creating a doorhanger action
*/
_showLoginNotification : function (aTitle, aBody, aButtons, aUsername, aPassword) {
_showLoginNotification : function (aBody, aButtons, aUsername, aPassword) {
let notifyWin = this._window.top;
let chromeWin = this._getChromeWindow(notifyWin).wrappedJSObject;
let browser = chromeWin.BrowserApp.getBrowserForWindow(notifyWin);
@ -174,7 +172,6 @@ LoginManagerPrompter.prototype = {
let options = {
persistWhileVisible: true,
timeout: Date.now() + 10000,
title: aTitle,
actionText: actionText
}
@ -196,9 +193,6 @@ LoginManagerPrompter.prototype = {
let brandShortName = this._strBundle.brand.GetStringFromName("brandShortName");
let notificationText = this._getLocalizedString("saveLogin", [brandShortName]);
let displayHost = this._getShortDisplayHost(aLogin.hostname);
let title = { text: displayHost, resource: aLogin.hostname };
let username = aLogin.username ? this._sanitizeUsername(aLogin.username) : "";
// The callbacks in |buttons| have a closure to access the variables
@ -230,7 +224,7 @@ LoginManagerPrompter.prototype = {
}
];
this._showLoginNotification(title, notificationText, buttons, aLogin.username, aLogin.password);
this._showLoginNotification(notificationText, buttons, aLogin.username, aLogin.password);
},
/*
@ -261,9 +255,6 @@ LoginManagerPrompter.prototype = {
notificationText = this._getLocalizedString("updatePasswordNoUser");
}
let displayHost = this._getShortDisplayHost(aOldLogin.hostname);
let title = { text: displayHost, resource: aOldLogin.hostname };
// The callbacks in |buttons| have a closure to access the variables
// in scope here; set one to |this._pwmgr| so we can get back to pwmgr
// without a getService() call.
@ -290,7 +281,7 @@ LoginManagerPrompter.prototype = {
}
];
this._showLoginNotification(title, notificationText, buttons, aOldLogin.username, aNewPassword);
this._showLoginNotification(notificationText, buttons, aOldLogin.username, aNewPassword);
},
@ -462,35 +453,6 @@ LoginManagerPrompter.prototype = {
return hostname;
},
/*
* _getShortDisplayHost
*
* Converts a login's hostname field (a URL) to a short string for
* prompting purposes. Eg, "http://foo.com" --> "foo.com", or
* "ftp://www.site.co.uk" --> "site.co.uk".
*/
_getShortDisplayHost: function (aURIString) {
var displayHost;
var eTLDService = Cc["@mozilla.org/network/effective-tld-service;1"].
getService(Ci.nsIEffectiveTLDService);
var idnService = Cc["@mozilla.org/network/idn-service;1"].
getService(Ci.nsIIDNService);
try {
var uri = Services.io.newURI(aURIString, null, null);
var baseDomain = eTLDService.getBaseDomain(uri);
displayHost = idnService.convertToDisplayIDN(baseDomain, {});
} catch (e) {
this.log("_getShortDisplayHost couldn't process " + aURIString);
}
if (!displayHost)
displayHost = aURIString;
return displayHost;
},
}; // end of LoginManagerPrompter implementation