Merge fxteam to m-c

This commit is contained in:
Wes Kocher 2013-10-15 20:26:55 -07:00
commit f25b780813
25 changed files with 662 additions and 143 deletions

View File

@ -575,7 +575,7 @@
.variable-or-property[non-configurable] > tooltip > label[value=configurable],
.variable-or-property[non-writable] > tooltip > label[value=writable],
.variable-or-property[non-extensible] > tooltip > label[value=extensible] {
color: #800;
color: #f44;
text-decoration: line-through;
}

View File

@ -132,6 +132,7 @@ FENNEC_JAVA_FILES = \
prompts/Prompt.java \
prompts/PromptInput.java \
prompts/PromptService.java \
prompts/IconGridInput.java \
Restarter.java \
sqlite/ByteBufferInputStream.java \
sqlite/MatrixBlobCursor.java \
@ -447,6 +448,8 @@ RES_LAYOUT = \
res/layout/home_banner.xml \
res/layout/home_suggestion_prompt.xml \
res/layout/home_top_sites_page.xml \
res/layout/icon_grid.xml \
res/layout/icon_grid_item.xml \
res/layout/web_app.xml \
res/layout/launch_app_list.xml \
res/layout/launch_app_listitem.xml \
@ -613,6 +616,8 @@ RES_DRAWABLE_MDPI = \
res/drawable-mdpi/bookmark_folder_closed.png \
res/drawable-mdpi/bookmark_folder_opened.png \
res/drawable-mdpi/desktop_notification.png \
res/drawable-mdpi/grid_icon_bg_activated.9.png \
res/drawable-mdpi/grid_icon_bg_focused.9.png \
res/drawable-mdpi/home_tab_menu_strip.9.png \
res/drawable-mdpi/ic_menu_addons_filler.png \
res/drawable-mdpi/ic_menu_bookmark_add.png \
@ -718,6 +723,8 @@ RES_DRAWABLE_HDPI = \
res/drawable-hdpi/folder.png \
res/drawable-hdpi/home_bg.png \
res/drawable-hdpi/home_star.png \
res/drawable-hdpi/grid_icon_bg_activated.9.png \
res/drawable-hdpi/grid_icon_bg_focused.9.png \
res/drawable-hdpi/abouthome_thumbnail.png \
res/drawable-hdpi/alert_addon.png \
res/drawable-hdpi/alert_app.png \
@ -830,6 +837,8 @@ RES_DRAWABLE_XHDPI = \
res/drawable-xhdpi/alert_mic_camera.png \
res/drawable-xhdpi/arrow_popup_bg.9.png \
res/drawable-xhdpi/home_tab_menu_strip.9.png \
res/drawable-xhdpi/grid_icon_bg_activated.9.png \
res/drawable-xhdpi/grid_icon_bg_focused.9.png \
res/drawable-xhdpi/ic_menu_addons_filler.png \
res/drawable-xhdpi/ic_menu_bookmark_add.png \
res/drawable-xhdpi/ic_menu_bookmark_remove.png \
@ -1079,6 +1088,7 @@ RES_DRAWABLE += \
res/drawable/url_bar_bg.xml \
res/drawable/url_bar_entry.xml \
res/drawable/url_bar_nav_button.xml \
res/drawable/icon_grid_item_bg.xml \
res/drawable/url_bar_right_edge.xml \
res/drawable/bookmark_folder.xml \
res/drawable/divider_horizontal.xml \

View File

@ -21,12 +21,14 @@ import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.PixelFormat;
import android.graphics.Point;
import android.graphics.PointF;
import android.graphics.Rect;
import android.graphics.SurfaceTexture;
import android.os.Build;
import android.os.Handler;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.KeyEvent;
import android.view.MotionEvent;
@ -125,6 +127,18 @@ public class LayerView extends FrameLayout {
GeckoAccessibility.setDelegate(this);
}
private Point getEventRadius(MotionEvent event) {
if (Build.VERSION.SDK_INT >= 9) {
return new Point((int)event.getToolMajor()/2,
(int)event.getToolMinor()/2);
}
float size = event.getSize();
DisplayMetrics displaymetrics = getContext().getResources().getDisplayMetrics();
size = size * Math.min(displaymetrics.heightPixels, displaymetrics.widthPixels);
return new Point((int)size, (int)size);
}
public void geckoConnected() {
// See if we want to force 16-bit colour before doing anything
PrefsHelper.getPref("gfx.android.rgb16.force", new PrefsHelper.PrefHandlerBase() {
@ -157,8 +171,10 @@ public class LayerView extends FrameLayout {
}
if (mInitialTouchPoint != null && action == MotionEvent.ACTION_MOVE) {
Point p = getEventRadius(event);
if (PointUtils.subtract(point, mInitialTouchPoint).length() <
PanZoomController.PAN_THRESHOLD) {
Math.max(PanZoomController.CLICK_THRESHOLD, Math.min(Math.min(p.x, p.y), PanZoomController.PAN_THRESHOLD))) {
// Don't send the touchmove event if if the users finger hasn't moved far.
// Necessary for Google Maps to work correctly. See bug 771099.
return true;

View File

@ -18,6 +18,9 @@ public interface PanZoomController {
// between the touch-down and touch-up of a click). In units of density-independent pixels.
public static final float PAN_THRESHOLD = 1/16f * GeckoAppShell.getDpi();
// Threshold for sending touch move events to content
public static final float CLICK_THRESHOLD = 1/50f * GeckoAppShell.getDpi();
static class Factory {
static PanZoomController create(PanZoomTarget target, View view, EventDispatcher dispatcher) {
return new JavaPanZoomController(target, view, dispatcher);

View File

@ -0,0 +1,172 @@
/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
package org.mozilla.gecko.prompts;
import org.json.JSONArray;
import org.json.JSONObject;
import org.mozilla.gecko.R;
import org.mozilla.gecko.GeckoAppShell;
import org.mozilla.gecko.gfx.BitmapUtils;
import org.mozilla.gecko.util.ThreadUtils;
import android.app.Activity;
import android.app.ActivityManager;
import android.content.Context;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.text.TextUtils;
import android.view.Display;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewGroup.LayoutParams;
import android.view.LayoutInflater;
import android.view.WindowManager;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.ArrayAdapter;
import android.widget.GridView;
import android.widget.TextView;
import android.widget.ImageView;
import android.widget.ListView;
import android.util.Log;
import java.util.ArrayList;
import java.util.List;
public class IconGridInput extends PromptInput implements OnItemClickListener {
public static final String INPUT_TYPE = "icongrid";
public static final String LOGTAG = "GeckoIconGridInput";
private ArrayAdapter<IconGridItem> mAdapter; // An adapter holding a list of items to show in the grid
private static int mColumnWidth = -1; // The maximum width of columns
private static int mMaxColumns = -1; // The maximum number of columns to show
private static int mIconSize = -1; // Size of icons in the grid
private int mSelected = -1; // Current selection
private JSONArray mArray;
public IconGridInput(JSONObject obj) {
super(obj);
mArray = obj.optJSONArray("items");
}
@Override
public View getView(Context context) throws UnsupportedOperationException {
if (mColumnWidth < 0) {
// getColumnWidth isn't available on pre-ICS, so we pull it out and assign it here
mColumnWidth = context.getResources().getDimensionPixelSize(R.dimen.icongrid_columnwidth);
}
if (mIconSize < 0) {
mIconSize = GeckoAppShell.getPreferredIconSize();
}
if (mMaxColumns < 0) {
mMaxColumns = context.getResources().getInteger(R.integer.max_icon_grid_columns);
}
// TODO: Dynamically handle size changes
final WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
final Display display = wm.getDefaultDisplay();
final int screenWidth = display.getWidth();
int maxColumns = Math.min(mMaxColumns, screenWidth / mColumnWidth);
final GridView view = (GridView) LayoutInflater.from(context).inflate(R.layout.icon_grid, null, false);
view.setColumnWidth(mColumnWidth);
final ArrayList<IconGridItem> items = new ArrayList<IconGridItem>(mArray.length());
for (int i = 0; i < mArray.length(); i++) {
IconGridItem item = new IconGridItem(context, mArray.optJSONObject(i));
items.add(item);
if (item.selected) {
mSelected = i;
view.setSelection(i);
}
}
view.setNumColumns(Math.min(items.size(), maxColumns));
view.setOnItemClickListener(this);
mAdapter = new IconGridAdapter(context, -1, items);
view.setAdapter(mAdapter);
mView = view;
return mView;
}
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
mSelected = position;
}
@Override
public String getValue() {
return Integer.toString(mSelected);
}
@Override
public boolean getScrollable() {
return true;
}
private class IconGridAdapter extends ArrayAdapter<IconGridItem> {
public IconGridAdapter(Context context, int resource, List<IconGridItem> items) {
super(context, resource, items);
}
@Override
public View getView(int position, View convert, ViewGroup parent) {
final Context context = parent.getContext();
if (convert == null) {
convert = LayoutInflater.from(context).inflate(R.layout.icon_grid_item, parent, false);
}
bindView(convert, context, position);
return convert;
}
private void bindView(View v, Context c, int position) {
final IconGridItem item = getItem(position);
final TextView text1 = (TextView) v.findViewById(android.R.id.text1);
text1.setText(item.label);
final TextView text2 = (TextView) v.findViewById(android.R.id.text2);
if (TextUtils.isEmpty(item.description)) {
text2.setVisibility(View.GONE);
} else {
text2.setVisibility(View.VISIBLE);
text2.setText(item.description);
}
final ImageView icon = (ImageView) v.findViewById(R.id.icon);
icon.setImageDrawable(item.icon);
ViewGroup.LayoutParams lp = icon.getLayoutParams();
lp.width = lp.height = mIconSize;
}
}
private class IconGridItem {
final String label;
final String description;
final boolean selected;
Drawable icon;
public IconGridItem(final Context context, final JSONObject obj) {
label = obj.optString("name");
final String iconUrl = obj.optString("iconUri");
description = obj.optString("description");
selected = obj.optBoolean("selected");
BitmapUtils.getDrawable(context, iconUrl, new BitmapUtils.BitmapLoader() {
public void onBitmapFound(Drawable d) {
icon = d;
if (mAdapter != null) {
mAdapter.notifyDataSetChanged();
}
}
});
}
}
}

View File

@ -35,6 +35,7 @@ import android.view.View;
import android.view.ViewGroup;
import android.view.ViewGroup.LayoutParams;
import android.view.inputmethod.InputMethodManager;
import android.widget.AbsListView;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.ArrayAdapter;
@ -116,89 +117,30 @@ public class Prompt implements OnClickListener, OnCancelListener, OnItemClickLis
processMessage(message);
}
public void show(String aTitle, String aText, PromptListItem[] aMenuList, boolean aMultipleSelection) {
public void show(String title, String text, PromptListItem[] listItems, boolean multipleSelection) {
ThreadUtils.assertOnUiThread();
GeckoAppShell.getLayerView().abortPanning();
AlertDialog.Builder builder = new AlertDialog.Builder(mContext);
if (!TextUtils.isEmpty(aTitle)) {
builder.setTitle(aTitle);
if (!TextUtils.isEmpty(title)) {
builder.setTitle(title);
}
if (!TextUtils.isEmpty(aText)) {
builder.setMessage(aText);
if (!TextUtils.isEmpty(text)) {
builder.setMessage(text);
}
int length = mInputs == null ? 0 : mInputs.length;
if (aMenuList != null && aMenuList.length > 0) {
int resourceId = android.R.layout.simple_list_item_1;
if (mSelected != null && mSelected.length > 0) {
if (aMultipleSelection) {
resourceId = R.layout.select_dialog_multichoice;
} else {
resourceId = R.layout.select_dialog_singlechoice;
}
}
PromptListAdapter adapter = new PromptListAdapter(mContext, resourceId, aMenuList);
if (mSelected != null && mSelected.length > 0) {
if (aMultipleSelection) {
adapter.listView = (ListView) mInflater.inflate(R.layout.select_dialog_list, null);
adapter.listView.setOnItemClickListener(this);
builder.setInverseBackgroundForced(true);
adapter.listView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);
adapter.listView.setAdapter(adapter);
builder.setView(adapter.listView);
} else {
int selectedIndex = -1;
for (int i = 0; i < mSelected.length; i++) {
if (mSelected[i]) {
selectedIndex = i;
break;
}
}
mSelected = null;
builder.setSingleChoiceItems(adapter, selectedIndex, this);
}
} else {
builder.setAdapter(adapter, this);
mSelected = null;
}
} else if (length == 1) {
try {
ScrollView view = new ScrollView(mContext);
view.addView(mInputs[0].getView(mContext));
builder.setView(applyInputStyle(view));
} catch(UnsupportedOperationException ex) {
// We cannot display these input widgets with this sdk version,
// do not display any dialog and finish the prompt now.
try {
finishDialog(new JSONObject("{\"button\": -1}"));
} catch(JSONException e) { }
return;
}
} else if (length > 1) {
try {
LinearLayout linearLayout = new LinearLayout(mContext);
linearLayout.setOrientation(LinearLayout.VERTICAL);
for (int i = 0; i < length; i++) {
View content = mInputs[i].getView(mContext);
linearLayout.addView(content);
}
ScrollView view = new ScrollView(mContext);
view.addView(linearLayout);
builder.setView(applyInputStyle(view));
} catch(UnsupportedOperationException ex) {
// We cannot display these input widgets with this sdk version,
// do not display any dialog and finish the prompt now.
try {
finishDialog(new JSONObject("{\"button\": -1}"));
} catch(JSONException e) { }
return;
}
// Because lists are currently added through the normal Android AlertBuilder interface, they're
// incompatible with also adding additional input elements to a dialog.
if (listItems != null && listItems.length > 0) {
addlistItems(builder, listItems, multipleSelection);
} else if (!addInputs(builder)) {
// If we failed to add any requested input elements, don't show the dialog
return;
}
length = mButtons == null ? 0 : mButtons.length;
int length = mButtons == null ? 0 : mButtons.length;
if (length > 0) {
builder.setPositiveButton(mButtons[0], this);
if (length > 1) {
@ -222,64 +164,242 @@ public class Prompt implements OnClickListener, OnCancelListener, OnItemClickLis
mInputs = inputs;
}
/* Adds to a result value from the lists that can be shown in dialogs.
* Will set the selected value(s) to the button attribute of the
* object that's passed in. If this is a multi-select dialog, can set
* the button attribute to an array.
*/
private void addListResult(final JSONObject result, int which) {
try {
if (mSelected != null) {
JSONArray selected = new JSONArray();
for (int i = 0; i < mSelected.length; i++) {
selected.put(mSelected[i]);
}
result.put("button", selected);
} else {
result.put("button", which);
}
} catch(JSONException ex) { }
}
/* Adds to a result value from the inputs that can be shown in dialogs.
* Each input will set its own value in the result.
*/
private void addInputValues(final JSONObject result) {
try {
if (mInputs != null) {
for (int i = 0; i < mInputs.length; i++) {
result.put(mInputs[i].getId(), mInputs[i].getValue());
}
}
} catch(JSONException ex) { }
}
/* Adds the selected button to a result. This should only be called if there
* are no lists shown on the dialog, since they also write their results to the button
* attribute.
*/
private void addButtonResult(final JSONObject result, int which) {
int button = -1;
switch(which) {
case DialogInterface.BUTTON_POSITIVE : button = 0; break;
case DialogInterface.BUTTON_NEUTRAL : button = 1; break;
case DialogInterface.BUTTON_NEGATIVE : button = 2; break;
}
try {
result.put("button", button);
} catch(JSONException ex) { }
}
@Override
public void onClick(DialogInterface aDialog, int aWhich) {
public void onClick(DialogInterface dialog, int which) {
ThreadUtils.assertOnUiThread();
JSONObject ret = new JSONObject();
try {
int button = -1;
ListView list = mDialog.getListView();
if (list != null || mSelected != null) {
button = aWhich;
if (mSelected != null) {
JSONArray selected = new JSONArray();
for (int i = 0; i < mSelected.length; i++) {
selected.put(mSelected[i]);
}
ret.put("button", selected);
} else {
ret.put("button", button);
}
addListResult(ret, which);
} else {
switch(aWhich) {
case DialogInterface.BUTTON_POSITIVE : button = 0; break;
case DialogInterface.BUTTON_NEUTRAL : button = 1; break;
case DialogInterface.BUTTON_NEGATIVE : button = 2; break;
}
ret.put("button", button);
}
if (mInputs != null) {
for (int i = 0; i < mInputs.length; i++) {
ret.put(mInputs[i].getId(), mInputs[i].getValue());
}
addButtonResult(ret, which);
}
addInputValues(ret);
} catch(Exception ex) {
Log.i(LOGTAG, "Error building return: " + ex);
}
if (mDialog != null) {
mDialog.dismiss();
if (dialog != null) {
dialog.dismiss();
}
finishDialog(ret);
}
/* Adds a set of list items to the prompt. This can be used for either context menu type dialogs, checked lists,
* or multiple selection lists. If mSelected is set in the prompt before addlistItems is called, the items will be
* shown with "checkmarks" on their left side.
*
* @param builder
* The alert builder currently building this dialog.
* @param listItems
* The items to add.
* @param multipleSelection
* If true, and mSelected is defined to be a non-zero-length list, the list will show checkmarks on the
* left and allow multiple selection.
*/
private void addlistItems(AlertDialog.Builder builder, PromptListItem[] listItems, boolean multipleSelection) {
if (mSelected != null && mSelected.length > 0) {
if (multipleSelection) {
addMultiSelectList(builder, listItems);
} else {
addSingleSelectList(builder, listItems);
}
} else {
addMenuList(builder, listItems);
}
}
/* Shows a multi-select list with checkmarks on the side. Android doesn't support using an adapter for
* multi-choice lists by default so instead we insert our own custom list so that we can do fancy things
* to the rows like disabling/indenting them.
*
* @param builder
* The alert builder currently building this dialog.
* @param listItems
* The items to add.
*/
private void addMultiSelectList(AlertDialog.Builder builder, PromptListItem[] listItems) {
PromptListAdapter adapter = new PromptListAdapter(mContext, R.layout.select_dialog_multichoice, listItems);
adapter.listView = (ListView) mInflater.inflate(R.layout.select_dialog_list, null);
adapter.listView.setOnItemClickListener(this);
builder.setInverseBackgroundForced(true);
adapter.listView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);
adapter.listView.setAdapter(adapter);
builder.setView(adapter.listView);
}
/* Shows a single-select list with radio boxes on the side.
*
* @param builder
* the alert builder currently building this dialog.
* @param listItems
* The items to add.
*/
private void addSingleSelectList(AlertDialog.Builder builder, PromptListItem[] listItems) {
PromptListAdapter adapter = new PromptListAdapter(mContext, R.layout.select_dialog_singlechoice, listItems);
// For single select, we only maintain a single index of the selected row
int selectedIndex = -1;
for (int i = 0; i < mSelected.length; i++) {
if (mSelected[i]) {
selectedIndex = i;
break;
}
}
mSelected = null;
builder.setSingleChoiceItems(adapter, selectedIndex, this);
}
/* Shows a single-select list.
*
* @param builder
* the alert builder currently building this dialog.
* @param listItems
* The items to add.
*/
private void addMenuList(AlertDialog.Builder builder, PromptListItem[] listItems) {
PromptListAdapter adapter = new PromptListAdapter(mContext, android.R.layout.simple_list_item_1, listItems);
builder.setAdapter(adapter, this);
mSelected = null;
}
/* Add the requested input elements to the dialog.
*
* @param builder
* the alert builder currently building this dialog.
* @return
* return true if the inputs were added successfully. This may fail
* if the requested input is compatible with this Android verison
*/
private boolean addInputs(AlertDialog.Builder builder) {
int length = mInputs == null ? 0 : mInputs.length;
if (length == 0) {
return true;
}
try {
View root = null;
boolean scrollable = false; // If any of the innuts are scrollable, we won't wrap this in a ScrollView
if (length == 1) {
root = mInputs[0].getView(mContext);
scrollable |= mInputs[0].getScrollable();
} else if (length > 1) {
LinearLayout linearLayout = new LinearLayout(mContext);
linearLayout.setOrientation(LinearLayout.VERTICAL);
for (int i = 0; i < length; i++) {
View content = mInputs[i].getView(mContext);
linearLayout.addView(content);
scrollable |= mInputs[i].getScrollable();
}
root = linearLayout;
}
if (scrollable) {
builder.setView(applyInputStyle(root));
} else {
ScrollView view = new ScrollView(mContext);
view.addView(root);
builder.setView(applyInputStyle(view));
}
} catch(Exception ex) {
Log.e(LOGTAG, "Error showing prompt inputs", ex);
// We cannot display these input widgets with this sdk version,
// do not display any dialog and finish the prompt now.
cancelDialog();
return false;
}
return true;
}
/* AdapterView.OnItemClickListener
* Called when a list item is clicked
*/
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
ThreadUtils.assertOnUiThread();
mSelected[position] = !mSelected[position];
}
/* @DialogInterface.OnCancelListener
* Called when the user hits back to cancel a dialog. The dialog will close itself when this
* ends. Setup the correct return values here.
*
* @param aDialog
* A dialog interface for the dialog that's being closed.
*/
@Override
public void onCancel(DialogInterface aDialog) {
ThreadUtils.assertOnUiThread();
cancelDialog();
}
/* Called in situations where we want to cancel the dialog . This can happen if the user hits back,
* or if the dialog can't be created because of invalid JSON.
*/
private void cancelDialog() {
JSONObject ret = new JSONObject();
try {
ret.put("button", -1);
} catch(Exception ex) { }
addInputValues(ret);
finishDialog(ret);
}
/* Called any time we're closing the dialog to cleanup and notify listeners that the dialog
* is closing.
*/
public void finishDialog(JSONObject aReturn) {
mInputs = null;
mButtons = null;
@ -302,10 +422,12 @@ public class Prompt implements OnClickListener, OnCancelListener, OnItemClickLis
mGuid = null;
}
/* Handles parsing the initial JSON sent to show dialogs
*/
private void processMessage(JSONObject geckoObject) {
String title = getSafeString(geckoObject, "title");
String text = getSafeString(geckoObject, "text");
mGuid = getSafeString(geckoObject, "guid");
String title = geckoObject.optString("title");
String text = geckoObject.optString("text");
mGuid = geckoObject.optString("guid");
mButtons = getStringArray(geckoObject, "buttons");
@ -323,14 +445,6 @@ public class Prompt implements OnClickListener, OnCancelListener, OnItemClickLis
show(title, text, menuitems, multiple);
}
private static String getSafeString(JSONObject json, String key) {
try {
return json.getString(key);
} catch (Exception e) {
return "";
}
}
private static JSONArray getSafeArray(JSONObject json, String key) {
try {
return json.getJSONArray(key);
@ -339,22 +453,6 @@ public class Prompt implements OnClickListener, OnCancelListener, OnItemClickLis
}
}
private static boolean getSafeBool(JSONObject json, String key) {
try {
return json.getBoolean(key);
} catch (Exception e) {
return false;
}
}
private static int getSafeInt(JSONObject json, String key ) {
try {
return json.getInt(key);
} catch (Exception e) {
return 0;
}
}
public static String[] getStringArray(JSONObject aObject, String aName) {
JSONArray items = getSafeArray(aObject, aName);
int length = items.length();
@ -406,12 +504,12 @@ public class Prompt implements OnClickListener, OnCancelListener, OnItemClickLis
public Drawable icon;
PromptListItem(JSONObject aObject) {
label = getSafeString(aObject, "label");
isGroup = getSafeBool(aObject, "isGroup");
inGroup = getSafeBool(aObject, "inGroup");
disabled = getSafeBool(aObject, "disabled");
id = getSafeInt(aObject, "id");
isParent = getSafeBool(aObject, "isParent");
label = aObject.optString("label");
isGroup = aObject.optBoolean("isGroup");
inGroup = aObject.optBoolean("inGroup");
disabled = aObject.optBoolean("disabled");
id = aObject.optInt("id");
isParent = aObject.optBoolean("isParent");
}
public PromptListItem(String aLabel) {

View File

@ -42,8 +42,6 @@ import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.TimeUnit;
public class PromptInput {
private final JSONObject mJSONInput;
protected final String mLabel;
protected final String mType;
protected final String mId;
@ -329,7 +327,6 @@ public class PromptInput {
}
public PromptInput(JSONObject obj) {
mJSONInput = obj;
mLabel = obj.optString("label");
mType = obj.optString("type");
String id = obj.optString("id");
@ -351,6 +348,8 @@ public class PromptInput {
return new MenulistInput(obj);
} else if (LabelInput.INPUT_TYPE.equals(type)) {
return new LabelInput(obj);
} else if (IconGridInput.INPUT_TYPE.equals(type)) {
return new IconGridInput(obj);
} else {
for (String dtType : DateTimeInput.INPUT_TYPES) {
if (dtType.equals(type)) {
@ -372,4 +371,8 @@ public class PromptInput {
public String getValue() {
return "";
}
public boolean getScrollable() {
return false;
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 326 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 326 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 263 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 284 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 242 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 413 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 324 B

View File

@ -0,0 +1,26 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2012 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_focused="true"
android:state_pressed="true"
android:drawable="@drawable/grid_icon_bg_focused" />
<item android:state_activated="true"
android:drawable="@drawable/grid_icon_bg_activated" />
<item android:drawable="@android:color/transparent" />
</selector>

View File

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
<GridView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_height="wrap_content"
android:layout_width="match_parent"
android:choiceMode="singleChoice"
android:padding="@dimen/icongrid_padding"/>

View File

@ -0,0 +1,55 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
//device/apps/common/res/any/layout/resolve_list_item.xml
Copyright 2006, The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:gravity="center"
android:orientation="vertical"
android:layout_height="wrap_content"
android:layout_width="match_parent"
android:background="@drawable/icon_grid_item_bg"
android:padding="16dp">
<!-- Extended activity info to distinguish between duplicate activity names -->
<TextView android:id="@android:id/text2"
android:textAppearance="?android:attr/textAppearance"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center"
android:minLines="2"
android:maxLines="2"
android:paddingTop="4dip"
android:paddingBottom="4dip" />
<!-- Activity icon when presenting dialog
Size will be filled in by ResolverActivity -->
<ImageView android:id="@+id/icon"
android:layout_width="0dp"
android:layout_height="0dp"
android:scaleType="fitCenter" />
<!-- Activity name -->
<TextView android:id="@android:id/text1"
android:textAppearance="?android:attr/textAppearanceSmall"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center"
android:minLines="2"
android:maxLines="2"
android:paddingTop="4dip"
android:paddingBottom="4dip" />
</LinearLayout>

View File

@ -96,4 +96,8 @@
<!-- Banner -->
<dimen name="home_banner_height">72dp</dimen>
<!-- Icon Grid -->
<dimen name="icongrid_columnwidth">128dp</dimen>
<dimen name="icongrid_padding">16dp</dimen>
</resources>

View File

@ -7,5 +7,6 @@
<integer name="number_of_top_sites">6</integer>
<integer name="number_of_top_sites_cols">2</integer>
<integer name="max_icon_grid_columns">4</integer>
</resources>

View File

@ -20,6 +20,7 @@
[testMailToContextMenu]
[testPictureLinkContextMenu]
[testPasswordProvider]
[testPromptGridInput]
# [testPasswordEncrypt] # see bug 824067
[testFormHistory]
[testBrowserProvider]

View File

@ -0,0 +1,51 @@
<html>
<head>
<title>IconGrid test page</title>
<meta name="viewport" content="initial-scale=1.0"/>
<script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script>
<script type="application/javascript">
Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
Components.utils.import("resource://gre/modules/Services.jsm");
Components.utils.import("resource://gre/modules/Prompt.jsm");
function start() {
var test = location.hash.substring(1);
window[test]();
}
function test1() {
var p = new Prompt({
title: "Prompt 1",
buttons: [
"OK"
],
}).addIconGrid({
items: [
{ iconUri: "drawable://alert_app", name: "Icon 1", selected: true },
{ iconUri: "drawable://alert_download", name: "Icon 2" },
{ iconUri: "drawable://alert_addon", name: "Icon 3" },
{ iconUri: "drawable://alert_addon", name: "Icon 4" },
{ iconUri: "drawable://alert_addon", name: "Icon 5" },
{ iconUri: "drawable://alert_addon", name: "Icon 6" },
{ iconUri: "drawable://alert_addon", name: "Icon 7" },
{ iconUri: "drawable://alert_addon", name: "Icon 8" },
{ iconUri: "drawable://alert_addon", name: "Icon 9" },
{ iconUri: "drawable://alert_addon", name: "Icon 10" },
{ iconUri: "drawable://alert_addon", name: "Icon 11" },
]
});
p.show(function(data) {
sendResult(data.icongrid0 == 10, "Got result " + data.icongrid0);
});
}
function sendResult(pass, message) {
setTimeout(function() {
alert((pass ? "PASS " : "FAIL ") + message);
}, 1000);
}
</script>
</head>
<body onload="start();">
</body>
</html>

View File

@ -0,0 +1,61 @@
#filter substitution
package @ANDROID_PACKAGE_NAME@.tests;
import @ANDROID_PACKAGE_NAME@.*;
import android.graphics.drawable.Drawable;
import android.widget.EditText;
import android.widget.CheckedTextView;
import android.widget.TextView;
import android.text.InputType;
import android.util.DisplayMetrics;
import android.view.View;
import android.util.Log;
import org.json.JSONObject;
public class testPromptGridInput extends BaseTest {
@Override
protected int getTestType() {
return TEST_MOCHITEST;
}
protected int index = 1;
public void testPromptGridInput() {
blockForGeckoReady();
test(1);
testGridItem("Icon 1");
testGridItem("Icon 2");
testGridItem("Icon 3");
testGridItem("Icon 4");
testGridItem("Icon 5");
testGridItem("Icon 6");
testGridItem("Icon 7");
testGridItem("Icon 8");
testGridItem("Icon 9");
testGridItem("Icon 10");
testGridItem("Icon 11");
mSolo.clickOnText("Icon 11");
mSolo.clickOnText("OK");
mAsserter.ok(waitForText("PASS"), "test passed", "PASS");
mActions.sendSpecialKey(Actions.SpecialKey.BACK);
}
public void testGridItem(String title) {
// Force the list to scroll if necessary
mSolo.waitForText(title, 1, 500, true);
mAsserter.ok(waitForText(title), "Found grid item", title);
}
public void test(final int num) {
// Load about:blank between each test to ensure we reset state
loadUrl("about:blank");
mAsserter.ok(waitForText("about:blank"), "Loaded blank page", "page title match");
loadUrl("chrome://roboextender/content/robocop_prompt_gridinput.html#test" + num);
}
}

View File

@ -224,7 +224,7 @@ InternalPrompt.prototype = {
alertCheck: function alertCheck(aTitle, aText, aCheckMsg, aCheckState) {
let p = this._getPrompt(aTitle, aText, [ PromptUtils.getLocaleString("OK") ], aCheckMsg, aCheckState);
let data = this.showPrompt(p);
if (aCheckState)
if (aCheckState && data.button > -1)
aCheckState.value = data.checkbox0 == "true";
},
@ -238,7 +238,7 @@ InternalPrompt.prototype = {
let p = this._getPrompt(aTitle, aText, null, aCheckMsg, aCheckState);
let data = this.showPrompt(p);
let ok = data.button == 0;
if (aCheckState)
if (aCheckState && data.button > -1)
aCheckState.value = data.checkbox0 == "true";
return ok;
},
@ -284,7 +284,7 @@ InternalPrompt.prototype = {
let p = this._getPrompt(aTitle, aText, buttons, aCheckMsg, aCheckState);
let data = this.showPrompt(p);
if (aCheckState)
if (aCheckState && data.button > -1)
aCheckState.value = data.checkbox0 == "true";
return data.button;
},
@ -298,7 +298,7 @@ InternalPrompt.prototype = {
let data = this.showPrompt(p);
let ok = data.button == 0;
if (aCheckState)
if (aCheckState && data.button > -1)
aCheckState.value = data.checkbox0 == "true";
if (ok)
aValue.value = data.textbox0;
@ -316,7 +316,7 @@ InternalPrompt.prototype = {
let data = this.showPrompt(p);
let ok = data.button == 0;
if (aCheckState)
if (aCheckState && data.button > -1)
aCheckState.value = data.checkbox0 == "true";
if (ok)
aPassword.value = data.password0;
@ -337,7 +337,7 @@ InternalPrompt.prototype = {
let data = this.showPrompt(p);
let ok = data.button == 0;
if (aCheckState)
if (aCheckState && data.button > -1)
aCheckState.value = data.checkbox0 == "true";
if (ok) {
aUsername.value = data.textbox0;

View File

@ -122,6 +122,14 @@ Prompt.prototype = {
});
},
addIconGrid: function(aOptions) {
return this._addInput({
type: "icongrid",
items: aOptions.items,
id: aOptions.id
});
},
show: function(callback) {
this.callback = callback;
log("Sending message");

View File

@ -61,8 +61,8 @@
#define END_RESULT_BATCH_AND_REFRESH_CONTENTS() \
PR_BEGIN_MACRO \
nsNavHistoryResult* result = GetResult(); \
NS_ENSURE_STATE(result); \
if (result->mBatchInProgress) { \
NS_WARN_IF_FALSE(result, "Working with a non-live-updating Places container"); \
if (result && result->mBatchInProgress) { \
result->EndBatch(); \
} \
PR_END_MACRO