mirror of
https://repo.dactyloidae.xyz/Dactyloidae/UXP.git
synced 2026-09-08 00:38:39 +09:00
import FIREFOX_52_6_0esr_RELEASE from mozilla-esr52 hg repo
This commit is contained in:
commit
dcd9973243
150858 changed files with 23884658 additions and 0 deletions
|
|
@ -0,0 +1,59 @@
|
|||
/* -*- 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.JSONObject;
|
||||
import org.mozilla.gecko.R;
|
||||
import org.mozilla.gecko.widget.BasicColorPicker;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Color;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
|
||||
public class ColorPickerInput extends PromptInput {
|
||||
public static final String INPUT_TYPE = "color";
|
||||
public static final String LOGTAG = "GeckoColorPickerInput";
|
||||
|
||||
private final boolean mShowAdvancedButton = true;
|
||||
private final int mInitialColor;
|
||||
|
||||
public ColorPickerInput(JSONObject obj) {
|
||||
super(obj);
|
||||
String init = obj.optString("value");
|
||||
mInitialColor = Color.rgb(Integer.parseInt(init.substring(1, 3), 16),
|
||||
Integer.parseInt(init.substring(3, 5), 16),
|
||||
Integer.parseInt(init.substring(5, 7), 16));
|
||||
}
|
||||
|
||||
@Override
|
||||
public View getView(Context context) throws UnsupportedOperationException {
|
||||
LayoutInflater inflater = LayoutInflater.from(context);
|
||||
mView = inflater.inflate(R.layout.basic_color_picker_dialog, null);
|
||||
|
||||
BasicColorPicker cp = (BasicColorPicker) mView.findViewById(R.id.colorpicker);
|
||||
cp.setColor(mInitialColor);
|
||||
|
||||
return mView;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getValue() {
|
||||
BasicColorPicker cp = (BasicColorPicker) mView.findViewById(R.id.colorpicker);
|
||||
int color = cp.getColor();
|
||||
return "#" + Integer.toHexString(color).substring(2);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getScrollable() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canApplyInputStyle() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,171 @@
|
|||
/* -*- 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 java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONObject;
|
||||
import org.mozilla.gecko.GeckoAppShell;
|
||||
import org.mozilla.gecko.R;
|
||||
import org.mozilla.gecko.util.ResourceDrawableUtils;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.text.TextUtils;
|
||||
import android.view.Display;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.WindowManager;
|
||||
import android.widget.AdapterView;
|
||||
import android.widget.AdapterView.OnItemClickListener;
|
||||
import android.widget.ArrayAdapter;
|
||||
import android.widget.GridView;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
|
||||
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; // Current selection
|
||||
private final 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.setNumColumns(Math.min(items.size(), maxColumns));
|
||||
view.setOnItemClickListener(this);
|
||||
// Despite what the docs say, setItemChecked was not moved into the AbsListView class until sometime between
|
||||
// Android 2.3.7 and Android 4.0.3. For other versions the item won't be visually highlighted, BUT we really only
|
||||
// mSelected will still be set so that we default to its behavior.
|
||||
if (mSelected > -1) {
|
||||
view.setItemChecked(mSelected, true);
|
||||
}
|
||||
|
||||
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;
|
||||
notifyListeners(Integer.toString(position));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getValue() {
|
||||
return 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");
|
||||
|
||||
ResourceDrawableUtils.getDrawable(context, iconUrl, new ResourceDrawableUtils.BitmapLoader() {
|
||||
@Override
|
||||
public void onBitmapFound(Drawable d) {
|
||||
icon = d;
|
||||
if (mAdapter != null) {
|
||||
mAdapter.notifyDataSetChanged();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,158 @@
|
|||
/* 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.mozilla.gecko.GeckoAppShell;
|
||||
import org.mozilla.gecko.util.ThreadUtils;
|
||||
import org.mozilla.gecko.widget.GeckoActionProvider;
|
||||
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.ResolveInfo;
|
||||
import android.widget.ListView;
|
||||
import android.util.Log;
|
||||
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Shows a prompt letting the user pick from a list of intent handlers for a set of Intents or
|
||||
* for a GeckoActionProvider. Basic usage:
|
||||
* IntentChooserPrompt prompt = new IntentChooserPrompt(context, new Intent[] {
|
||||
* ... // some intents
|
||||
* });
|
||||
* prompt.show("Title", context, new IntentHandler() {
|
||||
* public void onIntentSelected(Intent intent, int position) { }
|
||||
* public void onCancelled() { }
|
||||
* });
|
||||
**/
|
||||
public class IntentChooserPrompt {
|
||||
private static final String LOGTAG = "GeckoIntentChooser";
|
||||
|
||||
private final ArrayList<PromptListItem> mItems;
|
||||
|
||||
public IntentChooserPrompt(Context context, Intent[] intents) {
|
||||
mItems = getItems(context, intents);
|
||||
}
|
||||
|
||||
public IntentChooserPrompt(Context context, GeckoActionProvider provider) {
|
||||
mItems = getItems(context, provider);
|
||||
}
|
||||
|
||||
/* If an IntentHandler is passed in, will asynchronously call the handler when the dialog is closed
|
||||
* Otherwise, will return the Intent that was chosen by the user. Must be called on the UI thread.
|
||||
*/
|
||||
public void show(final String title, final Context context, final IntentHandler handler) {
|
||||
ThreadUtils.assertOnUiThread();
|
||||
|
||||
if (mItems.isEmpty()) {
|
||||
Log.i(LOGTAG, "No activities for the intent chooser!");
|
||||
handler.onCancelled();
|
||||
return;
|
||||
}
|
||||
|
||||
// If there's only one item in the intent list, just return it
|
||||
if (mItems.size() == 1) {
|
||||
handler.onIntentSelected(mItems.get(0).getIntent(), 0);
|
||||
return;
|
||||
}
|
||||
|
||||
final Prompt prompt = new Prompt(context, new Prompt.PromptCallback() {
|
||||
@Override
|
||||
public void onPromptFinished(String promptServiceResult) {
|
||||
if (handler == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
int itemId = -1;
|
||||
try {
|
||||
itemId = new JSONObject(promptServiceResult).getInt("button");
|
||||
} catch (JSONException e) {
|
||||
Log.e(LOGTAG, "result from promptservice was invalid: ", e);
|
||||
}
|
||||
|
||||
if (itemId == -1) {
|
||||
handler.onCancelled();
|
||||
} else {
|
||||
handler.onIntentSelected(mItems.get(itemId).getIntent(), itemId);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
PromptListItem[] arrays = new PromptListItem[mItems.size()];
|
||||
mItems.toArray(arrays);
|
||||
prompt.show(title, "", arrays, ListView.CHOICE_MODE_NONE);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Whether or not any activities were found. Useful for checking if you should try a different Intent set
|
||||
public boolean hasActivities(Context context) {
|
||||
return mItems.isEmpty();
|
||||
}
|
||||
|
||||
// Gets a list of PromptListItems for an Intent array
|
||||
private ArrayList<PromptListItem> getItems(final Context context, Intent[] intents) {
|
||||
final ArrayList<PromptListItem> items = new ArrayList<PromptListItem>();
|
||||
|
||||
// If we have intents, use them to build the initial list
|
||||
for (final Intent intent : intents) {
|
||||
items.addAll(getItemsForIntent(context, intent));
|
||||
}
|
||||
|
||||
return items;
|
||||
}
|
||||
|
||||
// Gets a list of PromptListItems for a GeckoActionProvider
|
||||
private ArrayList<PromptListItem> getItems(final Context context, final GeckoActionProvider provider) {
|
||||
final ArrayList<PromptListItem> items = new ArrayList<PromptListItem>();
|
||||
|
||||
// Add any intents from the provider.
|
||||
final PackageManager packageManager = context.getPackageManager();
|
||||
final ArrayList<ResolveInfo> infos = provider.getSortedActivities();
|
||||
|
||||
for (final ResolveInfo info : infos) {
|
||||
items.add(getItemForResolveInfo(info, packageManager, provider.getIntent()));
|
||||
}
|
||||
|
||||
return items;
|
||||
}
|
||||
|
||||
private PromptListItem getItemForResolveInfo(ResolveInfo info, PackageManager pm, Intent intent) {
|
||||
PromptListItem item = new PromptListItem(info.loadLabel(pm).toString());
|
||||
item.setIcon(info.loadIcon(pm));
|
||||
|
||||
Intent i = new Intent(intent);
|
||||
// These intents should be implicit.
|
||||
i.setComponent(new ComponentName(info.activityInfo.applicationInfo.packageName,
|
||||
info.activityInfo.name));
|
||||
item.setIntent(new Intent(i));
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
private ArrayList<PromptListItem> getItemsForIntent(Context context, Intent intent) {
|
||||
ArrayList<PromptListItem> items = new ArrayList<PromptListItem>();
|
||||
PackageManager pm = context.getPackageManager();
|
||||
List<ResolveInfo> lri = pm.queryIntentActivityOptions(GeckoAppShell.getGeckoInterface().getActivity().getComponentName(), null, intent, 0);
|
||||
|
||||
// If we didn't find any activities, just return the empty list
|
||||
if (lri == null) {
|
||||
return items;
|
||||
}
|
||||
|
||||
// Otherwise, convert the ResolveInfo. Note we don't currently check for duplicates here.
|
||||
for (ResolveInfo ri : lri) {
|
||||
items.add(getItemForResolveInfo(ri, pm, intent));
|
||||
}
|
||||
|
||||
return items;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
/* 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 android.content.Intent;
|
||||
|
||||
public interface IntentHandler {
|
||||
public void onIntentSelected(Intent intent, int position);
|
||||
public void onCancelled();
|
||||
}
|
||||
586
mobile/android/base/java/org/mozilla/gecko/prompts/Prompt.java
Normal file
586
mobile/android/base/java/org/mozilla/gecko/prompts/Prompt.java
Normal file
|
|
@ -0,0 +1,586 @@
|
|||
/* -*- 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.JSONException;
|
||||
import org.json.JSONObject;
|
||||
import org.mozilla.gecko.GeckoAppShell;
|
||||
import org.mozilla.gecko.R;
|
||||
import org.mozilla.gecko.gfx.LayerView;
|
||||
import org.mozilla.gecko.util.ThreadUtils;
|
||||
import org.mozilla.gecko.Tab;
|
||||
import org.mozilla.gecko.Tabs;
|
||||
|
||||
import android.app.AlertDialog;
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.DialogInterface.OnCancelListener;
|
||||
import android.content.DialogInterface.OnClickListener;
|
||||
import android.content.res.Resources;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.widget.AdapterView;
|
||||
import android.widget.AdapterView.OnItemClickListener;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.ListView;
|
||||
import android.widget.ScrollView;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
public class Prompt implements OnClickListener, OnCancelListener, OnItemClickListener,
|
||||
PromptInput.OnChangeListener, Tabs.OnTabsChangedListener {
|
||||
private static final String LOGTAG = "GeckoPromptService";
|
||||
|
||||
private String[] mButtons;
|
||||
private PromptInput[] mInputs;
|
||||
private AlertDialog mDialog;
|
||||
private int mDoubleTapButtonType;
|
||||
|
||||
private final LayoutInflater mInflater;
|
||||
private final Context mContext;
|
||||
private PromptCallback mCallback;
|
||||
private String mGuid;
|
||||
private PromptListAdapter mAdapter;
|
||||
|
||||
private static boolean mInitialized;
|
||||
private static int mInputPaddingSize;
|
||||
|
||||
private int mTabId = Tabs.INVALID_TAB_ID;
|
||||
private Object mPreviousInputValue = null;
|
||||
|
||||
public Prompt(Context context, PromptCallback callback) {
|
||||
this(context);
|
||||
mCallback = callback;
|
||||
}
|
||||
|
||||
private Prompt(Context context) {
|
||||
mContext = context;
|
||||
mInflater = LayoutInflater.from(mContext);
|
||||
|
||||
if (!mInitialized) {
|
||||
Resources res = mContext.getResources();
|
||||
mInputPaddingSize = (int) (res.getDimension(R.dimen.prompt_service_inputs_padding));
|
||||
mInitialized = true;
|
||||
}
|
||||
}
|
||||
|
||||
private View applyInputStyle(View view, PromptInput input) {
|
||||
// Don't add padding to color picker views
|
||||
if (input.canApplyInputStyle()) {
|
||||
view.setPadding(mInputPaddingSize, 0, mInputPaddingSize, 0);
|
||||
}
|
||||
return view;
|
||||
}
|
||||
|
||||
public void show(JSONObject message) {
|
||||
String title = message.optString("title");
|
||||
String text = message.optString("text");
|
||||
mGuid = message.optString("guid");
|
||||
|
||||
mButtons = getStringArray(message, "buttons");
|
||||
final int buttonCount = mButtons == null ? 0 : mButtons.length;
|
||||
mDoubleTapButtonType = convertIndexToButtonType(message.optInt("doubleTapButton", -1), buttonCount);
|
||||
mPreviousInputValue = null;
|
||||
|
||||
JSONArray inputs = getSafeArray(message, "inputs");
|
||||
mInputs = new PromptInput[inputs.length()];
|
||||
for (int i = 0; i < mInputs.length; i++) {
|
||||
try {
|
||||
mInputs[i] = PromptInput.getInput(inputs.getJSONObject(i));
|
||||
mInputs[i].setListener(this);
|
||||
} catch (Exception ex) { }
|
||||
}
|
||||
|
||||
PromptListItem[] menuitems = PromptListItem.getArray(message.optJSONArray("listitems"));
|
||||
String selected = message.optString("choiceMode");
|
||||
|
||||
int choiceMode = ListView.CHOICE_MODE_NONE;
|
||||
if ("single".equals(selected)) {
|
||||
choiceMode = ListView.CHOICE_MODE_SINGLE;
|
||||
} else if ("multiple".equals(selected)) {
|
||||
choiceMode = ListView.CHOICE_MODE_MULTIPLE;
|
||||
}
|
||||
|
||||
if (message.has("tabId")) {
|
||||
mTabId = message.optInt("tabId", Tabs.INVALID_TAB_ID);
|
||||
}
|
||||
|
||||
show(title, text, menuitems, choiceMode);
|
||||
}
|
||||
|
||||
private int convertIndexToButtonType(final int buttonIndex, final int buttonCount) {
|
||||
if (buttonIndex < 0 || buttonIndex >= buttonCount) {
|
||||
// All valid DialogInterface button values are < 0,
|
||||
// so we return 0 as an invalid value.
|
||||
return 0;
|
||||
}
|
||||
|
||||
switch (buttonIndex) {
|
||||
case 0:
|
||||
return DialogInterface.BUTTON_POSITIVE;
|
||||
case 1:
|
||||
return DialogInterface.BUTTON_NEUTRAL;
|
||||
case 2:
|
||||
return DialogInterface.BUTTON_NEGATIVE;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
public void show(String title, String text, PromptListItem[] listItems, int choiceMode) {
|
||||
ThreadUtils.assertOnUiThread();
|
||||
|
||||
try {
|
||||
create(title, text, listItems, choiceMode);
|
||||
} catch (IllegalStateException ex) {
|
||||
Log.i(LOGTAG, "Error building dialog", ex);
|
||||
return;
|
||||
}
|
||||
|
||||
if (mTabId != Tabs.INVALID_TAB_ID) {
|
||||
Tabs.registerOnTabsChangedListener(this);
|
||||
|
||||
final Tab tab = Tabs.getInstance().getTab(mTabId);
|
||||
if (Tabs.getInstance().getSelectedTab() == tab) {
|
||||
mDialog.show();
|
||||
}
|
||||
} else {
|
||||
mDialog.show();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTabChanged(final Tab tab, final Tabs.TabEvents msg, final String data) {
|
||||
if (tab != Tabs.getInstance().getTab(mTabId)) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (msg) {
|
||||
case SELECTED:
|
||||
Log.i(LOGTAG, "Selected");
|
||||
mDialog.show();
|
||||
break;
|
||||
case UNSELECTED:
|
||||
Log.i(LOGTAG, "Unselected");
|
||||
mDialog.hide();
|
||||
break;
|
||||
case LOCATION_CHANGE:
|
||||
Log.i(LOGTAG, "Location change");
|
||||
mDialog.cancel();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void create(String title, String text, PromptListItem[] listItems, int choiceMode)
|
||||
throws IllegalStateException {
|
||||
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(mContext);
|
||||
if (!TextUtils.isEmpty(title)) {
|
||||
// Long strings can delay showing the dialog, so we cap the number of characters shown to 256.
|
||||
builder.setTitle(title.substring(0, Math.min(title.length(), 256)));
|
||||
}
|
||||
|
||||
if (!TextUtils.isEmpty(text)) {
|
||||
builder.setMessage(text);
|
||||
}
|
||||
|
||||
// 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, choiceMode);
|
||||
} else if (!addInputs(builder)) {
|
||||
throw new IllegalStateException("Could not add inputs to dialog");
|
||||
}
|
||||
|
||||
int length = mButtons == null ? 0 : mButtons.length;
|
||||
if (length > 0) {
|
||||
builder.setPositiveButton(mButtons[0], this);
|
||||
if (length > 1) {
|
||||
builder.setNeutralButton(mButtons[1], this);
|
||||
if (length > 2) {
|
||||
builder.setNegativeButton(mButtons[2], this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mDialog = builder.create();
|
||||
mDialog.setOnCancelListener(Prompt.this);
|
||||
}
|
||||
|
||||
public void setButtons(String[] buttons) {
|
||||
mButtons = buttons;
|
||||
}
|
||||
|
||||
public void setInputs(PromptInput[] inputs) {
|
||||
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, sets a
|
||||
* selected attribute to an array of booleans.
|
||||
*/
|
||||
private void addListResult(final JSONObject result, int which) {
|
||||
if (mAdapter == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
JSONArray selected = new JSONArray();
|
||||
|
||||
// If the button has already been filled in
|
||||
ArrayList<Integer> selectedItems = mAdapter.getSelected();
|
||||
for (Integer item : selectedItems) {
|
||||
selected.put(item);
|
||||
}
|
||||
|
||||
// If we haven't assigned a button yet, or we assigned it to -1, assign the which
|
||||
// parameter to both selected and the button.
|
||||
if (!result.has("button") || result.optInt("button") == -1) {
|
||||
if (!selectedItems.contains(which)) {
|
||||
selected.put(which);
|
||||
}
|
||||
|
||||
result.put("button", which);
|
||||
}
|
||||
|
||||
result.put("list", selected);
|
||||
} 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++) {
|
||||
if (mInputs[i] != null) {
|
||||
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 dialog, int which) {
|
||||
ThreadUtils.assertOnUiThread();
|
||||
closeDialog(which);
|
||||
}
|
||||
|
||||
/* 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.
|
||||
*
|
||||
* @param builder
|
||||
* The alert builder currently building this dialog.
|
||||
* @param listItems
|
||||
* The items to add.
|
||||
* @param choiceMode
|
||||
* One of the ListView.CHOICE_MODE constants to designate whether this list shows checkmarks, radios buttons, or nothing.
|
||||
*/
|
||||
private void addListItems(AlertDialog.Builder builder, PromptListItem[] listItems, int choiceMode) {
|
||||
switch (choiceMode) {
|
||||
case ListView.CHOICE_MODE_MULTIPLE_MODAL:
|
||||
case ListView.CHOICE_MODE_MULTIPLE:
|
||||
addMultiSelectList(builder, listItems);
|
||||
break;
|
||||
case ListView.CHOICE_MODE_SINGLE:
|
||||
addSingleSelectList(builder, listItems);
|
||||
break;
|
||||
case ListView.CHOICE_MODE_NONE:
|
||||
default:
|
||||
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) {
|
||||
ListView listView = (ListView) mInflater.inflate(R.layout.select_dialog_list, null);
|
||||
listView.setOnItemClickListener(this);
|
||||
listView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);
|
||||
|
||||
mAdapter = new PromptListAdapter(mContext, R.layout.select_dialog_multichoice, listItems);
|
||||
listView.setAdapter(mAdapter);
|
||||
builder.setView(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) {
|
||||
mAdapter = new PromptListAdapter(mContext, R.layout.select_dialog_singlechoice, listItems);
|
||||
builder.setSingleChoiceItems(mAdapter, mAdapter.getSelectedIndex(), new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
// The adapter isn't aware of single vs. multi choice lists, so manually
|
||||
// clear any other selected items first.
|
||||
ArrayList<Integer> selected = mAdapter.getSelected();
|
||||
for (Integer sel : selected) {
|
||||
mAdapter.toggleSelected(sel);
|
||||
}
|
||||
|
||||
// Now select this item.
|
||||
mAdapter.toggleSelected(which);
|
||||
closeIfNoButtons(which);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/* 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) {
|
||||
mAdapter = new PromptListAdapter(mContext, android.R.layout.simple_list_item_1, listItems);
|
||||
builder.setAdapter(mAdapter, this);
|
||||
}
|
||||
|
||||
/* Wraps an input in a linearlayout. We do this so that we can set padding that appears outside the background
|
||||
* drawable for the view.
|
||||
*/
|
||||
private View wrapInput(final PromptInput input) {
|
||||
final LinearLayout linearLayout = new LinearLayout(mContext);
|
||||
linearLayout.setOrientation(LinearLayout.VERTICAL);
|
||||
applyInputStyle(linearLayout, input);
|
||||
|
||||
linearLayout.addView(input.getView(mContext));
|
||||
|
||||
return linearLayout;
|
||||
}
|
||||
|
||||
/* 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 version.
|
||||
*/
|
||||
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 inputs are scrollable, we won't wrap this in a ScrollView
|
||||
|
||||
if (length == 1) {
|
||||
root = wrapInput(mInputs[0]);
|
||||
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 = wrapInput(mInputs[i]);
|
||||
linearLayout.addView(content);
|
||||
scrollable |= mInputs[i].getScrollable();
|
||||
}
|
||||
root = linearLayout;
|
||||
}
|
||||
|
||||
if (scrollable) {
|
||||
// If we're showing some sort of scrollable list, force an inverse background.
|
||||
builder.setInverseBackgroundForced(true);
|
||||
builder.setView(root);
|
||||
} else {
|
||||
ScrollView view = new ScrollView(mContext);
|
||||
view.addView(root);
|
||||
builder.setView(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();
|
||||
mAdapter.toggleSelected(position);
|
||||
|
||||
// If there are no buttons on this dialog, then we take selecting an item as a sign to close
|
||||
// the dialog. Note that means it will be hard to select multiple things in this list, but
|
||||
// given there is no way to confirm+close the dialog, it seems reasonable.
|
||||
closeIfNoButtons(position);
|
||||
}
|
||||
|
||||
private boolean closeIfNoButtons(int selected) {
|
||||
ThreadUtils.assertOnUiThread();
|
||||
if (mButtons == null || mButtons.length == 0) {
|
||||
closeDialog(selected);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/* @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);
|
||||
notifyClosing(ret);
|
||||
}
|
||||
|
||||
/* Called any time we're closing the dialog to cleanup and notify listeners that the dialog
|
||||
* is closing.
|
||||
*/
|
||||
private void closeDialog(int which) {
|
||||
JSONObject ret = new JSONObject();
|
||||
mDialog.dismiss();
|
||||
|
||||
addButtonResult(ret, which);
|
||||
addListResult(ret, which);
|
||||
addInputValues(ret);
|
||||
|
||||
notifyClosing(ret);
|
||||
}
|
||||
|
||||
/* Called any time we're closing the dialog to cleanup and notify listeners that the dialog
|
||||
* is closing.
|
||||
*/
|
||||
private void notifyClosing(JSONObject aReturn) {
|
||||
try {
|
||||
aReturn.put("guid", mGuid);
|
||||
} catch (JSONException ex) { }
|
||||
|
||||
if (mTabId != Tabs.INVALID_TAB_ID) {
|
||||
Tabs.unregisterOnTabsChangedListener(this);
|
||||
}
|
||||
|
||||
if (mCallback != null) {
|
||||
mCallback.onPromptFinished(aReturn.toString());
|
||||
}
|
||||
}
|
||||
|
||||
// Called when the prompt inputs on the dialog change
|
||||
@Override
|
||||
public void onChange(PromptInput input) {
|
||||
// If there are no buttons on this dialog, assuming that "changing" an input
|
||||
// means something was selected and we can close. This provides a way to tap
|
||||
// on a list item and close the dialog automatically.
|
||||
if (!closeIfNoButtons(-1)) {
|
||||
// Alternatively, if a default button has been specified for double tapping,
|
||||
// we want to close the dialog if the same input value has been transmitted
|
||||
// twice in a row.
|
||||
closeIfDoubleTapEnabled(input.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
private boolean closeIfDoubleTapEnabled(Object inputValue) {
|
||||
if (mDoubleTapButtonType != 0 && inputValue == mPreviousInputValue) {
|
||||
closeDialog(mDoubleTapButtonType);
|
||||
return true;
|
||||
}
|
||||
mPreviousInputValue = inputValue;
|
||||
return false;
|
||||
}
|
||||
|
||||
private static JSONArray getSafeArray(JSONObject json, String key) {
|
||||
try {
|
||||
return json.getJSONArray(key);
|
||||
} catch (Exception e) {
|
||||
return new JSONArray();
|
||||
}
|
||||
}
|
||||
|
||||
public static String[] getStringArray(JSONObject aObject, String aName) {
|
||||
JSONArray items = getSafeArray(aObject, aName);
|
||||
int length = items.length();
|
||||
String[] list = new String[length];
|
||||
for (int i = 0; i < length; i++) {
|
||||
try {
|
||||
list[i] = items.getString(i);
|
||||
} catch (Exception ex) { }
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
private static boolean[] getBooleanArray(JSONObject aObject, String aName) {
|
||||
JSONArray items = new JSONArray();
|
||||
try {
|
||||
items = aObject.getJSONArray(aName);
|
||||
} catch (Exception ex) { return null; }
|
||||
int length = items.length();
|
||||
boolean[] list = new boolean[length];
|
||||
for (int i = 0; i < length; i++) {
|
||||
try {
|
||||
list[i] = items.getBoolean(i);
|
||||
} catch (Exception ex) { }
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
public interface PromptCallback {
|
||||
|
||||
/**
|
||||
* Called when the Prompt has been completed (i.e. when the user has selected an item or action in the Prompt).
|
||||
* This callback is run on the UI thread.
|
||||
*/
|
||||
public void onPromptFinished(String jsonResult);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,398 @@
|
|||
/* -*- 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 java.text.SimpleDateFormat;
|
||||
import java.util.Calendar;
|
||||
import java.util.GregorianCalendar;
|
||||
|
||||
import org.json.JSONObject;
|
||||
import org.mozilla.gecko.AppConstants.Versions;
|
||||
import org.mozilla.gecko.widget.AllCapsTextView;
|
||||
import org.mozilla.gecko.widget.DateTimePicker;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.Configuration;
|
||||
import android.support.design.widget.TextInputLayout;
|
||||
import android.support.v7.widget.AppCompatCheckBox;
|
||||
import android.text.Html;
|
||||
import android.text.InputType;
|
||||
import android.text.TextUtils;
|
||||
import android.text.format.DateFormat;
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup.LayoutParams;
|
||||
import android.view.inputmethod.InputMethodManager;
|
||||
import android.widget.ArrayAdapter;
|
||||
import android.widget.CheckBox;
|
||||
import android.widget.DatePicker;
|
||||
import android.widget.EditText;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.Spinner;
|
||||
import android.widget.TextView;
|
||||
import android.widget.TimePicker;
|
||||
|
||||
public abstract class PromptInput {
|
||||
protected final String mLabel;
|
||||
protected final String mType;
|
||||
protected final String mId;
|
||||
protected final String mValue;
|
||||
protected final String mMinValue;
|
||||
protected final String mMaxValue;
|
||||
protected OnChangeListener mListener;
|
||||
protected View mView;
|
||||
public static final String LOGTAG = "GeckoPromptInput";
|
||||
|
||||
public interface OnChangeListener {
|
||||
void onChange(PromptInput input);
|
||||
}
|
||||
|
||||
public void setListener(OnChangeListener listener) {
|
||||
mListener = listener;
|
||||
}
|
||||
|
||||
public static class EditInput extends PromptInput {
|
||||
protected final String mHint;
|
||||
protected final boolean mAutofocus;
|
||||
public static final String INPUT_TYPE = "textbox";
|
||||
|
||||
public EditInput(JSONObject object) {
|
||||
super(object);
|
||||
mHint = object.optString("hint");
|
||||
mAutofocus = object.optBoolean("autofocus");
|
||||
}
|
||||
|
||||
@Override
|
||||
public View getView(final Context context) throws UnsupportedOperationException {
|
||||
EditText input = new EditText(context);
|
||||
input.setInputType(InputType.TYPE_CLASS_TEXT);
|
||||
input.setText(mValue);
|
||||
|
||||
if (!TextUtils.isEmpty(mHint)) {
|
||||
input.setHint(mHint);
|
||||
}
|
||||
|
||||
if (mAutofocus) {
|
||||
input.setOnFocusChangeListener(new View.OnFocusChangeListener() {
|
||||
@Override
|
||||
public void onFocusChange(View v, boolean hasFocus) {
|
||||
if (hasFocus) {
|
||||
((InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE)).showSoftInput(v, 0);
|
||||
}
|
||||
}
|
||||
});
|
||||
input.requestFocus();
|
||||
}
|
||||
|
||||
TextInputLayout inputLayout = new TextInputLayout(context);
|
||||
inputLayout.addView(input);
|
||||
|
||||
mView = (View) inputLayout;
|
||||
return mView;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getValue() {
|
||||
final TextInputLayout inputLayout = (TextInputLayout) mView;
|
||||
return inputLayout.getEditText().getText();
|
||||
}
|
||||
}
|
||||
|
||||
public static class NumberInput extends EditInput {
|
||||
public static final String INPUT_TYPE = "number";
|
||||
public NumberInput(JSONObject obj) {
|
||||
super(obj);
|
||||
}
|
||||
|
||||
@Override
|
||||
public View getView(final Context context) throws UnsupportedOperationException {
|
||||
final TextInputLayout inputLayout = (TextInputLayout) super.getView(context);
|
||||
final EditText input = inputLayout.getEditText();
|
||||
input.setRawInputType(Configuration.KEYBOARD_12KEY);
|
||||
input.setInputType(InputType.TYPE_CLASS_NUMBER |
|
||||
InputType.TYPE_NUMBER_FLAG_SIGNED);
|
||||
return input;
|
||||
}
|
||||
}
|
||||
|
||||
public static class PasswordInput extends EditInput {
|
||||
public static final String INPUT_TYPE = "password";
|
||||
public PasswordInput(JSONObject obj) {
|
||||
super(obj);
|
||||
}
|
||||
|
||||
@Override
|
||||
public View getView(Context context) throws UnsupportedOperationException {
|
||||
final TextInputLayout inputLayout = (TextInputLayout) super.getView(context);
|
||||
inputLayout.getEditText().setInputType(InputType.TYPE_CLASS_TEXT |
|
||||
InputType.TYPE_TEXT_VARIATION_PASSWORD |
|
||||
InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS);
|
||||
return inputLayout;
|
||||
}
|
||||
}
|
||||
|
||||
public static class CheckboxInput extends PromptInput {
|
||||
public static final String INPUT_TYPE = "checkbox";
|
||||
private final boolean mChecked;
|
||||
|
||||
public CheckboxInput(JSONObject obj) {
|
||||
super(obj);
|
||||
mChecked = obj.optBoolean("checked");
|
||||
}
|
||||
|
||||
@Override
|
||||
public View getView(Context context) throws UnsupportedOperationException {
|
||||
final CheckBox checkbox = new AppCompatCheckBox(context);
|
||||
checkbox.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT));
|
||||
checkbox.setText(mLabel);
|
||||
checkbox.setChecked(mChecked);
|
||||
mView = (View)checkbox;
|
||||
return mView;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getValue() {
|
||||
CheckBox checkbox = (CheckBox)mView;
|
||||
return checkbox.isChecked() ? Boolean.TRUE : Boolean.FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
public static class DateTimeInput extends PromptInput {
|
||||
public static final String[] INPUT_TYPES = new String[] {
|
||||
"date",
|
||||
"week",
|
||||
"time",
|
||||
"datetime-local",
|
||||
"datetime",
|
||||
"month"
|
||||
};
|
||||
|
||||
public DateTimeInput(JSONObject obj) {
|
||||
super(obj);
|
||||
}
|
||||
|
||||
@Override
|
||||
public View getView(Context context) throws UnsupportedOperationException {
|
||||
if (mType.equals("date")) {
|
||||
try {
|
||||
DateTimePicker input = new DateTimePicker(context, "yyyy-MM-dd", mValue,
|
||||
DateTimePicker.PickersState.DATE, mMinValue, mMaxValue);
|
||||
input.toggleCalendar(true);
|
||||
mView = (View)input;
|
||||
} catch (UnsupportedOperationException ex) {
|
||||
// We can't use our custom version of the DatePicker widget because the sdk is too old.
|
||||
// But we can fallback on the native one.
|
||||
DatePicker input = new DatePicker(context);
|
||||
try {
|
||||
if (!TextUtils.isEmpty(mValue)) {
|
||||
GregorianCalendar calendar = new GregorianCalendar();
|
||||
calendar.setTime(new SimpleDateFormat("yyyy-MM-dd").parse(mValue));
|
||||
input.updateDate(calendar.get(Calendar.YEAR),
|
||||
calendar.get(Calendar.MONTH),
|
||||
calendar.get(Calendar.DAY_OF_MONTH));
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Log.e(LOGTAG, "error parsing format string: " + e);
|
||||
}
|
||||
mView = (View)input;
|
||||
}
|
||||
} else if (mType.equals("week")) {
|
||||
DateTimePicker input = new DateTimePicker(context, "yyyy-'W'ww", mValue,
|
||||
DateTimePicker.PickersState.WEEK, mMinValue, mMaxValue);
|
||||
mView = (View)input;
|
||||
} else if (mType.equals("time")) {
|
||||
TimePicker input = new TimePicker(context);
|
||||
input.setIs24HourView(DateFormat.is24HourFormat(context));
|
||||
|
||||
GregorianCalendar calendar = new GregorianCalendar();
|
||||
if (!TextUtils.isEmpty(mValue)) {
|
||||
try {
|
||||
calendar.setTime(new SimpleDateFormat("HH:mm").parse(mValue));
|
||||
} catch (Exception e) { }
|
||||
}
|
||||
input.setCurrentHour(calendar.get(GregorianCalendar.HOUR_OF_DAY));
|
||||
input.setCurrentMinute(calendar.get(GregorianCalendar.MINUTE));
|
||||
mView = (View)input;
|
||||
} else if (mType.equals("datetime-local") || mType.equals("datetime")) {
|
||||
DateTimePicker input = new DateTimePicker(context, "yyyy-MM-dd HH:mm", mValue.replace("T", " ").replace("Z", ""),
|
||||
DateTimePicker.PickersState.DATETIME,
|
||||
mMinValue.replace("T", " ").replace("Z", ""), mMaxValue.replace("T", " ").replace("Z", ""));
|
||||
input.toggleCalendar(true);
|
||||
mView = (View)input;
|
||||
} else if (mType.equals("month")) {
|
||||
DateTimePicker input = new DateTimePicker(context, "yyyy-MM", mValue,
|
||||
DateTimePicker.PickersState.MONTH, mMinValue, mMaxValue);
|
||||
mView = (View)input;
|
||||
}
|
||||
return mView;
|
||||
}
|
||||
|
||||
private static String formatDateString(String dateFormat, Calendar calendar) {
|
||||
return new SimpleDateFormat(dateFormat).format(calendar.getTime());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getValue() {
|
||||
if (mType.equals("time")) {
|
||||
TimePicker tp = (TimePicker)mView;
|
||||
GregorianCalendar calendar =
|
||||
new GregorianCalendar(0, 0, 0, tp.getCurrentHour(), tp.getCurrentMinute());
|
||||
return formatDateString("HH:mm", calendar);
|
||||
} else {
|
||||
DateTimePicker dp = (DateTimePicker)mView;
|
||||
GregorianCalendar calendar = new GregorianCalendar();
|
||||
calendar.setTimeInMillis(dp.getTimeInMillis());
|
||||
if (mType.equals("date")) {
|
||||
return formatDateString("yyyy-MM-dd", calendar);
|
||||
} else if (mType.equals("week")) {
|
||||
return formatDateString("yyyy-'W'ww", calendar);
|
||||
} else if (mType.equals("datetime-local")) {
|
||||
return formatDateString("yyyy-MM-dd'T'HH:mm", calendar);
|
||||
} else if (mType.equals("datetime")) {
|
||||
calendar.set(GregorianCalendar.ZONE_OFFSET, 0);
|
||||
calendar.setTimeInMillis(dp.getTimeInMillis());
|
||||
return formatDateString("yyyy-MM-dd'T'HH:mm'Z'", calendar);
|
||||
} else if (mType.equals("month")) {
|
||||
return formatDateString("yyyy-MM", calendar);
|
||||
}
|
||||
}
|
||||
return super.getValue();
|
||||
}
|
||||
}
|
||||
|
||||
public static class MenulistInput extends PromptInput {
|
||||
public static final String INPUT_TYPE = "menulist";
|
||||
private static String[] mListitems;
|
||||
private static int mSelected;
|
||||
|
||||
public Spinner spinner;
|
||||
public AllCapsTextView textView;
|
||||
|
||||
public MenulistInput(JSONObject obj) {
|
||||
super(obj);
|
||||
mListitems = Prompt.getStringArray(obj, "values");
|
||||
mSelected = obj.optInt("selected");
|
||||
}
|
||||
|
||||
@Override
|
||||
public View getView(final Context context) throws UnsupportedOperationException {
|
||||
spinner = new Spinner(context, Spinner.MODE_DIALOG);
|
||||
try {
|
||||
if (mListitems.length > 0) {
|
||||
ArrayAdapter<String> adapter = new ArrayAdapter<String>(context, android.R.layout.simple_spinner_item, mListitems);
|
||||
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
|
||||
|
||||
spinner.setAdapter(adapter);
|
||||
spinner.setSelection(mSelected);
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
}
|
||||
|
||||
if (!TextUtils.isEmpty(mLabel)) {
|
||||
LinearLayout container = new LinearLayout(context);
|
||||
container.setOrientation(LinearLayout.VERTICAL);
|
||||
|
||||
textView = new AllCapsTextView(context, null);
|
||||
textView.setText(mLabel);
|
||||
container.addView(textView);
|
||||
|
||||
container.addView(spinner);
|
||||
return container;
|
||||
}
|
||||
|
||||
return spinner;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getValue() {
|
||||
return spinner.getSelectedItemPosition();
|
||||
}
|
||||
}
|
||||
|
||||
public static class LabelInput extends PromptInput {
|
||||
public static final String INPUT_TYPE = "label";
|
||||
public LabelInput(JSONObject obj) {
|
||||
super(obj);
|
||||
}
|
||||
|
||||
@Override
|
||||
public View getView(Context context) throws UnsupportedOperationException {
|
||||
// not really an input, but a way to add labels and such to the dialog
|
||||
TextView view = new TextView(context);
|
||||
view.setText(Html.fromHtml(mLabel));
|
||||
mView = view;
|
||||
return mView;
|
||||
}
|
||||
}
|
||||
|
||||
public PromptInput(JSONObject obj) {
|
||||
mLabel = obj.optString("label");
|
||||
mType = obj.optString("type");
|
||||
String id = obj.optString("id");
|
||||
mId = TextUtils.isEmpty(id) ? mType : id;
|
||||
mValue = obj.optString("value");
|
||||
mMaxValue = obj.optString("max");
|
||||
mMinValue = obj.optString("min");
|
||||
}
|
||||
|
||||
public static PromptInput getInput(JSONObject obj) {
|
||||
String type = obj.optString("type");
|
||||
switch (type) {
|
||||
case EditInput.INPUT_TYPE:
|
||||
return new EditInput(obj);
|
||||
case NumberInput.INPUT_TYPE:
|
||||
return new NumberInput(obj);
|
||||
case PasswordInput.INPUT_TYPE:
|
||||
return new PasswordInput(obj);
|
||||
case CheckboxInput.INPUT_TYPE:
|
||||
return new CheckboxInput(obj);
|
||||
case MenulistInput.INPUT_TYPE:
|
||||
return new MenulistInput(obj);
|
||||
case LabelInput.INPUT_TYPE:
|
||||
return new LabelInput(obj);
|
||||
case IconGridInput.INPUT_TYPE:
|
||||
return new IconGridInput(obj);
|
||||
case ColorPickerInput.INPUT_TYPE:
|
||||
return new ColorPickerInput(obj);
|
||||
case TabInput.INPUT_TYPE:
|
||||
return new TabInput(obj);
|
||||
default:
|
||||
for (String dtType : DateTimeInput.INPUT_TYPES) {
|
||||
if (dtType.equals(type)) {
|
||||
return new DateTimeInput(obj);
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public abstract View getView(Context context) throws UnsupportedOperationException;
|
||||
|
||||
public String getId() {
|
||||
return mId;
|
||||
}
|
||||
|
||||
public Object getValue() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public boolean getScrollable() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean canApplyInputStyle() {
|
||||
return true;
|
||||
}
|
||||
|
||||
protected void notifyListeners(String val) {
|
||||
if (mListener != null) {
|
||||
mListener.onChange(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,281 @@
|
|||
package org.mozilla.gecko.prompts;
|
||||
|
||||
import org.mozilla.gecko.R;
|
||||
import org.mozilla.gecko.Telemetry;
|
||||
import org.mozilla.gecko.TelemetryContract;
|
||||
import org.mozilla.gecko.menu.MenuItemSwitcherLayout;
|
||||
import org.mozilla.gecko.widget.GeckoActionProvider;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.res.Resources;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.drawable.BitmapDrawable;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.CheckedTextView;
|
||||
import android.widget.TextView;
|
||||
import android.widget.ListView;
|
||||
import android.widget.ArrayAdapter;
|
||||
import android.util.TypedValue;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
public class PromptListAdapter extends ArrayAdapter<PromptListItem> {
|
||||
private static final int VIEW_TYPE_ITEM = 0;
|
||||
private static final int VIEW_TYPE_GROUP = 1;
|
||||
private static final int VIEW_TYPE_ACTIONS = 2;
|
||||
private static final int VIEW_TYPE_COUNT = 3;
|
||||
|
||||
private static final String LOGTAG = "GeckoPromptListAdapter";
|
||||
|
||||
private final int mResourceId;
|
||||
private Drawable mBlankDrawable;
|
||||
private Drawable mMoreDrawable;
|
||||
private static int mGroupPaddingSize;
|
||||
private static int mLeftRightTextWithIconPadding;
|
||||
private static int mTopBottomTextWithIconPadding;
|
||||
private static int mIconSize;
|
||||
private static int mMinRowSize;
|
||||
private static int mIconTextPadding;
|
||||
private static float mTextSize;
|
||||
private static boolean mInitialized;
|
||||
|
||||
PromptListAdapter(Context context, int textViewResourceId, PromptListItem[] objects) {
|
||||
super(context, textViewResourceId, objects);
|
||||
mResourceId = textViewResourceId;
|
||||
init();
|
||||
}
|
||||
|
||||
private void init() {
|
||||
if (!mInitialized) {
|
||||
Resources res = getContext().getResources();
|
||||
mGroupPaddingSize = (int) (res.getDimension(R.dimen.prompt_service_group_padding_size));
|
||||
mLeftRightTextWithIconPadding = (int) (res.getDimension(R.dimen.prompt_service_left_right_text_with_icon_padding));
|
||||
mTopBottomTextWithIconPadding = (int) (res.getDimension(R.dimen.prompt_service_top_bottom_text_with_icon_padding));
|
||||
mIconTextPadding = (int) (res.getDimension(R.dimen.prompt_service_icon_text_padding));
|
||||
mIconSize = (int) (res.getDimension(R.dimen.prompt_service_icon_size));
|
||||
mMinRowSize = (int) (res.getDimension(R.dimen.menu_item_row_height));
|
||||
mTextSize = res.getDimension(R.dimen.menu_item_textsize);
|
||||
|
||||
mInitialized = true;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemViewType(int position) {
|
||||
PromptListItem item = getItem(position);
|
||||
if (item.isGroup) {
|
||||
return VIEW_TYPE_GROUP;
|
||||
} else if (item.showAsActions) {
|
||||
return VIEW_TYPE_ACTIONS;
|
||||
} else {
|
||||
return VIEW_TYPE_ITEM;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getViewTypeCount() {
|
||||
return VIEW_TYPE_COUNT;
|
||||
}
|
||||
|
||||
private Drawable getMoreDrawable(Resources res) {
|
||||
if (mMoreDrawable == null) {
|
||||
mMoreDrawable = res.getDrawable(R.drawable.menu_item_more);
|
||||
}
|
||||
return mMoreDrawable;
|
||||
}
|
||||
|
||||
private Drawable getBlankDrawable(Resources res) {
|
||||
if (mBlankDrawable == null) {
|
||||
mBlankDrawable = res.getDrawable(R.drawable.blank);
|
||||
}
|
||||
return mBlankDrawable;
|
||||
}
|
||||
|
||||
public void toggleSelected(int position) {
|
||||
PromptListItem item = getItem(position);
|
||||
item.setSelected(!item.getSelected());
|
||||
}
|
||||
|
||||
private void maybeUpdateIcon(PromptListItem item, TextView t) {
|
||||
if (item.getIcon() == null && !item.inGroup && !item.isParent) {
|
||||
t.setCompoundDrawablesWithIntrinsicBounds(null, null, null, null);
|
||||
return;
|
||||
}
|
||||
|
||||
Drawable d = null;
|
||||
Resources res = getContext().getResources();
|
||||
// Set the padding between the icon and the text.
|
||||
t.setCompoundDrawablePadding(mIconTextPadding);
|
||||
if (item.getIcon() != null) {
|
||||
// We want the icon to be of a specific size. Some do not
|
||||
// follow this rule so we have to resize them.
|
||||
Bitmap bitmap = ((BitmapDrawable) item.getIcon()).getBitmap();
|
||||
d = new BitmapDrawable(res, Bitmap.createScaledBitmap(bitmap, mIconSize, mIconSize, true));
|
||||
} else if (item.inGroup) {
|
||||
// We don't currently support "indenting" items with icons
|
||||
d = getBlankDrawable(res);
|
||||
}
|
||||
|
||||
Drawable moreDrawable = null;
|
||||
if (item.isParent) {
|
||||
moreDrawable = getMoreDrawable(res);
|
||||
}
|
||||
|
||||
if (d != null || moreDrawable != null) {
|
||||
t.setCompoundDrawablesWithIntrinsicBounds(d, null, moreDrawable, null);
|
||||
}
|
||||
}
|
||||
|
||||
private void maybeUpdateCheckedState(ListView list, int position, PromptListItem item, ViewHolder viewHolder) {
|
||||
viewHolder.textView.setEnabled(!item.disabled && !item.isGroup);
|
||||
viewHolder.textView.setClickable(item.isGroup || item.disabled);
|
||||
if (viewHolder.textView instanceof CheckedTextView) {
|
||||
// Apparently just using ct.setChecked(true) doesn't work, so this
|
||||
// is stolen from the android source code as a way to set the checked
|
||||
// state of these items
|
||||
list.setItemChecked(position, item.getSelected());
|
||||
}
|
||||
}
|
||||
|
||||
boolean isSelected(int position) {
|
||||
return getItem(position).getSelected();
|
||||
}
|
||||
|
||||
ArrayList<Integer> getSelected() {
|
||||
int length = getCount();
|
||||
|
||||
ArrayList<Integer> selected = new ArrayList<Integer>();
|
||||
for (int i = 0; i < length; i++) {
|
||||
if (isSelected(i)) {
|
||||
selected.add(i);
|
||||
}
|
||||
}
|
||||
|
||||
return selected;
|
||||
}
|
||||
|
||||
int getSelectedIndex() {
|
||||
int length = getCount();
|
||||
for (int i = 0; i < length; i++) {
|
||||
if (isSelected(i)) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
private View getActionView(PromptListItem item, final ListView list, final int position) {
|
||||
final GeckoActionProvider provider = GeckoActionProvider.getForType(item.getIntent().getType(), getContext());
|
||||
provider.setIntent(item.getIntent());
|
||||
|
||||
final MenuItemSwitcherLayout view = (MenuItemSwitcherLayout) provider.onCreateActionView(
|
||||
GeckoActionProvider.ActionViewType.CONTEXT_MENU);
|
||||
// If a quickshare button is clicked, we need to close the dialog.
|
||||
view.addActionButtonClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
ListView.OnItemClickListener listener = list.getOnItemClickListener();
|
||||
if (listener != null) {
|
||||
listener.onItemClick(list, view, position, position);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return view;
|
||||
}
|
||||
|
||||
private void updateActionView(final PromptListItem item, final MenuItemSwitcherLayout view, final ListView list, final int position) {
|
||||
view.setTitle(item.label);
|
||||
view.setIcon(item.getIcon());
|
||||
view.setSubMenuIndicator(item.isParent);
|
||||
|
||||
// If the share button is clicked, we need to close the dialog and then show an intent chooser
|
||||
view.setMenuItemClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
ListView.OnItemClickListener listener = list.getOnItemClickListener();
|
||||
if (listener != null) {
|
||||
listener.onItemClick(list, view, position, position);
|
||||
}
|
||||
|
||||
final GeckoActionProvider provider = GeckoActionProvider.getForType(item.getIntent().getType(), getContext());
|
||||
IntentChooserPrompt prompt = new IntentChooserPrompt(getContext(), provider);
|
||||
prompt.show(item.label, getContext(), new IntentHandler() {
|
||||
@Override
|
||||
public void onIntentSelected(final Intent intent, final int p) {
|
||||
provider.chooseActivity(p);
|
||||
|
||||
// Context: Sharing via content contextmenu list (no explicit session is active)
|
||||
Telemetry.sendUIEvent(TelemetryContract.Event.SHARE, TelemetryContract.Method.LIST, "promptlist");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCancelled() {
|
||||
// do nothing
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public View getView(int position, View convertView, ViewGroup parent) {
|
||||
PromptListItem item = getItem(position);
|
||||
int type = getItemViewType(position);
|
||||
ViewHolder viewHolder = null;
|
||||
|
||||
if (convertView == null) {
|
||||
if (type == VIEW_TYPE_ACTIONS) {
|
||||
convertView = getActionView(item, (ListView) parent, position);
|
||||
} else {
|
||||
int resourceId = mResourceId;
|
||||
if (item.isGroup) {
|
||||
resourceId = R.layout.list_item_header;
|
||||
}
|
||||
|
||||
LayoutInflater mInflater = LayoutInflater.from(getContext());
|
||||
convertView = mInflater.inflate(resourceId, null);
|
||||
convertView.setMinimumHeight(mMinRowSize);
|
||||
|
||||
TextView tv = (TextView) convertView.findViewById(android.R.id.text1);
|
||||
tv.setTextSize(TypedValue.COMPLEX_UNIT_PX, mTextSize);
|
||||
viewHolder = new ViewHolder(tv, tv.getPaddingLeft(), tv.getPaddingRight(),
|
||||
tv.getPaddingTop(), tv.getPaddingBottom());
|
||||
|
||||
convertView.setTag(viewHolder);
|
||||
}
|
||||
} else {
|
||||
viewHolder = (ViewHolder) convertView.getTag();
|
||||
}
|
||||
|
||||
if (type == VIEW_TYPE_ACTIONS) {
|
||||
updateActionView(item, (MenuItemSwitcherLayout) convertView, (ListView) parent, position);
|
||||
} else {
|
||||
viewHolder.textView.setText(item.label);
|
||||
maybeUpdateCheckedState((ListView) parent, position, item, viewHolder);
|
||||
maybeUpdateIcon(item, viewHolder.textView);
|
||||
}
|
||||
|
||||
return convertView;
|
||||
}
|
||||
|
||||
private static class ViewHolder {
|
||||
public final TextView textView;
|
||||
public final int paddingLeft;
|
||||
public final int paddingRight;
|
||||
public final int paddingTop;
|
||||
public final int paddingBottom;
|
||||
|
||||
ViewHolder(TextView aTextView, int aLeft, int aRight, int aTop, int aBottom) {
|
||||
textView = aTextView;
|
||||
paddingLeft = aLeft;
|
||||
paddingRight = aRight;
|
||||
paddingTop = aTop;
|
||||
paddingBottom = aBottom;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,128 @@
|
|||
package org.mozilla.gecko.prompts;
|
||||
|
||||
import org.json.JSONException;
|
||||
import org.mozilla.gecko.IntentHelper;
|
||||
import org.mozilla.gecko.GeckoAppShell;
|
||||
import org.mozilla.gecko.ThumbnailHelper;
|
||||
import org.mozilla.gecko.util.ResourceDrawableUtils;
|
||||
import org.mozilla.gecko.widget.GeckoActionProvider;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.graphics.drawable.Drawable;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.ArrayList;
|
||||
|
||||
// This class should die and be replaced with normal menu items
|
||||
public class PromptListItem {
|
||||
private static final String LOGTAG = "GeckoPromptListItem";
|
||||
public final String label;
|
||||
public final boolean isGroup;
|
||||
public final boolean inGroup;
|
||||
public final boolean disabled;
|
||||
public final int id;
|
||||
public final boolean showAsActions;
|
||||
public final boolean isParent;
|
||||
|
||||
public Intent mIntent;
|
||||
public boolean mSelected;
|
||||
public Drawable mIcon;
|
||||
|
||||
PromptListItem(JSONObject aObject) {
|
||||
Context context = GeckoAppShell.getContext();
|
||||
label = aObject.isNull("label") ? "" : aObject.optString("label");
|
||||
isGroup = aObject.optBoolean("isGroup");
|
||||
inGroup = aObject.optBoolean("inGroup");
|
||||
disabled = aObject.optBoolean("disabled");
|
||||
id = aObject.optInt("id");
|
||||
mSelected = aObject.optBoolean("selected");
|
||||
|
||||
JSONObject obj = aObject.optJSONObject("showAsActions");
|
||||
if (obj != null) {
|
||||
showAsActions = true;
|
||||
String uri = obj.isNull("uri") ? "" : obj.optString("uri");
|
||||
String type = obj.isNull("type") ? GeckoActionProvider.DEFAULT_MIME_TYPE :
|
||||
obj.optString("type", GeckoActionProvider.DEFAULT_MIME_TYPE);
|
||||
|
||||
mIntent = IntentHelper.getShareIntent(context, uri, type, "");
|
||||
isParent = true;
|
||||
} else {
|
||||
mIntent = null;
|
||||
showAsActions = false;
|
||||
// Support both "isParent" (backwards compat for older consumers), and "menu" for the new Tabbed prompt ui.
|
||||
isParent = aObject.optBoolean("isParent") || aObject.optBoolean("menu");
|
||||
}
|
||||
|
||||
final String iconStr = aObject.optString("icon");
|
||||
if (iconStr != null) {
|
||||
final ResourceDrawableUtils.BitmapLoader loader = new ResourceDrawableUtils.BitmapLoader() {
|
||||
@Override
|
||||
public void onBitmapFound(Drawable d) {
|
||||
mIcon = d;
|
||||
}
|
||||
};
|
||||
|
||||
if (iconStr.startsWith("thumbnail:")) {
|
||||
final int id = Integer.parseInt(iconStr.substring(10), 10);
|
||||
ThumbnailHelper.getInstance().getAndProcessThumbnailFor(id, loader);
|
||||
} else {
|
||||
ResourceDrawableUtils.getDrawable(context, iconStr, loader);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void setIntent(Intent i) {
|
||||
mIntent = i;
|
||||
}
|
||||
|
||||
public Intent getIntent() {
|
||||
return mIntent;
|
||||
}
|
||||
|
||||
public void setIcon(Drawable icon) {
|
||||
mIcon = icon;
|
||||
}
|
||||
|
||||
public Drawable getIcon() {
|
||||
return mIcon;
|
||||
}
|
||||
|
||||
public void setSelected(boolean selected) {
|
||||
mSelected = selected;
|
||||
}
|
||||
|
||||
public boolean getSelected() {
|
||||
return mSelected;
|
||||
}
|
||||
|
||||
public PromptListItem(String aLabel) {
|
||||
label = aLabel;
|
||||
isGroup = false;
|
||||
inGroup = false;
|
||||
isParent = false;
|
||||
disabled = false;
|
||||
id = 0;
|
||||
showAsActions = false;
|
||||
}
|
||||
|
||||
static PromptListItem[] getArray(JSONArray items) {
|
||||
if (items == null) {
|
||||
return new PromptListItem[0];
|
||||
}
|
||||
|
||||
int length = items.length();
|
||||
List<PromptListItem> list = new ArrayList<>(length);
|
||||
for (int i = 0; i < length; i++) {
|
||||
try {
|
||||
PromptListItem item = new PromptListItem(items.getJSONObject(i));
|
||||
list.add(item);
|
||||
} catch (JSONException ex) { }
|
||||
}
|
||||
|
||||
return list.toArray(new PromptListItem[length]);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,72 @@
|
|||
/* -*- 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.JSONException;
|
||||
import org.json.JSONObject;
|
||||
import org.mozilla.gecko.EventDispatcher;
|
||||
import org.mozilla.gecko.GeckoApp;
|
||||
import org.mozilla.gecko.GeckoAppShell;
|
||||
import org.mozilla.gecko.util.GeckoEventListener;
|
||||
import org.mozilla.gecko.util.ThreadUtils;
|
||||
|
||||
import android.content.Context;
|
||||
import android.util.Log;
|
||||
|
||||
public class PromptService implements GeckoEventListener {
|
||||
private static final String LOGTAG = "GeckoPromptService";
|
||||
|
||||
private final Context mContext;
|
||||
|
||||
public PromptService(Context context) {
|
||||
GeckoApp.getEventDispatcher().registerGeckoThreadListener(this,
|
||||
"Prompt:Show",
|
||||
"Prompt:ShowTop");
|
||||
mContext = context;
|
||||
}
|
||||
|
||||
public void destroy() {
|
||||
GeckoApp.getEventDispatcher().unregisterGeckoThreadListener(this,
|
||||
"Prompt:Show",
|
||||
"Prompt:ShowTop");
|
||||
}
|
||||
|
||||
public void show(final String aTitle, final String aText, final PromptListItem[] aMenuList,
|
||||
final int aChoiceMode, final Prompt.PromptCallback callback) {
|
||||
// The dialog must be created on the UI thread.
|
||||
ThreadUtils.postToUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
Prompt p;
|
||||
p = new Prompt(mContext, callback);
|
||||
p.show(aTitle, aText, aMenuList, aChoiceMode);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// GeckoEventListener implementation
|
||||
@Override
|
||||
public void handleMessage(String event, final JSONObject message) {
|
||||
// The dialog must be created on the UI thread.
|
||||
ThreadUtils.postToUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
Prompt p;
|
||||
p = new Prompt(mContext, new Prompt.PromptCallback() {
|
||||
@Override
|
||||
public void onPromptFinished(String jsonResult) {
|
||||
try {
|
||||
EventDispatcher.sendResponse(message, new JSONObject(jsonResult));
|
||||
} catch (JSONException ex) {
|
||||
Log.i(LOGTAG, "Error building json response", ex);
|
||||
}
|
||||
}
|
||||
});
|
||||
p.show(message);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
107
mobile/android/base/java/org/mozilla/gecko/prompts/TabInput.java
Normal file
107
mobile/android/base/java/org/mozilla/gecko/prompts/TabInput.java
Normal file
|
|
@ -0,0 +1,107 @@
|
|||
/* -*- 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 java.util.LinkedHashMap;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
import org.mozilla.gecko.AppConstants.Versions;
|
||||
import org.mozilla.gecko.R;
|
||||
import org.mozilla.gecko.util.ThreadUtils;
|
||||
|
||||
import android.content.Context;
|
||||
import android.util.Log;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.widget.AdapterView;
|
||||
import android.widget.ListView;
|
||||
import android.widget.TabHost;
|
||||
import android.widget.TextView;
|
||||
|
||||
public class TabInput extends PromptInput implements AdapterView.OnItemClickListener {
|
||||
public static final String INPUT_TYPE = "tabs";
|
||||
public static final String LOGTAG = "GeckoTabInput";
|
||||
|
||||
/* Keeping the order of this in sync with the JSON is important. */
|
||||
final private LinkedHashMap<String, PromptListItem[]> mTabs;
|
||||
|
||||
private TabHost mHost;
|
||||
private int mPosition;
|
||||
|
||||
public TabInput(JSONObject obj) {
|
||||
super(obj);
|
||||
mTabs = new LinkedHashMap<String, PromptListItem[]>();
|
||||
try {
|
||||
JSONArray tabs = obj.getJSONArray("items");
|
||||
for (int i = 0; i < tabs.length(); i++) {
|
||||
JSONObject tab = tabs.getJSONObject(i);
|
||||
String title = tab.getString("label");
|
||||
JSONArray items = tab.getJSONArray("items");
|
||||
mTabs.put(title, PromptListItem.getArray(items));
|
||||
}
|
||||
} catch (JSONException ex) {
|
||||
Log.e(LOGTAG, "Exception", ex);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public View getView(final Context context) throws UnsupportedOperationException {
|
||||
final LayoutInflater inflater = LayoutInflater.from(context);
|
||||
mHost = (TabHost) inflater.inflate(R.layout.tab_prompt_input, null);
|
||||
mHost.setup();
|
||||
|
||||
for (String title : mTabs.keySet()) {
|
||||
final TabHost.TabSpec spec = mHost.newTabSpec(title);
|
||||
spec.setContent(new TabHost.TabContentFactory() {
|
||||
@Override
|
||||
public View createTabContent(final String tag) {
|
||||
PromptListAdapter adapter = new PromptListAdapter(context, android.R.layout.simple_list_item_1, mTabs.get(tag));
|
||||
ListView listView = new ListView(context);
|
||||
listView.setCacheColorHint(0);
|
||||
listView.setOnItemClickListener(TabInput.this);
|
||||
listView.setAdapter(adapter);
|
||||
return listView;
|
||||
}
|
||||
});
|
||||
|
||||
spec.setIndicator(title);
|
||||
mHost.addTab(spec);
|
||||
}
|
||||
mView = mHost;
|
||||
return mHost;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getValue() {
|
||||
JSONObject obj = new JSONObject();
|
||||
try {
|
||||
obj.put("tab", mHost.getCurrentTab());
|
||||
obj.put("item", mPosition);
|
||||
} catch (JSONException ex) { }
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getScrollable() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canApplyInputStyle() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
|
||||
ThreadUtils.assertOnUiThread();
|
||||
mPosition = position;
|
||||
notifyListeners(Integer.toString(position));
|
||||
}
|
||||
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue