From f66637044ead12f3b9dd062c3690f1d26b6aadea Mon Sep 17 00:00:00 2001 From: Manuel Martin Date: Fri, 21 Dec 2018 05:54:49 +0900 Subject: [PATCH] Fixes #435 Added support for alert/prompt/confirm (#905) * Fixes #435 Added support for alert/prompt/confirm * Fixed layout reference that caused a TC failure * Improve resizing glitch --- .../ui/views/settings/DoubleEditSetting.java | 2 +- .../ui/views/settings/SettingsEditText.java | 13 +- .../ui/views/settings/SingleEditSetting.java | 2 +- .../vrbrowser/ui/widgets/TopBarWidget.java | 2 +- .../vrbrowser/ui/widgets/WindowWidget.java | 44 +++- .../ui/widgets/prompts/AlertPromptWidget.java | 71 +++++ .../widgets/prompts/ChoicePromptWidget.java | 244 ++++++------------ .../widgets/prompts/ConfirmPromptWidget.java | 120 +++++++++ .../ui/widgets/prompts/PromptWidget.java | 104 ++++++++ .../ui/widgets/prompts/TextPromptWidget.java | 93 +++++++ app/src/main/res/layout/choice_prompt.xml | 73 ------ app/src/main/res/layout/close_button.xml | 2 +- app/src/main/res/layout/prompt_alert.xml | 66 +++++ app/src/main/res/layout/prompt_choice.xml | 109 ++++++++ ...prompt_item.xml => prompt_choice_item.xml} | 0 app/src/main/res/layout/prompt_confirm.xml | 102 ++++++++ app/src/main/res/layout/prompt_text.xml | 103 ++++++++ app/src/main/res/layout/top_bar.xml | 2 +- app/src/main/res/values/dimen.xml | 8 +- 19 files changed, 901 insertions(+), 259 deletions(-) create mode 100644 app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/prompts/AlertPromptWidget.java create mode 100644 app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/prompts/ConfirmPromptWidget.java create mode 100644 app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/prompts/PromptWidget.java create mode 100644 app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/prompts/TextPromptWidget.java delete mode 100644 app/src/main/res/layout/choice_prompt.xml create mode 100644 app/src/main/res/layout/prompt_alert.xml create mode 100644 app/src/main/res/layout/prompt_choice.xml rename app/src/main/res/layout/{choice_prompt_item.xml => prompt_choice_item.xml} (100%) create mode 100644 app/src/main/res/layout/prompt_confirm.xml create mode 100644 app/src/main/res/layout/prompt_text.xml diff --git a/app/src/common/shared/org/mozilla/vrbrowser/ui/views/settings/DoubleEditSetting.java b/app/src/common/shared/org/mozilla/vrbrowser/ui/views/settings/DoubleEditSetting.java index f61347b68..6bfa8974f 100644 --- a/app/src/common/shared/org/mozilla/vrbrowser/ui/views/settings/DoubleEditSetting.java +++ b/app/src/common/shared/org/mozilla/vrbrowser/ui/views/settings/DoubleEditSetting.java @@ -79,7 +79,7 @@ public void setDefaultSecondValue(String value) { } public String getSecondText() { - return mEdit2.getText().equals(mEdit2.getHint()) ? mDefaultSecondValue : mEdit2.getText().toString(); + return mEdit2.getText().toString().equals(mEdit2.getHint()) ? mDefaultSecondValue : mEdit2.getText().toString(); } public void setSecondText(String text) { diff --git a/app/src/common/shared/org/mozilla/vrbrowser/ui/views/settings/SettingsEditText.java b/app/src/common/shared/org/mozilla/vrbrowser/ui/views/settings/SettingsEditText.java index 8f885e71a..af1baabed 100644 --- a/app/src/common/shared/org/mozilla/vrbrowser/ui/views/settings/SettingsEditText.java +++ b/app/src/common/shared/org/mozilla/vrbrowser/ui/views/settings/SettingsEditText.java @@ -2,6 +2,7 @@ import android.annotation.SuppressLint; import android.content.Context; +import android.content.res.TypedArray; import android.text.Editable; import android.text.TextWatcher; import android.text.style.ForegroundColorSpan; @@ -9,6 +10,8 @@ import android.view.View; import android.widget.EditText; +import org.mozilla.vrbrowser.R; + import androidx.annotation.Nullable; @SuppressLint("AppCompatCustomView") @@ -18,17 +21,19 @@ public class SettingsEditText extends EditText { private int mHighlightedTextColor = 0; public SettingsEditText(Context context) { - super(context); - initialize(); + this(context, null); } public SettingsEditText(Context context, @Nullable AttributeSet attrs) { - super(context, attrs); - initialize(); + this(context, attrs, 0); } public SettingsEditText(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); + + TypedArray attributes = context.obtainStyledAttributes(attrs, R.styleable.EditSetting, defStyleAttr, 0); + mHighlightedTextColor = attributes.getColor(R.styleable.EditSetting_highlightedTextColor, 0); + initialize(); } diff --git a/app/src/common/shared/org/mozilla/vrbrowser/ui/views/settings/SingleEditSetting.java b/app/src/common/shared/org/mozilla/vrbrowser/ui/views/settings/SingleEditSetting.java index 658b9c1b3..7272eb9b7 100644 --- a/app/src/common/shared/org/mozilla/vrbrowser/ui/views/settings/SingleEditSetting.java +++ b/app/src/common/shared/org/mozilla/vrbrowser/ui/views/settings/SingleEditSetting.java @@ -126,7 +126,7 @@ public void setDefaultFirstValue(String value) { } public String getFirstText() { - return mEdit1.getText().equals(mEdit1.getHint()) ? mDefaultFirstValue : mEdit1.getText().toString(); + return mEdit1.getText().toString().equals(mEdit1.getHint()) ? mDefaultFirstValue : mEdit1.getText().toString(); } public void setFirstText(String text) { diff --git a/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/TopBarWidget.java b/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/TopBarWidget.java index 1bda459ce..13b0a016b 100644 --- a/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/TopBarWidget.java +++ b/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/TopBarWidget.java @@ -38,7 +38,7 @@ public TopBarWidget(Context aContext, AttributeSet aAttrs, int aDefStyle) { private void initialize(Context aContext) { inflate(aContext, R.layout.top_bar, this); - mCloseButton = findViewById(R.id.closeButton); + mCloseButton = findViewById(R.id.negativeButton); mCloseButton.setOnClickListener(view -> { view.requestFocusFromTouch(); if (mAudio != null) { diff --git a/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/WindowWidget.java b/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/WindowWidget.java index b5df83fc3..7b96796c8 100644 --- a/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/WindowWidget.java +++ b/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/WindowWidget.java @@ -26,7 +26,10 @@ import org.mozilla.vrbrowser.browser.SessionStore; import org.mozilla.vrbrowser.browser.SettingsStore; import org.mozilla.vrbrowser.ui.views.BookmarksView; +import org.mozilla.vrbrowser.ui.widgets.prompts.AlertPromptWidget; import org.mozilla.vrbrowser.ui.widgets.prompts.ChoicePromptWidget; +import org.mozilla.vrbrowser.ui.widgets.prompts.ConfirmPromptWidget; +import org.mozilla.vrbrowser.ui.widgets.prompts.TextPromptWidget; public class WindowWidget extends UIWidget implements SessionStore.SessionChangeListener, @@ -43,6 +46,9 @@ public class WindowWidget extends UIWidget implements SessionStore.SessionChange private WidgetPlacement mWidgetPlacement; private WidgetManagerDelegate mWidgetManager; private ChoicePromptWidget mChoicePrompt; + private AlertPromptWidget mAlertPrompt; + private ConfirmPromptWidget mConfirmPrompt; + private TextPromptWidget mTextPrompt; private int mWidthBackup; private int mHeightBackup; private int mBorderWidth; @@ -548,19 +554,38 @@ private void setPrivateBrowsingEnabled(boolean isEnabled) { // TODO: Fade in/out the browser window. Waiting for https://github.com/MozillaReality/FirefoxReality/issues/77 } + // PromptDelegate + @Override public void onAlert(GeckoSession session, String title, String msg, AlertCallback callback) { - + mAlertPrompt = new AlertPromptWidget(getContext()); + mAlertPrompt.mWidgetPlacement.parentHandle = getHandle(); + mAlertPrompt.setTitle(title); + mAlertPrompt.setMessage(msg); + mAlertPrompt.setDelegate(callback); + mAlertPrompt.show(); } @Override public void onButtonPrompt(GeckoSession session, String title, String msg, String[] btnMsg, ButtonCallback callback) { - + mConfirmPrompt = new ConfirmPromptWidget(getContext()); + mConfirmPrompt.mWidgetPlacement.parentHandle = getHandle(); + mConfirmPrompt.setTitle(title); + mConfirmPrompt.setMessage(msg); + mConfirmPrompt.setButtons(btnMsg); + mConfirmPrompt.setDelegate(callback); + mConfirmPrompt.show(); } @Override public void onTextPrompt(GeckoSession session, String title, String msg, String value, TextCallback callback) { - + mTextPrompt = new TextPromptWidget(getContext()); + mTextPrompt.mWidgetPlacement.parentHandle = getHandle(); + mTextPrompt.setTitle(title); + mTextPrompt.setMessage(msg); + mTextPrompt.setDefaultText(value); + mTextPrompt.setDelegate(callback); + mTextPrompt.show(); } @Override @@ -570,20 +595,13 @@ public void onAuthPrompt(GeckoSession session, String title, String msg, AuthOpt @Override public void onChoicePrompt(GeckoSession session, String title, String msg, int type, final Choice[] choices, final ChoiceCallback callback) { - if (mChoicePrompt == null) { - mChoicePrompt = new ChoicePromptWidget(getContext()); - mChoicePrompt.mWidgetPlacement.parentHandle = getHandle(); - } - + mChoicePrompt = new ChoicePromptWidget(getContext()); + mChoicePrompt.mWidgetPlacement.parentHandle = getHandle(); mChoicePrompt.setTitle(title); mChoicePrompt.setMessage(msg); mChoicePrompt.setChoices(choices); mChoicePrompt.setMenuType(type); - mChoicePrompt.setDelegate(ids -> { - callback.confirm(ids); - mChoicePrompt.hide(UIWidget.REMOVE_WIDGET); - }); - + mChoicePrompt.setDelegate(callback); mChoicePrompt.show(); } diff --git a/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/prompts/AlertPromptWidget.java b/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/prompts/AlertPromptWidget.java new file mode 100644 index 000000000..60e82f6a8 --- /dev/null +++ b/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/prompts/AlertPromptWidget.java @@ -0,0 +1,71 @@ +package org.mozilla.vrbrowser.ui.widgets.prompts; + +import android.content.Context; +import android.text.method.ScrollingMovementMethod; +import android.util.AttributeSet; +import android.widget.Button; + +import org.mozilla.geckoview.GeckoSession; +import org.mozilla.vrbrowser.R; +import org.mozilla.vrbrowser.audio.AudioEngine; + +public class AlertPromptWidget extends PromptWidget { + + private AudioEngine mAudio; + private Button mOkButton; + private GeckoSession.PromptDelegate.AlertCallback mCallback; + + public AlertPromptWidget(Context aContext) { + super(aContext); + initialize(aContext); + } + + public AlertPromptWidget(Context aContext, AttributeSet aAttrs) { + super(aContext, aAttrs); + initialize(aContext); + } + + public AlertPromptWidget(Context aContext, AttributeSet aAttrs, int aDefStyle) { + super(aContext, aAttrs, aDefStyle); + initialize(aContext); + } + + @Override + protected void initialize(Context aContext) { + super.initialize(aContext); + + inflate(aContext, R.layout.prompt_alert, this); + + mAudio = AudioEngine.fromContext(aContext); + + mLayout = findViewById(R.id.layout); + + mTitle = findViewById(R.id.alertTitle); + mMessage = findViewById(R.id.alertMessage); + mMessage.setMovementMethod(new ScrollingMovementMethod()); + + mOkButton = findViewById(R.id.positiveButton); + mOkButton.setSoundEffectsEnabled(false); + mOkButton.setOnClickListener(view -> { + if (mAudio != null) { + mAudio.playSound(AudioEngine.Sound.CLICK); + } + + onDismiss(); + }); + } + + @Override + protected void onDismiss() { + hide(REMOVE_WIDGET); + + if (mCallback != null) { + mCallback.dismiss(); + } + } + + public void setDelegate(GeckoSession.PromptDelegate.AlertCallback delegate) { + mCallback = delegate; + } + +} diff --git a/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/prompts/ChoicePromptWidget.java b/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/prompts/ChoicePromptWidget.java index f62ee4a1b..f1b3352ef 100644 --- a/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/prompts/ChoicePromptWidget.java +++ b/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/prompts/ChoicePromptWidget.java @@ -9,7 +9,6 @@ import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup; -import android.widget.AdapterView; import android.widget.ArrayAdapter; import android.widget.Button; import android.widget.LinearLayout; @@ -17,34 +16,26 @@ import android.widget.RadioButton; import android.widget.TextView; +import org.mozilla.geckoview.GeckoSession; import org.mozilla.geckoview.GeckoSession.PromptDelegate.Choice; import org.mozilla.vrbrowser.R; -import org.mozilla.vrbrowser.ui.widgets.WidgetManagerDelegate; -import org.mozilla.vrbrowser.ui.widgets.WidgetPlacement; import org.mozilla.vrbrowser.audio.AudioEngine; -import org.mozilla.vrbrowser.ui.widgets.UIWidget; import java.util.ArrayList; import java.util.Arrays; import androidx.annotation.NonNull; -public class ChoicePromptWidget extends UIWidget implements WidgetManagerDelegate.FocusChangeListener { +public class ChoicePromptWidget extends PromptWidget { private static final int DIALOG_CLOSE_DELAY = 250; - public interface ChoicePromptDelegate { - void onDismissed(String[] text); - } - private AudioEngine mAudio; private ListView mList; private Button mCloseButton; private Button mOkButton; - private TextView mPromptTitle; - private TextView mPromptMessage; private ChoiceWrapper[] mListItems; - private ChoicePromptDelegate mPromptDelegate; + private GeckoSession.PromptDelegate.ChoiceCallback mCallback; private ChoiceAdapter mAdapter; private final Handler handler = new Handler(); @@ -63,120 +54,96 @@ public ChoicePromptWidget(Context aContext, AttributeSet aAttrs, int aDefStyle) initialize(aContext); } - private void initialize(Context aContext) { - inflate(aContext, R.layout.choice_prompt, this); + @Override + protected void initialize(Context aContext) { + super.initialize(aContext); + + inflate(aContext, R.layout.prompt_choice, this); mWidgetManager.addFocusChangeListener(this); mAudio = AudioEngine.fromContext(aContext); + mLayout = findViewById(R.id.layout); + mList = findViewById(R.id.choiceslist); mList.setSoundEffectsEnabled(false); - mList.setOnItemClickListener(new AdapterView.OnItemClickListener() - { - public void onItemClick(AdapterView parent, View view, final int position, long id) - { - if (mAudio != null) { - mAudio.playSound(AudioEngine.Sound.CLICK); - } + mList.setOnItemClickListener((parent, view, position, id) -> { + if (mAudio != null) { + mAudio.playSound(AudioEngine.Sound.CLICK); + } - mAdapter.notifyDataSetChanged(); + mAdapter.notifyDataSetChanged(); - handler.postDelayed(new Runnable() { - @Override - public void run() { - ChoiceWrapper selectedItem = mListItems[position]; - if (mList.getChoiceMode() == ListView.CHOICE_MODE_SINGLE) { - if (mPromptDelegate != null) { - mPromptDelegate.onDismissed(new String[]{selectedItem.getChoice().id}); - } - } + handler.postDelayed(() -> { + ChoiceWrapper selectedItem = mListItems[position]; + if (mList.getChoiceMode() == ListView.CHOICE_MODE_SINGLE) { + if (mCallback != null) { + mCallback.confirm(new String[]{selectedItem.getChoice().id}); + hide(REMOVE_WIDGET); } - }, DIALOG_CLOSE_DELAY); - } + } + }, DIALOG_CLOSE_DELAY); }); - mPromptTitle = findViewById(R.id.promptTitle); - mPromptMessage = findViewById(R.id.promptMessage); + mTitle = findViewById(R.id.promptTitle); + mMessage = findViewById(R.id.promptMessage); - mCloseButton = findViewById(R.id.closeButton); + mCloseButton = findViewById(R.id.negativeButton); mCloseButton.setSoundEffectsEnabled(false); - mCloseButton.setOnClickListener(new OnClickListener() { - @Override - public void onClick(View view) { - if (mAudio != null) { - mAudio.playSound(AudioEngine.Sound.CLICK); - } + mCloseButton.setOnClickListener(view -> { + if (mAudio != null) { + mAudio.playSound(AudioEngine.Sound.CLICK); + } - switch (mList.getChoiceMode()) { - case ListView.CHOICE_MODE_SINGLE: - case ListView.CHOICE_MODE_MULTIPLE: { - if (mPromptDelegate != null) { - mPromptDelegate.onDismissed(getDefaultChoices(mListItems)); - } + switch (mList.getChoiceMode()) { + case ListView.CHOICE_MODE_SINGLE: + case ListView.CHOICE_MODE_MULTIPLE: { + if (mCallback != null) { + onDismiss(); } - break; } + break; } }); - mOkButton = findViewById(R.id.okButton); + mOkButton = findViewById(R.id.positiveButton); mOkButton.setSoundEffectsEnabled(false); - mOkButton.setOnClickListener(new OnClickListener() { - @Override - public void onClick(View view) { - if (mAudio != null) { - mAudio.playSound(AudioEngine.Sound.CLICK); - } + mOkButton.setOnClickListener(view -> { + if (mAudio != null) { + mAudio.playSound(AudioEngine.Sound.CLICK); + } - switch (mList.getChoiceMode()) { - case ListView.CHOICE_MODE_SINGLE: - case ListView.CHOICE_MODE_MULTIPLE: { - if (mPromptDelegate != null) { - int len = mList.getCount(); - SparseBooleanArray selected = mList.getCheckedItemPositions(); - ArrayList selectedChoices = new ArrayList<>(); - for (int i = 0; i < len; i++) { - if (selected.get(i)) { - selectedChoices.add(mListItems[i].getChoice().id); - } + switch (mList.getChoiceMode()) { + case ListView.CHOICE_MODE_SINGLE: + case ListView.CHOICE_MODE_MULTIPLE: { + if (mCallback != null) { + int len = mList.getCount(); + SparseBooleanArray selected = mList.getCheckedItemPositions(); + ArrayList selectedChoices = new ArrayList<>(); + for (int i = 0; i < len; i++) { + if (selected.get(i)) { + selectedChoices.add(mListItems[i].getChoice().id); } - mPromptDelegate.onDismissed(selectedChoices.toArray(new String[selectedChoices.size()])); } + mCallback.confirm(selectedChoices.toArray(new String[selectedChoices.size()])); } - break; } + break; } + + hide(REMOVE_WIDGET); }); mListItems = new ChoiceWrapper[]{}; } - @Override - protected void initializeWidgetPlacement(WidgetPlacement aPlacement) { - aPlacement.visible = false; - aPlacement.width = WidgetPlacement.dpDimension(getContext(), R.dimen.choice_prompt_width); - aPlacement.height = WidgetPlacement.dpDimension(getContext(), R.dimen.choice_prompt_height); - aPlacement.parentAnchorX = 0.5f; - aPlacement.parentAnchorY = 0.5f; - aPlacement.anchorX = 0.5f; - aPlacement.anchorY = 0.5f; - aPlacement.translationZ = WidgetPlacement.unitFromMeters(getContext(), R.dimen.browser_children_z_distance); - } - - @Override - public void releaseWidget() { - mWidgetManager.removeFocusChangeListener(this); - - super.releaseWidget(); - } - @Override protected void onDismiss() { hide(REMOVE_WIDGET); - if (mPromptDelegate != null) { - mPromptDelegate.onDismissed(getDefaultChoices(mListItems)); + if (mCallback != null) { + mCallback.dismiss(); } } @@ -190,34 +157,16 @@ public void show() { mAdapter.notifyDataSetChanged(); } - public void setDelegate(ChoicePromptDelegate delegate) { - mPromptDelegate = delegate; + public void setDelegate(GeckoSession.PromptDelegate.ChoiceCallback delegate) { + mCallback = delegate; } public void setChoices(Choice[] choices) { mListItems = getWrappedChoices(choices); - mAdapter = new ChoiceAdapter(getContext(), R.layout.choice_prompt_item, mListItems); + mAdapter = new ChoiceAdapter(getContext(), R.layout.prompt_choice_item, mListItems); mList.setAdapter(mAdapter); } - public void setTitle(String title) { - if (title == null || title.isEmpty()) { - mPromptTitle.setVisibility(View.GONE); - - } else { - mPromptTitle.setText(title); - } - } - - public void setMessage(String message) { - if (message == null || message.isEmpty()) { - mPromptMessage.setVisibility(View.GONE); - - } else { - mPromptMessage.setText(message); - } - } - public void setMenuType(int type) { switch (type) { case Choice.CHOICE_TYPE_SINGLE: @@ -255,18 +204,6 @@ private static ChoiceWrapper[] getWrappedChoices(Choice[] aChoices, int aLevel) return flattenedChoicesList.toArray(new ChoiceWrapper[flattenedChoicesList.size()]); } - @NonNull - private static String[] getDefaultChoices(ChoiceWrapper[] aChoices) { - ArrayList defaultChoices = new ArrayList<>(); - for (int i = 0; i < aChoices.length; i++) { - if (aChoices[i].getChoice().selected) { - defaultChoices.add(aChoices[i].getChoice().id); - } - } - - return defaultChoices.toArray(new String[defaultChoices.size()]); - } - static class ChoiceWrapper { private int mLevel; @@ -323,7 +260,7 @@ public View getView(int position, View convertView, ViewGroup parent) { ChoiceViewHolder choiceViewHolder; if(listItem == null) { - listItem = mInflater.inflate(R.layout.choice_prompt_item, parent, false); + listItem = mInflater.inflate(R.layout.prompt_choice_item, parent, false); choiceViewHolder = new ChoiceViewHolder(); @@ -367,46 +304,33 @@ public View getView(int position, View convertView, ViewGroup parent) { return listItem; } - private OnHoverListener mHoverListener = new OnHoverListener() { - @Override - public boolean onHover(View view, MotionEvent motionEvent) { - int position = (int)view.getTag(R.string.position_tag); - if (!isEnabled(position)) - return false; - - TextView label = view.findViewById(R.id.optionLabel); - RadioButton check = view.findViewById(R.id.radioOption); - int ev = motionEvent.getActionMasked(); - switch (ev) { - case MotionEvent.ACTION_HOVER_ENTER: - view.setHovered(true); - label.setHovered(true); - check.setHovered(true); - view.setBackgroundResource(R.drawable.prompt_item_selected); - return true; - - case MotionEvent.ACTION_HOVER_EXIT: - view.setHovered(false); - label.setHovered(false); - check.setHovered(false); - view.setBackgroundColor(getContext().getColor(R.color.void_color)); - return true; - } - + private OnHoverListener mHoverListener = (view, motionEvent) -> { + int position = (int)view.getTag(R.string.position_tag); + if (!isEnabled(position)) return false; + + TextView label = view.findViewById(R.id.optionLabel); + RadioButton check = view.findViewById(R.id.radioOption); + int ev = motionEvent.getActionMasked(); + switch (ev) { + case MotionEvent.ACTION_HOVER_ENTER: + view.setHovered(true); + label.setHovered(true); + check.setHovered(true); + view.setBackgroundResource(R.drawable.prompt_item_selected); + return true; + + case MotionEvent.ACTION_HOVER_EXIT: + view.setHovered(false); + label.setHovered(false); + check.setHovered(false); + view.setBackgroundColor(getContext().getColor(R.color.void_color)); + return true; } - }; - } + return false; + }; - // WidgetManagerDelegate.FocusChangeListener - @Override - public void onGlobalFocusChanged(View oldFocus, View newFocus) { - if (oldFocus == this && isVisible()) { - if (mPromptDelegate != null) { - mPromptDelegate.onDismissed(getDefaultChoices(mListItems)); - } - } } } diff --git a/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/prompts/ConfirmPromptWidget.java b/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/prompts/ConfirmPromptWidget.java new file mode 100644 index 000000000..3cec9e050 --- /dev/null +++ b/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/prompts/ConfirmPromptWidget.java @@ -0,0 +1,120 @@ +package org.mozilla.vrbrowser.ui.widgets.prompts; + +import android.content.Context; +import android.text.method.ScrollingMovementMethod; +import android.util.AttributeSet; +import android.view.View; +import android.widget.Button; + +import org.mozilla.geckoview.GeckoSession; +import org.mozilla.vrbrowser.R; +import org.mozilla.vrbrowser.audio.AudioEngine; + +public class ConfirmPromptWidget extends PromptWidget { + + private static final int POSITIVE = 0; + private static final int NEUTRAL = 1; + private static final int NEGATIVE = 2; + + private AudioEngine mAudio; + private Button[] mButtons; + private GeckoSession.PromptDelegate.ButtonCallback mCallback; + + public ConfirmPromptWidget(Context aContext) { + super(aContext); + initialize(aContext); + } + + public ConfirmPromptWidget(Context aContext, AttributeSet aAttrs) { + super(aContext, aAttrs); + initialize(aContext); + } + + public ConfirmPromptWidget(Context aContext, AttributeSet aAttrs, int aDefStyle) { + super(aContext, aAttrs, aDefStyle); + initialize(aContext); + } + + @Override + protected void initialize(Context aContext) { + super.initialize(aContext); + + inflate(aContext, R.layout.prompt_confirm, this); + + mWidgetManager.addFocusChangeListener(this); + + mAudio = AudioEngine.fromContext(aContext); + + mLayout = findViewById(R.id.layout); + + mTitle = findViewById(R.id.confirmTitle); + mMessage = findViewById(R.id.confirmMessage); + mMessage.setMovementMethod(new ScrollingMovementMethod()); + + mButtons = new Button[3]; + + mButtons[POSITIVE] = findViewById(R.id.positiveButton); + mButtons[POSITIVE].setSoundEffectsEnabled(false); + mButtons[POSITIVE].setOnClickListener(mButtonClickListener); + mButtons[POSITIVE].setVisibility(GONE); + + mButtons[NEUTRAL] = findViewById(R.id.neutralButton); + mButtons[NEUTRAL].setSoundEffectsEnabled(false); + mButtons[NEUTRAL].setOnClickListener(mButtonClickListener); + mButtons[NEUTRAL].setVisibility(GONE); + + mButtons[NEGATIVE] = findViewById(R.id.negativeButton); + mButtons[NEGATIVE].setSoundEffectsEnabled(false); + mButtons[NEGATIVE].setOnClickListener(mButtonClickListener); + mButtons[NEGATIVE].setVisibility(GONE); + } + + private OnClickListener mButtonClickListener = new OnClickListener() { + @Override + public void onClick(View view) { + if (mAudio != null) { + mAudio.playSound(AudioEngine.Sound.CLICK); + } + + if (mCallback != null) { + mCallback.confirm((int)view.getTag()); + } + + hide(REMOVE_WIDGET); + } + }; + + @Override + protected void onDismiss() { + hide(REMOVE_WIDGET); + + if (mCallback != null) { + mCallback.dismiss(); + } + } + + public void setDelegate(GeckoSession.PromptDelegate.ButtonCallback delegate) { + mCallback = delegate; + } + + public void setButtons(String[] btnMsg) { + // NOTE: For some reason Gecko handles positive and negative internally reversed. + // Returning 0 should be Ok but is in fact Cancel. + if (btnMsg[POSITIVE] != null) { + mButtons[POSITIVE].setText(btnMsg[POSITIVE]); + mButtons[POSITIVE].setTag(NEGATIVE); + mButtons[POSITIVE].setVisibility(VISIBLE); + } + if (btnMsg[NEUTRAL] != null) { + mButtons[NEUTRAL].setText(btnMsg[NEUTRAL]); + mButtons[NEUTRAL].setTag(NEUTRAL); + mButtons[NEUTRAL].setVisibility(VISIBLE); + } + if (btnMsg[NEGATIVE] != null) { + mButtons[NEGATIVE].setText(btnMsg[NEGATIVE]); + mButtons[NEGATIVE].setTag(POSITIVE); + mButtons[NEGATIVE].setVisibility(VISIBLE); + } + } + +} diff --git a/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/prompts/PromptWidget.java b/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/prompts/PromptWidget.java new file mode 100644 index 000000000..656ec8e97 --- /dev/null +++ b/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/prompts/PromptWidget.java @@ -0,0 +1,104 @@ +package org.mozilla.vrbrowser.ui.widgets.prompts; + +import android.content.Context; +import android.util.AttributeSet; +import android.view.View; +import android.view.ViewGroup; +import android.view.ViewTreeObserver; +import android.widget.TextView; + +import org.mozilla.vrbrowser.R; +import org.mozilla.vrbrowser.ui.widgets.UIWidget; +import org.mozilla.vrbrowser.ui.widgets.WidgetManagerDelegate; +import org.mozilla.vrbrowser.ui.widgets.WidgetPlacement; + +public class PromptWidget extends UIWidget implements WidgetManagerDelegate.FocusChangeListener { + + protected TextView mTitle; + protected TextView mMessage; + protected ViewGroup mLayout; + private int mMaxHeight; + + public PromptWidget(Context aContext) { + super(aContext); + } + + public PromptWidget(Context aContext, AttributeSet aAttrs) { + super(aContext, aAttrs); + } + + public PromptWidget(Context aContext, AttributeSet aAttrs, int aDefStyle) { + super(aContext, aAttrs, aDefStyle); + } + + protected void initialize(Context aContext) { + mWidgetManager.addFocusChangeListener(this); + } + + public void setTitle(String title) { + if (title == null || title.isEmpty()) { + mTitle.setVisibility(View.GONE); + + } else { + mTitle.setText(title); + } + } + + public void setMessage(String message) { + if (message == null || message.isEmpty()) { + mMessage.setVisibility(View.GONE); + + } else { + mMessage.setText(message); + } + } + + @Override + protected void initializeWidgetPlacement(WidgetPlacement aPlacement) { + aPlacement.visible = false; + aPlacement.width = WidgetPlacement.pixelDimension(getContext(), R.dimen.browser_width_pixels)/2; + mMaxHeight = WidgetPlacement.dpDimension(getContext(), R.dimen.prompt_height); + aPlacement.height = mMaxHeight; + aPlacement.parentAnchorX = 0.5f; + aPlacement.parentAnchorY = 0.5f; + aPlacement.anchorX = 0.5f; + aPlacement.anchorY = 0.5f; + aPlacement.translationZ = WidgetPlacement.unitFromMeters(getContext(), R.dimen.browser_children_z_distance); + } + + @Override + public void releaseWidget() { + mWidgetManager.removeFocusChangeListener(this); + + super.releaseWidget(); + } + + @Override + public void show() { + mLayout.measure(View.MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED), + View.MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED)); + mWidgetPlacement.height = (int)(mLayout.getMeasuredHeight()/mWidgetPlacement.density); + super.show(); + + ViewTreeObserver viewTreeObserver = mLayout.getViewTreeObserver(); + if (viewTreeObserver.isAlive()) { + viewTreeObserver.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { + @Override + public void onGlobalLayout() { + mLayout.getViewTreeObserver().removeOnGlobalLayoutListener(this); + mWidgetPlacement.height = (int)(mLayout.getHeight()/mWidgetPlacement.density); + mWidgetManager.updateWidget(PromptWidget.this); + } + }); + } + } + + // WidgetManagerDelegate.FocusChangeListener + @Override + public void onGlobalFocusChanged(View oldFocus, View newFocus) { + if (oldFocus == this && isVisible() && findViewById(newFocus.getId()) == null) { + onDismiss(); + } + } + +} diff --git a/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/prompts/TextPromptWidget.java b/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/prompts/TextPromptWidget.java new file mode 100644 index 000000000..07a52e172 --- /dev/null +++ b/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/prompts/TextPromptWidget.java @@ -0,0 +1,93 @@ +package org.mozilla.vrbrowser.ui.widgets.prompts; + +import android.content.Context; +import android.text.method.ScrollingMovementMethod; +import android.util.AttributeSet; +import android.widget.Button; + +import org.mozilla.geckoview.GeckoSession; +import org.mozilla.vrbrowser.R; +import org.mozilla.vrbrowser.audio.AudioEngine; +import org.mozilla.vrbrowser.ui.views.settings.SettingsEditText; + +public class TextPromptWidget extends PromptWidget { + + private AudioEngine mAudio; + private SettingsEditText mPromptText; + private Button mOkButton; + private Button mCancelButton; + private GeckoSession.PromptDelegate.TextCallback mCallback; + + public TextPromptWidget(Context aContext) { + super(aContext); + initialize(aContext); + } + + public TextPromptWidget(Context aContext, AttributeSet aAttrs) { + super(aContext, aAttrs); + initialize(aContext); + } + + public TextPromptWidget(Context aContext, AttributeSet aAttrs, int aDefStyle) { + super(aContext, aAttrs, aDefStyle); + initialize(aContext); + } + + @Override + protected void initialize(Context aContext) { + super.initialize(aContext); + + inflate(aContext, R.layout.prompt_text, this); + + mAudio = AudioEngine.fromContext(aContext); + + mLayout = findViewById(R.id.layout); + + mTitle = findViewById(R.id.textTitle); + mMessage = findViewById(R.id.textMessage); + mMessage.setMovementMethod(new ScrollingMovementMethod()); + mPromptText = findViewById(R.id.promptText); + + mOkButton = findViewById(R.id.positiveButton); + mOkButton.setSoundEffectsEnabled(false); + mOkButton.setOnClickListener(view -> { + if (mAudio != null) { + mAudio.playSound(AudioEngine.Sound.CLICK); + } + + if (mCallback != null) { + mCallback.confirm(mPromptText.getText().toString()); + } + + hide(REMOVE_WIDGET); + }); + + mCancelButton = findViewById(R.id.negativeButton); + mCancelButton.setSoundEffectsEnabled(false); + mCancelButton.setOnClickListener(view -> { + if (mAudio != null) { + mAudio.playSound(AudioEngine.Sound.CLICK); + } + + onDismiss(); + }); + } + + @Override + protected void onDismiss() { + hide(REMOVE_WIDGET); + + if (mCallback != null) { + mCallback.dismiss(); + } + } + + public void setDelegate(GeckoSession.PromptDelegate.TextCallback delegate) { + mCallback = delegate; + } + + public void setDefaultText(String text) { + mPromptText.setText(text); + } + +} diff --git a/app/src/main/res/layout/choice_prompt.xml b/app/src/main/res/layout/choice_prompt.xml deleted file mode 100644 index f2a8b4aaf..000000000 --- a/app/src/main/res/layout/choice_prompt.xml +++ /dev/null @@ -1,73 +0,0 @@ - - - - - -