diff --git a/mobile/android/base/FormAssistPopup.java b/mobile/android/base/FormAssistPopup.java index 48f8d3ba1e01..815fc7b9762b 100644 --- a/mobile/android/base/FormAssistPopup.java +++ b/mobile/android/base/FormAssistPopup.java @@ -51,6 +51,7 @@ import android.view.ViewGroup; import android.view.animation.Animation; import android.view.animation.AnimationUtils; import android.view.inputmethod.InputMethodManager; +import android.widget.AdapterView.OnItemClickListener; import android.widget.ArrayAdapter; import android.widget.AdapterView; import android.widget.RelativeLayout; @@ -61,22 +62,30 @@ import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; -public class FormAssistPopup extends ListView implements GeckoEventListener { +public class FormAssistPopup extends RelativeLayout implements GeckoEventListener { private Context mContext; private Animation mAnimation; + private ListView mAutoCompleteList; + private RelativeLayout mValidationMessage; + private TextView mValidationMessageText; + + private static int sAutoCompleteMinWidth = 0; + private static int sAutoCompleteRowHeight = 0; + private static int sValidationMessageHeight = 0; + + // Minimum popup width for autocomplete messages + private static final int AUTOCOMPLETE_MIN_WIDTH_IN_DPI = 200; + + // Height of the autocomplete_list_item TextView + private static final int AUTOCOMPLETE_ROW_HEIGHT_IN_DPI = 32; + + // Height of the validation_message_text TextView, plus the top margin set to + // make room for the arrow + private static final int VALIDATION_MESSAGE_HEIGHT_IN_DPI = 58; + private static final String LOGTAG = "FormAssistPopup"; - private static int sMinWidth = 0; - private static int sRowHeight = 0; - private static final int POPUP_MIN_WIDTH_IN_DPI = 200; - private static final int POPUP_ROW_HEIGHT_IN_DPI = 32; - - private static enum PopupType { NONE, AUTOCOMPLETE, VALIDATION }; - - // Keep track of the type of popup we're currently showing - private PopupType mTypeShowing = PopupType.NONE; - public FormAssistPopup(Context context, AttributeSet attrs) { super(context, attrs); mContext = context; @@ -86,19 +95,6 @@ public class FormAssistPopup extends ListView implements GeckoEventListener { setFocusable(false); - setOnItemClickListener(new OnItemClickListener() { - public void onItemClick(AdapterView parentView, View view, int position, long id) { - if (mTypeShowing.equals(PopupType.AUTOCOMPLETE)) { - // Use the value stored with the autocomplete view, not the label text, - // since they can be different. - TextView textView = (TextView) view; - String value = (String) textView.getTag(); - GeckoAppShell.sendEventToGecko(GeckoEvent.createBroadcastEvent("FormAssist:AutoComplete", value)); - hide(); - } - } - }); - GeckoAppShell.registerGeckoEventListener("FormAssist:AutoComplete", this); GeckoAppShell.registerGeckoEventListener("FormAssist:ValidationMessage", this); GeckoAppShell.registerGeckoEventListener("FormAssist:Hide", this); @@ -149,26 +145,51 @@ public class FormAssistPopup extends ListView implements GeckoEventListener { } private void showAutoCompleteSuggestions(JSONArray suggestions, JSONArray rect, double zoom) { + if (mAutoCompleteList == null) { + LayoutInflater inflater = LayoutInflater.from(mContext); + mAutoCompleteList = (ListView) inflater.inflate(R.layout.autocomplete_list, null); + + mAutoCompleteList.setOnItemClickListener(new OnItemClickListener() { + public void onItemClick(AdapterView parentView, View view, int position, long id) { + // Use the value stored with the autocomplete view, not the label text, + // since they can be different. + TextView textView = (TextView) view; + String value = (String) textView.getTag(); + GeckoAppShell.sendEventToGecko(GeckoEvent.createBroadcastEvent("FormAssist:AutoComplete", value)); + hide(); + } + }); + + addView(mAutoCompleteList); + } + AutoCompleteListAdapter adapter = new AutoCompleteListAdapter(mContext, R.layout.autocomplete_list_item); adapter.populateSuggestionsList(suggestions); - setAdapter(adapter); + mAutoCompleteList.setAdapter(adapter); - if (positionAndShowPopup(rect, zoom)) - mTypeShowing = PopupType.AUTOCOMPLETE; + positionAndShowPopup(rect, zoom, true); } - // TODO: style the validation message popup differently (bug 731654) private void showValidationMessage(String validationMessage, JSONArray rect, double zoom) { - ArrayAdapter adapter = new ArrayAdapter(mContext, R.layout.autocomplete_list_item); - adapter.add(validationMessage); - setAdapter(adapter); + if (mValidationMessage == null) { + LayoutInflater inflater = LayoutInflater.from(mContext); + mValidationMessage = (RelativeLayout) inflater.inflate(R.layout.validation_message, null); - if (positionAndShowPopup(rect, zoom)) - mTypeShowing = PopupType.VALIDATION; + addView(mValidationMessage); + mValidationMessageText = (TextView) mValidationMessage.findViewById(R.id.validation_message_text); + } + + validationMessage = "This is an incredibly long validation message to test the width of the popup."; + mValidationMessageText.setText(validationMessage); + + // We need to set the text as selected for the marquee text to work. + mValidationMessageText.setSelected(true); + + positionAndShowPopup(rect, zoom, false); } // Returns true if the popup is successfully shown, false otherwise - public boolean positionAndShowPopup(JSONArray rect, double zoom) { + private boolean positionAndShowPopup(JSONArray rect, double zoom, boolean isAutoComplete) { // Don't show the form assist popup when using fullscreen VKB InputMethodManager imm = (InputMethodManager) GeckoApp.mAppContext.getSystemService(Context.INPUT_METHOD_SERVICE); @@ -176,10 +197,28 @@ public class FormAssistPopup extends ListView implements GeckoEventListener { return false; if (!isShown()) { - setVisibility(View.VISIBLE); + setVisibility(VISIBLE); startAnimation(mAnimation); } + // Hide/show the appropriate popup contents + if (mAutoCompleteList != null) + mAutoCompleteList.setVisibility(isAutoComplete ? VISIBLE : GONE); + if (mValidationMessage != null) + mValidationMessage.setVisibility(isAutoComplete ? GONE : VISIBLE); + + // Initialize static variables based on DisplayMetrics. We delay this to + // make sure DisplayMetrics isn't null to avoid an NPE. + if (sAutoCompleteMinWidth == 0) { + DisplayMetrics metrics = new DisplayMetrics(); + GeckoApp.mAppContext.getWindowManager().getDefaultDisplay().getMetrics(metrics); + sAutoCompleteMinWidth = (int) (AUTOCOMPLETE_MIN_WIDTH_IN_DPI * metrics.density); + sAutoCompleteRowHeight = (int) (AUTOCOMPLETE_ROW_HEIGHT_IN_DPI * metrics.density); + sValidationMessageHeight = (int) (VALIDATION_MESSAGE_HEIGHT_IN_DPI * metrics.density); + } + + // These values correspond to the input box for which we want to + // display the FormAssistPopup. int left = 0; int top = 0; int width = 0; @@ -197,42 +236,42 @@ public class FormAssistPopup extends ListView implements GeckoEventListener { FloatSize viewport = GeckoApp.mAppContext.getLayerController().getViewportSize(); - // Late initializing static variables to allow DisplayMetrics not to be null and avoid NPE - if (sMinWidth == 0) { - DisplayMetrics metrics = new DisplayMetrics(); - GeckoApp.mAppContext.getWindowManager().getDefaultDisplay().getMetrics(metrics); - sMinWidth = (int) (POPUP_MIN_WIDTH_IN_DPI * metrics.density); - sRowHeight = (int) (POPUP_ROW_HEIGHT_IN_DPI * metrics.density); - } - - // If the textbox is smaller than the screen-width, - // shrink the list's width - if ((left + width) < viewport.width) + // For autocomplete suggestions, if the input is smaller than the screen-width, + // shrink the popup's width. Otherwise, keep it as FILL_PARENT. + if (isAutoComplete && (left + width) < viewport.width) { popupWidth = left < 0 ? left + width : width; - // popupWidth can be negative if it is a constant - FILL_PARENT or MATCH_PARENT - if (popupWidth >= 0 && popupWidth < sMinWidth) { - popupWidth = sMinWidth; + // Ensure the popup has a minimum width. + if (popupWidth < sAutoCompleteMinWidth) { + popupWidth = sAutoCompleteMinWidth; - if ((popupLeft + popupWidth) > viewport.width) - popupLeft = (int) (viewport.width - popupWidth); + // Move the popup to the left if there isn't enough room for it. + if ((popupLeft + popupWidth) > viewport.width) + popupLeft = (int) (viewport.width - popupWidth); + } } - int popupHeight = sRowHeight * getAdapter().getCount(); + int popupHeight; + if (isAutoComplete) + popupHeight = sAutoCompleteRowHeight * mAutoCompleteList.getAdapter().getCount(); + else + popupHeight = sValidationMessageHeight; + int popupTop = top + height; - // The text box doesnt fit below + // If the popup doesn't fit below the input box, shrink its height, or + // see if we can place it above the input instead. if ((popupTop + popupHeight) > viewport.height) { - // Find where the maximum space is, and fit it there + // Find where the maximum space is, and put the popup there. if ((viewport.height - popupTop) > top) { - // Shrink the height to fit it below the text-box + // Shrink the height to fit it below the input box. popupHeight = (int) (viewport.height - popupTop); } else { if (popupHeight < top) { - // No shrinking needed to fit on top + // No shrinking needed to fit on top. popupTop = (top - popupHeight); } else { - // Shrink to available space on top + // Shrink to available space on top. popupTop = 0; popupHeight = top; } @@ -250,8 +289,7 @@ public class FormAssistPopup extends ListView implements GeckoEventListener { public void hide() { if (isShown()) { - setVisibility(View.GONE); - mTypeShowing = PopupType.NONE; + setVisibility(GONE); GeckoAppShell.sendEventToGecko(GeckoEvent.createBroadcastEvent("FormAssist:Hidden", null)); } } diff --git a/mobile/android/base/Makefile.in b/mobile/android/base/Makefile.in index 969a9ebcddf7..54bc1005fcdf 100644 --- a/mobile/android/base/Makefile.in +++ b/mobile/android/base/Makefile.in @@ -241,6 +241,7 @@ endif RES_LAYOUT = \ $(SYNC_RES_LAYOUT) \ + res/layout/autocomplete_list.xml \ res/layout/autocomplete_list_item.xml \ res/layout/awesomebar.xml \ res/layout/awesomebar_actionbar.xml \ @@ -275,6 +276,7 @@ RES_LAYOUT = \ res/layout/abouthome_section.xml \ res/layout/abouthome_remote_tab_row.xml \ res/layout/abouthome_topsite_item.xml \ + res/layout/validation_message.xml \ $(NULL) RES_LAYOUT_V11 = \ @@ -358,6 +360,8 @@ RES_DRAWABLE_BASE = \ res/drawable/site_security_identified.png \ res/drawable/site_security_verified.png \ res/drawable/urlbar_stop.png \ + res/drawable/validation_arrow.png \ + res/drawable/validation_bg.9.png \ $(addprefix res/drawable-mdpi/,$(notdir $(SYNC_RES_DRAWABLE_MDPI))) \ $(NULL) diff --git a/mobile/android/base/resources/drawable/validation_arrow.png b/mobile/android/base/resources/drawable/validation_arrow.png new file mode 100644 index 000000000000..709dd1e99151 Binary files /dev/null and b/mobile/android/base/resources/drawable/validation_arrow.png differ diff --git a/mobile/android/base/resources/drawable/validation_bg.9.png b/mobile/android/base/resources/drawable/validation_bg.9.png new file mode 100644 index 000000000000..b66c26955829 Binary files /dev/null and b/mobile/android/base/resources/drawable/validation_bg.9.png differ diff --git a/mobile/android/base/resources/layout-v11/gecko_app.xml b/mobile/android/base/resources/layout-v11/gecko_app.xml index 92506744144e..192f21a0e470 100644 --- a/mobile/android/base/resources/layout-v11/gecko_app.xml +++ b/mobile/android/base/resources/layout-v11/gecko_app.xml @@ -14,11 +14,8 @@ android:layout_height="fill_parent"/> + diff --git a/mobile/android/base/resources/layout/gecko_app.xml b/mobile/android/base/resources/layout/gecko_app.xml index 5cc04c974e41..704923af69ab 100644 --- a/mobile/android/base/resources/layout/gecko_app.xml +++ b/mobile/android/base/resources/layout/gecko_app.xml @@ -16,11 +16,8 @@ android:layout_height="fill_parent"/> + + + + + + + diff --git a/mobile/android/base/resources/values/colors.xml b/mobile/android/base/resources/values/colors.xml index b6435d79a684..a66706089e22 100644 --- a/mobile/android/base/resources/values/colors.xml +++ b/mobile/android/base/resources/values/colors.xml @@ -5,5 +5,6 @@ #000000 #ffffff #ACC4D5 + #ffffff