Bug 1474888 - Fix Prompt.java's AlertDialog; r?sdaswani
PromptService will be informed when the screen is rotated and in turn it will
ask every Prompt shown to reset it's current layout.
For this, every Prompt will
- save it's current input value in PromptInput's mValue or similar field
already used for storing the default PromptInput value.
- create a new widget to be used in the new AlertDialog, with a new appropriate
layout for portrait / landscape. This is when the mValue field will be used
to initialize the new widget with the previous input.
MozReview-Commit-ID: L6eHyGNDt3d
--- a/mobile/android/base/java/org/mozilla/gecko/GeckoApp.java
+++ b/mobile/android/base/java/org/mozilla/gecko/GeckoApp.java
@@ -2181,16 +2181,21 @@ public abstract class GeckoApp extends G
// onConfigurationChanged is not called for 180 degree orientation changes,
// we will miss such rotations and the screen orientation will not be
// updated.
if (GeckoScreenOrientation.getInstance().update(newConfig.orientation)) {
if (mFormAssistPopup != null)
mFormAssistPopup.hide();
refreshChrome();
}
+
+ if (mPromptService != null) {
+ mPromptService.changePromptOrientation(newConfig.orientation);
+ }
+
super.onConfigurationChanged(newConfig);
}
public String getContentProcessName() {
return AppConstants.MOZ_CHILD_PROCESS_NAME;
}
public void addEnvToIntent(Intent intent) {
--- a/mobile/android/base/java/org/mozilla/gecko/customtabs/CustomTabsActivity.java
+++ b/mobile/android/base/java/org/mozilla/gecko/customtabs/CustomTabsActivity.java
@@ -4,16 +4,17 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
package org.mozilla.gecko.customtabs;
import android.app.PendingIntent;
import android.content.ActivityNotFoundException;
import android.content.Intent;
import android.content.pm.ResolveInfo;
+import android.content.res.Configuration;
import android.graphics.Bitmap;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.Bundle;
import android.provider.Browser;
import android.support.annotation.ColorInt;
import android.support.annotation.NonNull;
@@ -186,16 +187,25 @@ public class CustomTabsActivity extends
}
@Override
public void onRequestPermissionsResult(final int requestCode, final String[] permissions,
final int[] grantResults) {
Permissions.onRequestPermissionsResult(this, permissions, grantResults);
}
+ @Override
+ public void onConfigurationChanged(Configuration newConfig) {
+ super.onConfigurationChanged(newConfig);
+
+ if (mPromptService != null) {
+ mPromptService.changePromptOrientation(newConfig.orientation);
+ }
+ }
+
private void sendTelemetry() {
final SafeIntent startIntent = new SafeIntent(getIntent());
Telemetry.sendUIEvent(TelemetryContract.Event.LOAD_URL, TelemetryContract.Method.INTENT, "customtab");
if (IntentUtil.hasToolbarColor(startIntent)) {
Telemetry.sendUIEvent(TelemetryContract.Event.LOAD_URL, TelemetryContract.Method.INTENT, "customtab-hasToolbarColor");
}
if (IntentUtil.hasActionButton(startIntent)) {
--- a/mobile/android/base/java/org/mozilla/gecko/prompts/ColorPickerInput.java
+++ b/mobile/android/base/java/org/mozilla/gecko/prompts/ColorPickerInput.java
@@ -6,32 +6,30 @@
package org.mozilla.gecko.prompts;
import org.mozilla.gecko.R;
import org.mozilla.gecko.util.GeckoBundle;
import org.mozilla.gecko.widget.BasicColorPicker;
import android.content.Context;
import android.graphics.Color;
+import android.support.annotation.NonNull;
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 static final boolean mShowAdvancedButton = true;
- private final int mInitialColor;
+ private int mInitialColor;
public ColorPickerInput(GeckoBundle obj) {
super(obj);
- String init = obj.getString("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));
+ mInitialColor = getColorCode(obj.getString("value"));
}
@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);
@@ -51,9 +49,22 @@ public class ColorPickerInput extends Pr
public boolean getScrollable() {
return true;
}
@Override
public boolean canApplyInputStyle() {
return false;
}
+
+ @Override
+ public void saveCurrentInput(@NonNull final GeckoBundle userInput) {
+ if (userInput != null && userInput.containsKey(mId)) {
+ mInitialColor = getColorCode((String) userInput.get(mId));
+ }
+ }
+
+ private int getColorCode(@NonNull final String color) {
+ return Color.rgb(Integer.parseInt(color.substring(1, 3), 16),
+ Integer.parseInt(color.substring(3, 5), 16),
+ Integer.parseInt(color.substring(5, 7), 16));
+ }
}
--- a/mobile/android/base/java/org/mozilla/gecko/prompts/IconGridInput.java
+++ b/mobile/android/base/java/org/mozilla/gecko/prompts/IconGridInput.java
@@ -10,16 +10,17 @@ import java.util.List;
import org.mozilla.gecko.GeckoAppShell;
import org.mozilla.gecko.R;
import org.mozilla.gecko.util.GeckoBundle;
import org.mozilla.gecko.util.ResourceDrawableUtils;
import android.content.Context;
import android.graphics.drawable.Drawable;
+import android.support.annotation.NonNull;
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;
@@ -140,16 +141,21 @@ public class IconGridInput extends Promp
final ImageView icon = (ImageView) v.findViewById(R.id.icon);
icon.setImageDrawable(item.icon);
ViewGroup.LayoutParams lp = icon.getLayoutParams();
lp.width = lp.height = mIconSize;
}
}
+ @Override
+ public void saveCurrentInput(@NonNull final GeckoBundle userInput) {
+ // No user input to save
+ }
+
private class IconGridItem {
final String label;
final String description;
final boolean selected;
Drawable icon;
public IconGridItem(final Context context, final GeckoBundle obj) {
label = obj.getString("name", "");
--- a/mobile/android/base/java/org/mozilla/gecko/prompts/Prompt.java
+++ b/mobile/android/base/java/org/mozilla/gecko/prompts/Prompt.java
@@ -1,37 +1,38 @@
/* -*- 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.mozilla.gecko.R;
-import org.mozilla.gecko.util.GeckoBundle;
-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.support.annotation.NonNull;
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 org.mozilla.gecko.R;
+import org.mozilla.gecko.Tab;
+import org.mozilla.gecko.Tabs;
+import org.mozilla.gecko.util.GeckoBundle;
+import org.mozilla.gecko.util.ThreadUtils;
+
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;
@@ -45,16 +46,21 @@ public class Prompt implements OnClickLi
private PromptListAdapter mAdapter;
private static boolean mInitialized;
private static int mInputPaddingSize;
private int mTabId = Tabs.INVALID_TAB_ID;
private Object mPreviousInputValue = null;
+ private String currentTitle;
+ private String currentText;
+ private PromptListItem[] currentListItems;
+ private int currentChoiceMode;
+
public Prompt(Context context, PromptCallback callback) {
this(context);
mCallback = callback;
}
private Prompt(Context context) {
mContext = context;
mInflater = LayoutInflater.from(mContext);
@@ -120,25 +126,67 @@ public class Prompt implements OnClickLi
return DialogInterface.BUTTON_NEUTRAL;
case 2:
return DialogInterface.BUTTON_NEGATIVE;
default:
return 0;
}
}
- public void show(String title, String text, PromptListItem[] listItems, int choiceMode) {
+ public void show(@NonNull final String title, @NonNull final String text,
+ @NonNull final PromptListItem[] listItems, final int choiceMode) {
+ saveInputDetails(title, text, listItems, choiceMode);
+
+ if (createPrompt(title, text, listItems, choiceMode)) {
+ tryShowingInputPrompt();
+ }
+ }
+
+ public void resetLayout() {
+ if (mDialog != null && mDialog.isShowing()) {
+ mDialog.dismiss();
+
+ final GeckoBundle currentUserInput = new GeckoBundle();
+ addInputValues(currentUserInput);
+
+ for (PromptInput input : mInputs) {
+ input.saveCurrentInput(currentUserInput);
+ }
+
+ if (createPrompt(currentTitle, currentText, currentListItems, currentChoiceMode)) {
+ // If the dialog for this Prompt was shown before it's safe to show again
+ // and avoid the superfluous tryShowingInputPrompt()
+ mDialog.show();
+ }
+ }
+ }
+
+ private void saveInputDetails(@NonNull final String title, @NonNull final String text,
+ @NonNull final PromptListItem[] listItems, final int choiceMode) {
+ currentTitle = title;
+ currentText = text;
+ currentListItems = listItems;
+ currentChoiceMode = choiceMode;
+ }
+
+ private boolean createPrompt(@NonNull final String title, @NonNull final String text,
+ @NonNull final PromptListItem[] listItems, final int choiceMode) {
ThreadUtils.assertOnUiThread();
try {
create(title, text, listItems, choiceMode);
} catch (IllegalStateException ex) {
Log.i(LOGTAG, "Error building dialog", ex);
- return;
+ return false;
}
+ return true;
+ }
+
+ private void tryShowingInputPrompt() {
+ ThreadUtils.assertOnUiThread();
if (mTabId != Tabs.INVALID_TAB_ID) {
Tabs.registerOnTabsChangedListener(this);
final Tab tab = Tabs.getInstance().getTab(mTabId);
if (Tabs.getInstance().getSelectedTab() == tab) {
mDialog.show();
}
--- a/mobile/android/base/java/org/mozilla/gecko/prompts/PromptInput.java
+++ b/mobile/android/base/java/org/mozilla/gecko/prompts/PromptInput.java
@@ -13,16 +13,17 @@ import org.mozilla.gecko.AppConstants;
import org.mozilla.gecko.util.GeckoBundle;
import org.mozilla.gecko.widget.AllCapsTextView;
import org.mozilla.gecko.widget.FocusableDatePicker;
import org.mozilla.gecko.widget.DateTimePicker;
import org.mozilla.gecko.widget.FocusableTimePicker;
import android.content.Context;
import android.content.res.Configuration;
+import android.support.annotation.NonNull;
import android.support.design.widget.TextInputLayout;
import android.text.Html;
import android.text.InputType;
import android.text.TextUtils;
import android.text.format.DateFormat;
import android.util.Log;
import android.view.Gravity;
import android.view.View;
@@ -37,17 +38,17 @@ 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 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);
@@ -112,17 +113,17 @@ public abstract class PromptInput {
@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;
+ return inputLayout;
}
}
public static class PasswordInput extends EditInput {
public static final String INPUT_TYPE = "password";
public PasswordInput(GeckoBundle obj) {
super(obj);
}
@@ -134,17 +135,17 @@ public abstract class PromptInput {
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;
+ private boolean mChecked;
public CheckboxInput(GeckoBundle obj) {
super(obj);
mChecked = obj.getBoolean("checked");
}
@Override
public View getView(Context context) throws UnsupportedOperationException {
@@ -156,16 +157,23 @@ public abstract class PromptInput {
return mView;
}
@Override
public Object getValue() {
CheckBox checkbox = (CheckBox)mView;
return checkbox.isChecked() ? Boolean.TRUE : Boolean.FALSE;
}
+
+ @Override
+ public void saveCurrentInput(@NonNull final GeckoBundle userInput) {
+ if (userInput.containsKey(mId)) {
+ mChecked = (Boolean) userInput.get(mId);
+ }
+ }
}
public static class DateTimeInput extends PromptInput {
public static final String[] INPUT_TYPES = new String[] {
"date",
"week",
"time",
"datetime-local",
@@ -279,17 +287,17 @@ public abstract class PromptInput {
}
return super.getValue();
}
}
public static class MenulistInput extends PromptInput {
public static final String INPUT_TYPE = "menulist";
private final String[] mListitems;
- private final int mSelected;
+ private int mSelected;
public Spinner spinner;
public AllCapsTextView textView;
public MenulistInput(GeckoBundle obj) {
super(obj);
final String[] listitems = obj.getStringArray("values");
mListitems = listitems != null ? listitems : new String[0];
@@ -324,44 +332,62 @@ public abstract class PromptInput {
return spinner;
}
@Override
public Object getValue() {
return spinner.getSelectedItemPosition();
}
+
+ @Override
+ public void saveCurrentInput(@NonNull final GeckoBundle userInput) {
+ if (userInput.containsKey(mId)) {
+ mSelected = (Integer) userInput.get(mId);
+ }
+ }
}
public static class LabelInput extends PromptInput {
public static final String INPUT_TYPE = "label";
public LabelInput(GeckoBundle 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;
}
+
+ @Override
+ public void saveCurrentInput(@NonNull final GeckoBundle userInput) {
+ // No user input to save
+ }
}
public PromptInput(GeckoBundle obj) {
mLabel = obj.getString("label", "");
mType = obj.getString("type", "");
String id = obj.getString("id", "");
mId = TextUtils.isEmpty(id) ? mType : id;
mValue = obj.getString("value", "");
mMaxValue = obj.getString("max", "");
mMinValue = obj.getString("min", "");
}
+ public void saveCurrentInput(@NonNull final GeckoBundle userInput) {
+ if (userInput.containsKey(mId)) {
+ mValue = (String) userInput.get(mId);
+ }
+ }
+
public void putInBundle(final GeckoBundle bundle) {
final String id = getId();
final Object value = getValue();
if (value == null) {
bundle.putBundle(id, null);
} else if (value instanceof Boolean) {
bundle.putBoolean(id, (Boolean) value);
--- a/mobile/android/base/java/org/mozilla/gecko/prompts/PromptService.java
+++ b/mobile/android/base/java/org/mozilla/gecko/prompts/PromptService.java
@@ -4,47 +4,55 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
package org.mozilla.gecko.prompts;
import org.mozilla.gecko.EventDispatcher;
import org.mozilla.gecko.util.BundleEventListener;
import org.mozilla.gecko.util.EventCallback;
import org.mozilla.gecko.util.GeckoBundle;
-import org.mozilla.gecko.util.ThreadUtils;
import android.content.Context;
-import android.util.Log;
public class PromptService implements BundleEventListener {
private static final String LOGTAG = "GeckoPromptService";
- private final Context mContext;
- private final EventDispatcher mDispatcher;
+ private final Context context;
+ private final EventDispatcher dispatcher;
+ private Prompt currentPrompt;
+ private int currentOrientation;
public PromptService(final Context context, final EventDispatcher dispatcher) {
- mContext = context;
- mDispatcher = dispatcher;
- mDispatcher.registerUiThreadListener(this,
+ this.context = context;
+ this.currentOrientation = context.getResources().getConfiguration().orientation;
+ this.dispatcher = dispatcher;
+ this.dispatcher.registerUiThreadListener(this,
"Prompt:Show",
"Prompt:ShowTop");
}
public void destroy() {
- mDispatcher.unregisterUiThreadListener(this,
+ dispatcher.unregisterUiThreadListener(this,
"Prompt:Show",
"Prompt:ShowTop");
}
// BundleEventListener implementation
@Override
public void handleMessage(final String event, final GeckoBundle message,
final EventCallback callback) {
- Prompt p;
- p = new Prompt(mContext, new Prompt.PromptCallback() {
+ currentPrompt = new Prompt(context, new Prompt.PromptCallback() {
@Override
public void onPromptFinished(final GeckoBundle result) {
callback.sendSuccess(result);
+ currentPrompt = null;
}
});
- p.show(message);
+ currentPrompt.show(message);
+ }
+
+ public void changePromptOrientation(int newOrientation) {
+ if (currentPrompt != null && currentOrientation != newOrientation) {
+ currentPrompt.resetLayout();
+ }
+ currentOrientation = newOrientation;
}
}
--- a/mobile/android/base/java/org/mozilla/gecko/prompts/TabInput.java
+++ b/mobile/android/base/java/org/mozilla/gecko/prompts/TabInput.java
@@ -8,16 +8,17 @@ package org.mozilla.gecko.prompts;
import java.util.LinkedHashMap;
import org.mozilla.gecko.AppConstants.Versions;
import org.mozilla.gecko.R;
import org.mozilla.gecko.util.GeckoBundle;
import org.mozilla.gecko.util.ThreadUtils;
import android.content.Context;
+import android.support.annotation.NonNull;
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;
@@ -90,9 +91,13 @@ public class TabInput extends PromptInpu
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
ThreadUtils.assertOnUiThread();
mPosition = position;
notifyListeners(Integer.toString(position));
}
+ @Override
+ public void saveCurrentInput(@NonNull final GeckoBundle userInput) {
+ // No user input to save
+ }
}
--- a/mobile/android/base/java/org/mozilla/gecko/webapps/WebAppActivity.java
+++ b/mobile/android/base/java/org/mozilla/gecko/webapps/WebAppActivity.java
@@ -4,16 +4,17 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
package org.mozilla.gecko.webapps;
import android.annotation.TargetApi;
import android.app.ActivityManager;
import android.content.ActivityNotFoundException;
import android.content.Intent;
+import android.content.res.Configuration;
import android.graphics.Bitmap;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.support.customtabs.CustomTabsIntent;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.view.ActionMode;
import android.util.Log;
@@ -167,16 +168,25 @@ public class WebAppActivity extends AppC
return;
}
updateFromManifest();
mGeckoSession.loadUri(mManifest.getStartUri().toString());
}
+ @Override
+ public void onConfigurationChanged(Configuration newConfig) {
+ super.onConfigurationChanged(newConfig);
+
+ if (mPromptService != null) {
+ mPromptService.changePromptOrientation(newConfig.orientation);
+ }
+ }
+
private void fallbackToFennec(String message) {
if (message != null) {
Toast.makeText(this, message, Toast.LENGTH_LONG).show();
}
try {
Intent intent = new Intent(this, BrowserApp.class);
intent.setAction(Intent.ACTION_VIEW);