From 0ad0545819c53a8ae35e9c7f69d81cbef6224997 Mon Sep 17 00:00:00 2001 From: Manuel Martin Date: Wed, 10 Oct 2018 10:23:06 +0200 Subject: [PATCH] Improved focus handling (#608) * Improved focus handling * Added focus change events when clicked outside a widget * Check if motion event is in pressed state * Hanlding of clicking on the env. Better handling of focus in listview. --- .../vrbrowser/MotionEventGenerator.java | 9 ++- .../mozilla/vrbrowser/VRBrowserActivity.java | 51 ++++++++++---- .../vrbrowser/WidgetManagerDelegate.java | 32 +++++---- .../mozilla/vrbrowser/ui/BrowserWidget.java | 5 +- .../vrbrowser/ui/DeveloperOptionsWidget.java | 4 +- .../mozilla/vrbrowser/ui/KeyboardWidget.java | 15 +++- .../vrbrowser/ui/NavigationBarWidget.java | 18 +++-- .../vrbrowser/ui/NavigationURLBar.java | 1 - .../vrbrowser/ui/PermissionWidget.java | 40 ++++++----- .../org/mozilla/vrbrowser/ui/RootWidget.java | 43 ++++++++++++ .../mozilla/vrbrowser/ui/SettingsButton.java | 1 - .../mozilla/vrbrowser/ui/SettingsWidget.java | 20 +++--- .../mozilla/vrbrowser/ui/TopBarWidget.java | 9 +-- .../org/mozilla/vrbrowser/ui/TrayWidget.java | 38 +++++++--- .../org/mozilla/vrbrowser/ui/UIButton.java | 2 + .../org/mozilla/vrbrowser/ui/UIWidget.java | 37 +++++++--- .../vrbrowser/ui/VoiceSearchWidget.java | 15 +++- .../ui/prompts/ChoicePromptWidget.java | 69 ++++++------------- app/src/main/cpp/BrowserWorld.cpp | 14 ++-- 19 files changed, 277 insertions(+), 146 deletions(-) create mode 100644 app/src/common/shared/org/mozilla/vrbrowser/ui/RootWidget.java diff --git a/app/src/common/shared/org/mozilla/vrbrowser/MotionEventGenerator.java b/app/src/common/shared/org/mozilla/vrbrowser/MotionEventGenerator.java index 555822d03..a7bdc4489 100644 --- a/app/src/common/shared/org/mozilla/vrbrowser/MotionEventGenerator.java +++ b/app/src/common/shared/org/mozilla/vrbrowser/MotionEventGenerator.java @@ -16,6 +16,7 @@ class MotionEventGenerator { static class Device { int mDevice; Widget mPreviousWidget = null; + Widget mTouchStartWidget = null; boolean mWasPressed; long mDownTime; MotionEvent.PointerProperties mProperties[]; @@ -78,18 +79,19 @@ static void dispatch(Widget aWidget, int aDevice, boolean aPressed, float aX, fl device.mCoords[0].pressure = 0.0f; } } - if ((device.mPreviousWidget != null) && (device.mPreviousWidget != aWidget)) { + if (!aPressed && (device.mPreviousWidget != null) && (device.mPreviousWidget != aWidget)) { if (device.mWasPressed) { generateEvent(device.mPreviousWidget, device, MotionEvent.ACTION_CANCEL, false); device.mWasPressed = false; } generateEvent(device.mPreviousWidget, device, MotionEvent.ACTION_HOVER_EXIT, true); + device.mPreviousWidget = null; } if (aWidget == null) { device.mPreviousWidget = null; return; } - if (aWidget != device.mPreviousWidget) { + if (aWidget != device.mPreviousWidget && !aPressed) { generateEvent(aWidget, device, MotionEvent.ACTION_HOVER_ENTER, true); } if (aPressed && !device.mWasPressed) { @@ -97,9 +99,10 @@ static void dispatch(Widget aWidget, int aDevice, boolean aPressed, float aX, fl device.mWasPressed = true; generateEvent(aWidget, device, MotionEvent.ACTION_HOVER_EXIT, true); generateEvent(aWidget, device, MotionEvent.ACTION_DOWN, false); + device.mTouchStartWidget = aWidget; } else if (!aPressed && device.mWasPressed) { device.mWasPressed = false; - generateEvent(aWidget, device, MotionEvent.ACTION_UP, false); + generateEvent(device.mTouchStartWidget, device, MotionEvent.ACTION_UP, false); generateEvent(aWidget, device, MotionEvent.ACTION_HOVER_ENTER, true); } else if (moving && aPressed) { generateEvent(aWidget, device, MotionEvent.ACTION_MOVE, false); diff --git a/app/src/common/shared/org/mozilla/vrbrowser/VRBrowserActivity.java b/app/src/common/shared/org/mozilla/vrbrowser/VRBrowserActivity.java index c2511a3b9..a1f71e8c7 100644 --- a/app/src/common/shared/org/mozilla/vrbrowser/VRBrowserActivity.java +++ b/app/src/common/shared/org/mozilla/vrbrowser/VRBrowserActivity.java @@ -19,6 +19,7 @@ import android.support.annotation.Keep; import android.util.Log; import android.view.KeyEvent; +import android.view.MotionEvent; import android.view.View; import android.view.ViewTreeObserver; import android.widget.FrameLayout; @@ -81,14 +82,16 @@ public void run() { Handler mHandler = new Handler(); Runnable mAudioUpdateRunnable; BrowserWidget mBrowserWidget; + RootWidget mRootWidget; KeyboardWidget mKeyboard; NavigationBarWidget mNavigationBar; CrashDialogWidget mCrashDialog; TopBarWidget mTopBar; TrayWidget mTray; PermissionDelegate mPermissionDelegate; - LinkedList mWidgetEventListeners; - LinkedList mPermissionListeners; + LinkedList mWidgetUpdateListeners; + LinkedList mPermissionListeners; + LinkedList mFocusChangeListeners; LinkedList mBackHandlers; private boolean mIsPresentingImmersive = false; private Thread mUiThread; @@ -114,8 +117,9 @@ protected void onCreate(Bundle savedInstanceState) { mLastGesture = NoGesture; super.onCreate(savedInstanceState); - mWidgetEventListeners = new LinkedList<>(); + mWidgetUpdateListeners = new LinkedList<>(); mPermissionListeners = new LinkedList<>(); + mFocusChangeListeners = new LinkedList<>(); mBackHandlers = new LinkedList<>(); mWidgets = new HashMap<>(); @@ -123,8 +127,9 @@ protected void onCreate(Bundle savedInstanceState) { mWidgetContainer.getViewTreeObserver().addOnGlobalFocusChangeListener(new ViewTreeObserver.OnGlobalFocusChangeListener() { @Override public void onGlobalFocusChanged(View oldFocus, View newFocus) { - if (mKeyboard != null) { - mKeyboard.updateFocusedView(newFocus); + Log.d(LOGTAG, "======> OnGlobalFocusChangeListener: old(" + oldFocus + ") new(" + newFocus + ")"); + for (FocusChangeListener listener: mFocusChangeListeners) { + listener.onGlobalFocusChanged(oldFocus, newFocus); } } }); @@ -187,10 +192,13 @@ protected void initializeWorld() { mTopBar = new TopBarWidget(this); mTopBar.setBrowserWidget(mBrowserWidget); + // Empty widget just for handling focus on empty space + mRootWidget = new RootWidget(this); + // Create Tray mTray = new TrayWidget(this); - addWidgets(Arrays.asList(mBrowserWidget, mNavigationBar, mKeyboard, mTray)); + addWidgets(Arrays.asList(mRootWidget, mBrowserWidget, mNavigationBar, mKeyboard, mTray)); } @Override @@ -418,7 +426,12 @@ void handleMotionEvent(final int aHandle, final int aDevice, final boolean aPres @Override public void run() { Widget widget = mWidgets.get(aHandle); - MotionEventGenerator.dispatch(widget, aDevice, aPressed, aX, aY); + if (widget == null) { + MotionEventGenerator.dispatch(mRootWidget, aDevice, aPressed, aX, aY); + + } else { + MotionEventGenerator.dispatch(widget, aDevice, aPressed, aX, aY); + } } }); } @@ -685,7 +698,7 @@ public void run() { view.setVisibility(visible ? View.VISIBLE : View.GONE); } - for (WidgetManagerDelegate.Listener listener: mWidgetEventListeners) { + for (UpdateListener listener: mWidgetUpdateListeners) { listener.onWidgetUpdate(aWidget); } @@ -725,15 +738,15 @@ public void run() { } @Override - public void addListener(WidgetManagerDelegate.Listener aListener) { - if (!mWidgetEventListeners.contains(aListener)) { - mWidgetEventListeners.add(aListener); + public void addUpdateListener(UpdateListener aUpdateListener) { + if (!mWidgetUpdateListeners.contains(aUpdateListener)) { + mWidgetUpdateListeners.add(aUpdateListener); } } @Override - public void removeListener(WidgetManagerDelegate.Listener aListener) { - mWidgetEventListeners.remove(aListener); + public void removeUpdateListener(UpdateListener aUpdateListener) { + mWidgetUpdateListeners.remove(aUpdateListener); } @Override @@ -748,6 +761,18 @@ public void removePermissionListener(PermissionListener aListener) { mPermissionListeners.remove(aListener); } + @Override + public void addFocusChangeListener(FocusChangeListener aListener) { + if (!mFocusChangeListeners.contains(aListener)) { + mFocusChangeListeners.add(aListener); + } + } + + @Override + public void removeFocusChangeListener(FocusChangeListener aListener) { + mFocusChangeListeners.remove(aListener); + } + @Override public void pushBackHandler(Runnable aRunnable) { mBackHandlers.addLast(aRunnable); diff --git a/app/src/common/shared/org/mozilla/vrbrowser/WidgetManagerDelegate.java b/app/src/common/shared/org/mozilla/vrbrowser/WidgetManagerDelegate.java index 044aa32c8..1cac1d169 100644 --- a/app/src/common/shared/org/mozilla/vrbrowser/WidgetManagerDelegate.java +++ b/app/src/common/shared/org/mozilla/vrbrowser/WidgetManagerDelegate.java @@ -1,24 +1,28 @@ package org.mozilla.vrbrowser; -import android.support.annotation.Nullable; +import android.support.annotation.NonNull; +import android.view.View; public interface WidgetManagerDelegate { - interface Listener { + interface UpdateListener { void onWidgetUpdate(Widget aWidget); } interface PermissionListener { void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults); } + interface FocusChangeListener { + void onGlobalFocusChanged(View oldFocus, View newFocus); + } int newWidgetHandle(); - void addWidget(Widget aWidget); - void updateWidget(Widget aWidget); - void removeWidget(Widget aWidget); - void startWidgetResize(Widget aWidget); - void finishWidgetResize(Widget aWidget); - void addListener(WidgetManagerDelegate.Listener aListener); - void removeListener(WidgetManagerDelegate.Listener aListener); - void pushBackHandler(Runnable aRunnable); - void popBackHandler(Runnable aRunnable); + void addWidget(@NonNull Widget aWidget); + void updateWidget(@NonNull Widget aWidget); + void removeWidget(@NonNull Widget aWidget); + void startWidgetResize(@NonNull Widget aWidget); + void finishWidgetResize(@NonNull Widget aWidget); + void addUpdateListener(@NonNull UpdateListener aUpdateListener); + void removeUpdateListener(@NonNull UpdateListener aUpdateListener); + void pushBackHandler(@NonNull Runnable aRunnable); + void popBackHandler(@NonNull Runnable aRunnable); void fadeOutWorld(); void fadeInWorld(); void setTrayVisible(boolean visible); @@ -26,6 +30,8 @@ interface PermissionListener { void keyboardDismissed(); void updateEnvironment(); void updatePointerColor(); - void addPermissionListener(PermissionListener aListener); - void removePermissionListener(PermissionListener aListener); + void addPermissionListener(@NonNull PermissionListener aListener); + void removePermissionListener(@NonNull PermissionListener aListener); + void addFocusChangeListener(@NonNull FocusChangeListener aListener); + void removeFocusChangeListener(@NonNull FocusChangeListener aListener); } diff --git a/app/src/common/shared/org/mozilla/vrbrowser/ui/BrowserWidget.java b/app/src/common/shared/org/mozilla/vrbrowser/ui/BrowserWidget.java index d9bca5ca6..a921847d9 100644 --- a/app/src/common/shared/org/mozilla/vrbrowser/ui/BrowserWidget.java +++ b/app/src/common/shared/org/mozilla/vrbrowser/ui/BrowserWidget.java @@ -44,7 +44,7 @@ public BrowserWidget(Context aContext, int aSessionId) { mWidgetManager = (WidgetManagerDelegate) aContext; SessionStore.get().addSessionChangeListener(this); SessionStore.get().addPromptListener(this); - setFocusableInTouchMode(true); + setFocusable(true); GeckoSession session = SessionStore.get().getSession(mSessionId); if (session != null) { session.getTextInput().setView(this); @@ -140,8 +140,9 @@ public WidgetPlacement getPlacement() { @Override public void handleTouchEvent(MotionEvent aEvent) { - if (aEvent.getActionMasked() == MotionEvent.ACTION_DOWN) { + if (aEvent.getActionMasked() == MotionEvent.ACTION_UP) { requestFocus(); + requestFocusFromTouch(); } GeckoSession session = SessionStore.get().getSession(mSessionId); if (session == null) { diff --git a/app/src/common/shared/org/mozilla/vrbrowser/ui/DeveloperOptionsWidget.java b/app/src/common/shared/org/mozilla/vrbrowser/ui/DeveloperOptionsWidget.java index bf071227b..20adb3534 100644 --- a/app/src/common/shared/org/mozilla/vrbrowser/ui/DeveloperOptionsWidget.java +++ b/app/src/common/shared/org/mozilla/vrbrowser/ui/DeveloperOptionsWidget.java @@ -332,6 +332,8 @@ protected void initializeWidgetPlacement(WidgetPlacement aPlacement) { } private void showRestartDialog() { + hide(); + UIWidget widget = getChild(mRestartDialogHandle); if (widget == null) { widget = createChild(RestartDialogWidget.class, false); @@ -339,8 +341,6 @@ private void showRestartDialog() { } widget.show(); - - hide(); } private CompoundButton.OnCheckedChangeListener mRemoteDebuggingListener = new CompoundButton.OnCheckedChangeListener() { diff --git a/app/src/common/shared/org/mozilla/vrbrowser/ui/KeyboardWidget.java b/app/src/common/shared/org/mozilla/vrbrowser/ui/KeyboardWidget.java index 9053071d8..fd2169ddd 100644 --- a/app/src/common/shared/org/mozilla/vrbrowser/ui/KeyboardWidget.java +++ b/app/src/common/shared/org/mozilla/vrbrowser/ui/KeyboardWidget.java @@ -27,11 +27,14 @@ import org.mozilla.geckoview.GeckoSession; import org.mozilla.vrbrowser.R; import org.mozilla.vrbrowser.SessionStore; +import org.mozilla.vrbrowser.Widget; +import org.mozilla.vrbrowser.WidgetManagerDelegate; import org.mozilla.vrbrowser.WidgetPlacement; import org.mozilla.vrbrowser.telemetry.TelemetryWrapper; -public class KeyboardWidget extends UIWidget implements CustomKeyboardView.OnKeyboardActionListener, GeckoSession.TextInputDelegate { +public class KeyboardWidget extends UIWidget implements CustomKeyboardView.OnKeyboardActionListener, + GeckoSession.TextInputDelegate, WidgetManagerDelegate.FocusChangeListener { private static final String LOGTAG = "VRB"; @@ -80,6 +83,8 @@ public KeyboardWidget(Context aContext, AttributeSet aAttrs, int aDefStyle) { private void initialize(Context aContext) { inflate(aContext, R.layout.keyboard, this); + mWidgetManager.addFocusChangeListener(this); + mKeyboardview = findViewById(R.id.keyboard); mPopupKeyboardview = findViewById(R.id.popupKeyboard); mVoiceInput = findViewById(R.id.keyboard_microphone); @@ -177,6 +182,7 @@ public void run() { @Override public void releaseWidget() { + mWidgetManager.removeFocusChangeListener(this); SessionStore.get().removeTextInputListener(this); mBrowserWidget = null; super.releaseWidget(); @@ -599,4 +605,11 @@ public void updateCursorAnchorInfo(@NonNull GeckoSession session, @NonNull Curso public void notifyAutoFill(GeckoSession session, int notification, int virtualId) { } + + // FocusChangeListener + + @Override + public void onGlobalFocusChanged(View oldFocus, View newFocus) { + updateFocusedView(newFocus); + } } \ No newline at end of file diff --git a/app/src/common/shared/org/mozilla/vrbrowser/ui/NavigationBarWidget.java b/app/src/common/shared/org/mozilla/vrbrowser/ui/NavigationBarWidget.java index 3fd5126aa..52a36c33b 100644 --- a/app/src/common/shared/org/mozilla/vrbrowser/ui/NavigationBarWidget.java +++ b/app/src/common/shared/org/mozilla/vrbrowser/ui/NavigationBarWidget.java @@ -24,7 +24,7 @@ public class NavigationBarWidget extends UIWidget implements GeckoSession.NavigationDelegate, GeckoSession.ProgressDelegate, GeckoSession.ContentDelegate, - WidgetManagerDelegate.Listener, SessionStore.SessionChangeListener, + WidgetManagerDelegate.UpdateListener, SessionStore.SessionChangeListener, NavigationURLBar.NavigationURLBarDelegate, VoiceSearchWidget.VoiceSearchDelegate { private static final String LOGTAG = "VRB"; @@ -90,6 +90,7 @@ public void run() { mBackButton.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { + requestFocusFromTouch(); if (SessionStore.get().canGoBack()) SessionStore.get().goBack(); else if (SessionStore.get().canUnstackSession()) @@ -104,6 +105,7 @@ else if (SessionStore.get().canUnstackSession()) mForwardButton.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { + requestFocusFromTouch(); SessionStore.get().goForward(); if (mAudio != null) { mAudio.playSound(AudioEngine.Sound.CLICK); @@ -114,6 +116,7 @@ public void onClick(View v) { mReloadButton.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { + requestFocusFromTouch(); if (mIsLoading) { SessionStore.get().stop(); } else { @@ -128,6 +131,7 @@ public void onClick(View v) { mHomeButton.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { + requestFocusFromTouch(); SessionStore.get().loadUri(SessionStore.get().getHomeUri()); if (mAudio != null) { mAudio.playSound(AudioEngine.Sound.CLICK); @@ -145,6 +149,7 @@ public void onClick(View v) { mResizeEnterButton.setOnClickListener(new OnClickListener() { @Override public void onClick(View view) { + requestFocusFromTouch(); enterResizeMode(); if (mAudio != null) { mAudio.playSound(AudioEngine.Sound.CLICK); @@ -155,6 +160,7 @@ public void onClick(View view) { mResizeExitButton.setOnClickListener(new OnClickListener() { @Override public void onClick(View view) { + requestFocusFromTouch(); exitResizeMode(true); if (mAudio != null) { mAudio.playSound(AudioEngine.Sound.CLICK); @@ -165,6 +171,7 @@ public void onClick(View view) { mPreset0.setOnClickListener(new OnClickListener() { @Override public void onClick(View view) { + requestFocusFromTouch(); setResizePreset(0.5f); if (mAudio != null) { mAudio.playSound(AudioEngine.Sound.CLICK); @@ -175,6 +182,7 @@ public void onClick(View view) { mPreset1.setOnClickListener(new OnClickListener() { @Override public void onClick(View view) { + requestFocusFromTouch(); setResizePreset(1.0f); if (mAudio != null) { mAudio.playSound(AudioEngine.Sound.CLICK); @@ -185,6 +193,7 @@ public void onClick(View view) { mPreset2.setOnClickListener(new OnClickListener() { @Override public void onClick(View view) { + requestFocusFromTouch(); setResizePreset(2.0f); if (mAudio != null) { mAudio.playSound(AudioEngine.Sound.CLICK); @@ -195,6 +204,7 @@ public void onClick(View view) { mPreset3.setOnClickListener(new OnClickListener() { @Override public void onClick(View view) { + requestFocusFromTouch(); setResizePreset(3.0f); if (mAudio != null) { mAudio.playSound(AudioEngine.Sound.CLICK); @@ -212,7 +222,7 @@ public void onClick(View view) { SessionStore.get().addNavigationListener(this); SessionStore.get().addProgressListener(this); SessionStore.get().addContentListener(this); - mWidgetManager.addListener(this); + mWidgetManager.addUpdateListener(this); mVoiceSearchWidget = createChild(VoiceSearchWidget.class, false); mVoiceSearchWidget.setDelegate(this); @@ -222,7 +232,7 @@ public void onClick(View view) { @Override public void releaseWidget() { - mWidgetManager.removeListener(this); + mWidgetManager.removeUpdateListener(this); SessionStore.get().removeNavigationListener(this); SessionStore.get().removeProgressListener(this); SessionStore.get().removeContentListener(this); @@ -491,7 +501,7 @@ public void onCrash(GeckoSession session) { } - // WidgetManagerDelegate.Listener + // WidgetManagerDelegate.UpdateListener @Override public void onWidgetUpdate(Widget aWidget) { if (aWidget != mBrowserWidget || mIsResizing) { diff --git a/app/src/common/shared/org/mozilla/vrbrowser/ui/NavigationURLBar.java b/app/src/common/shared/org/mozilla/vrbrowser/ui/NavigationURLBar.java index d4732c582..c773a6a1b 100644 --- a/app/src/common/shared/org/mozilla/vrbrowser/ui/NavigationURLBar.java +++ b/app/src/common/shared/org/mozilla/vrbrowser/ui/NavigationURLBar.java @@ -108,7 +108,6 @@ public boolean onTouch(View view, MotionEvent motionEvent) { mURLWebsiteColor = typedValue.data; // Prevent the URL TextEdit to get focus when user touches something outside of it - setFocusable(true); setFocusableInTouchMode(true); setClickable(true); } diff --git a/app/src/common/shared/org/mozilla/vrbrowser/ui/PermissionWidget.java b/app/src/common/shared/org/mozilla/vrbrowser/ui/PermissionWidget.java index 4a0db0608..70c3d6348 100644 --- a/app/src/common/shared/org/mozilla/vrbrowser/ui/PermissionWidget.java +++ b/app/src/common/shared/org/mozilla/vrbrowser/ui/PermissionWidget.java @@ -23,18 +23,20 @@ import org.mozilla.geckoview.GeckoSession; import org.mozilla.vrbrowser.R; import org.mozilla.vrbrowser.Widget; +import org.mozilla.vrbrowser.WidgetManagerDelegate; import org.mozilla.vrbrowser.WidgetPlacement; import java.net.URI; import java.net.URL; import java.util.ArrayList; -public class PermissionWidget extends UIWidget { +public class PermissionWidget extends UIWidget implements WidgetManagerDelegate.FocusChangeListener { + private static final String LOGTAG = "VRB"; + private TextView mPermissionMessage; private ImageView mPermissionIcon; private GeckoSession.PermissionDelegate.Callback mPermissionCallback; - private Runnable mBackHandler; public enum PermissionType { Camera, @@ -61,6 +63,9 @@ public PermissionWidget(Context aContext, AttributeSet aAttrs, int aDefStyle) { private void initialize(Context aContext) { inflate(aContext, R.layout.permission, this); + + mWidgetManager.addFocusChangeListener(this); + mPermissionIcon = findViewById(R.id.permissionIcon); mPermissionMessage = findViewById(R.id.permissionText); @@ -81,15 +86,13 @@ public void onClick(View v) { handlePermissionResult(true); } }); + } - mBackHandler = new Runnable() { - @Override - public void run() { - mWidgetPlacement.visible = false; - mWidgetManager.updateWidget(PermissionWidget.this); - mWidgetManager.popBackHandler(mBackHandler); - } - }; + @Override + public void releaseWidget() { + mWidgetManager.removeFocusChangeListener(this); + + super.releaseWidget(); } @Override @@ -147,9 +150,7 @@ public void showPrompt(String aUri, PermissionType aType, GeckoSession.Permissio mPermissionMessage.setText(str); mPermissionIcon.setImageResource(iconId); - mWidgetPlacement.visible = true; - mWidgetManager.updateWidget(this); - mWidgetManager.pushBackHandler(mBackHandler); + show(); } String getRequesterName(String aUri) { @@ -164,9 +165,6 @@ String getRequesterName(String aUri) { } private void handlePermissionResult(boolean aGranted) { - mWidgetPlacement.visible = false; - mWidgetManager.updateWidget(this); - mWidgetManager.popBackHandler(mBackHandler); if (mPermissionCallback == null) { return; } @@ -176,5 +174,15 @@ private void handlePermissionResult(boolean aGranted) { mPermissionCallback.reject(); } mPermissionCallback = null; + + hide(); + } + + // WidgetManagerDelegate.FocusChangeListener + @Override + public void onGlobalFocusChanged(View oldFocus, View newFocus) { + if (oldFocus == this) { + hide(); + } } } diff --git a/app/src/common/shared/org/mozilla/vrbrowser/ui/RootWidget.java b/app/src/common/shared/org/mozilla/vrbrowser/ui/RootWidget.java new file mode 100644 index 000000000..76e1f8dbb --- /dev/null +++ b/app/src/common/shared/org/mozilla/vrbrowser/ui/RootWidget.java @@ -0,0 +1,43 @@ +package org.mozilla.vrbrowser.ui; + +import android.content.Context; +import android.util.AttributeSet; +import android.view.View; + +import org.mozilla.vrbrowser.WidgetPlacement; + +public class RootWidget extends UIWidget { + + public RootWidget(Context aContext) { + super(aContext); + initialize(aContext); + } + + public RootWidget(Context aContext, AttributeSet aAttrs) { + super(aContext, aAttrs); + initialize(aContext); + } + + public RootWidget(Context aContext, AttributeSet aAttrs, int aDefStyle) { + super(aContext, aAttrs, aDefStyle); + initialize(aContext); + } + + @Override + protected void initializeWidgetPlacement(WidgetPlacement aPlacement) { + + } + + private void initialize(Context aContext) { + setFocusable(true); + setSoundEffectsEnabled(false); + + setOnClickListener(new OnClickListener() { + @Override + public void onClick(View v) { + requestFocus(); + requestFocusFromTouch(); + } + }); + } +} diff --git a/app/src/common/shared/org/mozilla/vrbrowser/ui/SettingsButton.java b/app/src/common/shared/org/mozilla/vrbrowser/ui/SettingsButton.java index 0f169cad3..aefae6040 100644 --- a/app/src/common/shared/org/mozilla/vrbrowser/ui/SettingsButton.java +++ b/app/src/common/shared/org/mozilla/vrbrowser/ui/SettingsButton.java @@ -40,7 +40,6 @@ private void initialize(Context aContext) { inflate(aContext, R.layout.settings_btn, this); setClickable(true); - setFocusable(true); mIcon = findViewById(R.id.settings_button_icon); if (mIcon != null) diff --git a/app/src/common/shared/org/mozilla/vrbrowser/ui/SettingsWidget.java b/app/src/common/shared/org/mozilla/vrbrowser/ui/SettingsWidget.java index 67b4aaa3a..b0f1381a3 100644 --- a/app/src/common/shared/org/mozilla/vrbrowser/ui/SettingsWidget.java +++ b/app/src/common/shared/org/mozilla/vrbrowser/ui/SettingsWidget.java @@ -29,6 +29,7 @@ import java.util.GregorianCalendar; public class SettingsWidget extends UIWidget { + private static final String LOGTAG = "VRB"; private AudioEngine mAudio; @@ -186,6 +187,11 @@ public void onClick(View view) { mAudio = AudioEngine.fromContext(aContext); } + @Override + public void releaseWidget() { + super.releaseWidget(); + } + @Override protected void initializeWidgetPlacement(WidgetPlacement aPlacement) { aPlacement.visible = false; @@ -252,8 +258,6 @@ private void onSettingsReportClick() { private void onDeveloperOptionsClick() { showDeveloperOptionsDialog(); - - hide(); } /** @@ -306,6 +310,8 @@ private void showRestartDialog() { } private void showDeveloperOptionsDialog() { + hide(); + UIWidget widget = getChild(mDeveloperOptionsDialogHandle); if (widget == null) { widget = createChild(DeveloperOptionsWidget.class, false); @@ -313,20 +319,13 @@ private void showDeveloperOptionsDialog() { } widget.show(); - - hide(); } @Override public void toggle() { - for (UIWidget child : mChildren.values()) { - if (child.getPlacement().visible) - return; - } - super.toggle(); - if (!mWidgetPlacement.visible) + if (!isVisible()) mWidgetManager.fadeInWorld(); else mWidgetManager.fadeOutWorld(); @@ -338,4 +337,5 @@ protected void onBackButton() { mWidgetManager.fadeInWorld(); } + } diff --git a/app/src/common/shared/org/mozilla/vrbrowser/ui/TopBarWidget.java b/app/src/common/shared/org/mozilla/vrbrowser/ui/TopBarWidget.java index 83eaa7e14..89ad4dc0c 100644 --- a/app/src/common/shared/org/mozilla/vrbrowser/ui/TopBarWidget.java +++ b/app/src/common/shared/org/mozilla/vrbrowser/ui/TopBarWidget.java @@ -18,7 +18,7 @@ import org.mozilla.vrbrowser.WidgetPlacement; import org.mozilla.vrbrowser.audio.AudioEngine; -public class TopBarWidget extends UIWidget implements SessionStore.SessionChangeListener, WidgetManagerDelegate.Listener { +public class TopBarWidget extends UIWidget implements SessionStore.SessionChangeListener, WidgetManagerDelegate.UpdateListener { private static final String LOGTAG = "VRB"; private UIButton mCloseButton; @@ -47,6 +47,7 @@ private void initialize(Context aContext) { mCloseButton.setOnClickListener(new OnClickListener() { @Override public void onClick(View view) { + requestFocusFromTouch(); if (mAudio != null) { mAudio.playSound(AudioEngine.Sound.CLICK); } @@ -58,7 +59,7 @@ public void onClick(View view) { mAudio = AudioEngine.fromContext(aContext); SessionStore.get().addSessionChangeListener(this); - mWidgetManager.addListener(this); + mWidgetManager.addUpdateListener(this); } @Override @@ -79,7 +80,7 @@ protected void initializeWidgetPlacement(WidgetPlacement aPlacement) { @Override public void releaseWidget() { SessionStore.get().removeSessionChangeListener(this); - mWidgetManager.removeListener(this); + mWidgetManager.removeUpdateListener(this); super.releaseWidget(); } @@ -119,7 +120,7 @@ public void setVisible(boolean isVisible) { mWidgetManager.removeWidget(this); } - // WidgetManagerDelegate.Listener + // WidgetManagerDelegate.UpdateListener @Override public void onWidgetUpdate(Widget aWidget) { if (aWidget != mBrowserWidget) { diff --git a/app/src/common/shared/org/mozilla/vrbrowser/ui/TrayWidget.java b/app/src/common/shared/org/mozilla/vrbrowser/ui/TrayWidget.java index 9bdc516d2..30739cb00 100644 --- a/app/src/common/shared/org/mozilla/vrbrowser/ui/TrayWidget.java +++ b/app/src/common/shared/org/mozilla/vrbrowser/ui/TrayWidget.java @@ -12,10 +12,13 @@ import org.mozilla.geckoview.GeckoSessionSettings; import org.mozilla.vrbrowser.R; import org.mozilla.vrbrowser.SessionStore; +import org.mozilla.vrbrowser.WidgetManagerDelegate; import org.mozilla.vrbrowser.WidgetPlacement; import org.mozilla.vrbrowser.audio.AudioEngine; -public class TrayWidget extends UIWidget implements SessionStore.SessionChangeListener { +public class TrayWidget extends UIWidget implements SessionStore.SessionChangeListener, + WidgetManagerDelegate.FocusChangeListener { + private static final String LOGTAG = "VRB"; private UIButton mHelpButton; @@ -43,10 +46,13 @@ public TrayWidget(Context aContext, AttributeSet aAttrs, int aDefStyle) { private void initialize(Context aContext) { inflate(aContext, R.layout.tray, this); + mWidgetManager.addFocusChangeListener(this); + mHelpButton = findViewById(R.id.helpButton); mHelpButton.setOnClickListener(new OnClickListener() { @Override public void onClick(View view) { + requestFocusFromTouch(); if (mAudio != null) { mAudio.playSound(AudioEngine.Sound.CLICK); } @@ -59,6 +65,7 @@ public void onClick(View view) { mPrivateButton.setOnClickListener(new OnClickListener() { @Override public void onClick(View view) { + requestFocusFromTouch(); if (mAudio != null) { mAudio.playSound(AudioEngine.Sound.CLICK); } @@ -71,6 +78,9 @@ public void onClick(View view) { mSettingsButton.setOnClickListener(new OnClickListener() { @Override public void onClick(View view) { + if (!isChildVisible(mSettingsDialogHandle)) + requestFocusFromTouch(); + if (mAudio != null) { mAudio.playSound(AudioEngine.Sound.CLICK); } @@ -105,6 +115,7 @@ protected void initializeWidgetPlacement(WidgetPlacement aPlacement) { @Override public void releaseWidget() { + mWidgetManager.removeFocusChangeListener(this); SessionStore.get().removeSessionChangeListener(this); super.releaseWidget(); @@ -151,15 +162,6 @@ private void showSettingsDialog() { widget.toggle(); } - public boolean isSettingsDialogOpened() { - UIWidget widget = getChild(mSettingsDialogHandle); - if (widget != null) { - return widget.isOpened(); - } - - return false; - } - @Override public void show() { if (!mWidgetPlacement.visible) { @@ -176,6 +178,11 @@ public void hide() { } } + + public boolean isSettingsDialogOpened() { + return isChildVisible(mSettingsDialogHandle); + } + private void onHelpButtonClicked() { GeckoSession session = SessionStore.get().getCurrentSession(); if (session == null) { @@ -186,4 +193,15 @@ private void onHelpButtonClicked() { SessionStore.get().loadUri(getContext().getString(R.string.help_url)); } + + // WindowManagerDelegate.FocusChangeListener + @Override + public void onGlobalFocusChanged(View oldFocus, View newFocus) { + if (isSettingsDialogOpened()) { + UIWidget child = getChild(mSettingsDialogHandle); + if (child != null) { + child.toggle(); + } + } + } } diff --git a/app/src/common/shared/org/mozilla/vrbrowser/ui/UIButton.java b/app/src/common/shared/org/mozilla/vrbrowser/ui/UIButton.java index 85e9df7ec..5c92f4ed2 100644 --- a/app/src/common/shared/org/mozilla/vrbrowser/ui/UIButton.java +++ b/app/src/common/shared/org/mozilla/vrbrowser/ui/UIButton.java @@ -11,6 +11,7 @@ import android.graphics.drawable.Drawable; import android.util.AttributeSet; import android.support.v7.widget.AppCompatImageButton; +import android.view.View; import org.mozilla.vrbrowser.R; @@ -88,4 +89,5 @@ public void setPrivateMode(boolean isEnabled) { } } } + } diff --git a/app/src/common/shared/org/mozilla/vrbrowser/ui/UIWidget.java b/app/src/common/shared/org/mozilla/vrbrowser/ui/UIWidget.java index 86193dd5d..7abb81b21 100644 --- a/app/src/common/shared/org/mozilla/vrbrowser/ui/UIWidget.java +++ b/app/src/common/shared/org/mozilla/vrbrowser/ui/UIWidget.java @@ -24,12 +24,14 @@ import java.util.HashMap; public abstract class UIWidget extends FrameLayout implements Widget { - UISurfaceTextureRenderer mRenderer; - SurfaceTexture mTexture; + + private static final String LOGTAG = "VRB"; + + private UISurfaceTextureRenderer mRenderer; + private SurfaceTexture mTexture; protected int mHandle; protected WidgetPlacement mWidgetPlacement; protected WidgetManagerDelegate mWidgetManager; - static final String LOGTAG = "VRB"; protected int mInitialWidth; protected int mInitialHeight; protected Runnable mBackHandler; @@ -204,7 +206,7 @@ public void setDelegate(UIWidgetDelegate aDelegate) { } public void toggle() { - if (mWidgetPlacement.visible) { + if (isVisible()) { hide(); } else { @@ -218,25 +220,45 @@ public void show() { mWidgetManager.addWidget(this); mWidgetManager.pushBackHandler(mBackHandler); } + + setFocusableInTouchMode(true); + requestFocusFromTouch(); } public void hide() { + for (UIWidget child : mChildren.values()) { + if (child.isVisible()) { + child.hide(); + } + } + if (mWidgetPlacement.visible) { mWidgetPlacement.visible = false; mWidgetManager.removeWidget(this); mWidgetManager.popBackHandler(mBackHandler); } + + clearFocus(); } - public boolean isOpened() { + public boolean isVisible() { for (UIWidget child : mChildren.values()) { - if (child.mWidgetPlacement.visible) + if (child.isVisible()) return true; } return mWidgetPlacement.visible; } + public boolean isChildVisible(int aHandle) { + UIWidget widget = getChild(aHandle); + if (widget != null) { + return widget.isVisible(); + } + + return false; + } + protected T createChild(@NonNull Class aChildClassName) { return createChild(aChildClassName, true); } @@ -281,8 +303,7 @@ protected void removeChild(int aChildId) { protected void removeAllChildren() { for (UIWidget child : mChildren.values()) { - child.hide(); - child.releaseWidget(); + removeChild(child.mHandle); } mChildren.clear(); } diff --git a/app/src/common/shared/org/mozilla/vrbrowser/ui/VoiceSearchWidget.java b/app/src/common/shared/org/mozilla/vrbrowser/ui/VoiceSearchWidget.java index ac108db29..69b447673 100644 --- a/app/src/common/shared/org/mozilla/vrbrowser/ui/VoiceSearchWidget.java +++ b/app/src/common/shared/org/mozilla/vrbrowser/ui/VoiceSearchWidget.java @@ -28,7 +28,8 @@ import static org.mozilla.gecko.GeckoAppShell.getApplicationContext; -public class VoiceSearchWidget extends UIWidget implements WidgetManagerDelegate.PermissionListener, Application.ActivityLifecycleCallbacks { +public class VoiceSearchWidget extends UIWidget implements WidgetManagerDelegate.PermissionListener, + Application.ActivityLifecycleCallbacks, WidgetManagerDelegate.FocusChangeListener { private static final String LOGTAG = "VRB"; private static final int VOICESEARCH_AUDIO_REQUEST_CODE = 7455; @@ -77,6 +78,7 @@ public VoiceSearchWidget(Context aContext, AttributeSet aAttrs, int aDefStyle) { private void initialize(Context aContext) { inflate(aContext, R.layout.voice_search_dialog, this); + mWidgetManager.addFocusChangeListener(this); mWidgetManager.addPermissionListener(this); mMozillaSpeechService = MozillaSpeechService.getInstance(); @@ -102,7 +104,7 @@ private void initialize(Context aContext) { mSearchingAnimation.setRepeatCount(Animation.INFINITE); mVoiceSearchSearching = findViewById(R.id.voiceSearchSearching); - mCloseButton = createChild(CloseButtonWidget.class, true); + mCloseButton = createChild(CloseButtonWidget.class); mCloseButton.setDelegate(new CloseButtonWidget.CloseButtonDelegate() { @Override public void OnClick() { @@ -119,6 +121,7 @@ public void setDelegate(VoiceSearchDelegate delegate) { @Override public void releaseWidget() { + mWidgetManager.removeFocusChangeListener(this); mWidgetManager.removePermissionListener(this); mMozillaSpeechService.removeListener(mVoiceSearchListener); ((Application)getApplicationContext()).unregisterActivityLifecycleCallbacks(this); @@ -352,4 +355,12 @@ public void onActivityDestroyed(Activity activity) { } + // WidgetManagerDelegate.FocusChangeListener + @Override + public void onGlobalFocusChanged(View oldFocus, View newFocus) { + if (oldFocus == this) { + hide(); + } + } + } diff --git a/app/src/common/shared/org/mozilla/vrbrowser/ui/prompts/ChoicePromptWidget.java b/app/src/common/shared/org/mozilla/vrbrowser/ui/prompts/ChoicePromptWidget.java index 2f5cc9064..080486fe8 100644 --- a/app/src/common/shared/org/mozilla/vrbrowser/ui/prompts/ChoicePromptWidget.java +++ b/app/src/common/shared/org/mozilla/vrbrowser/ui/prompts/ChoicePromptWidget.java @@ -4,19 +4,23 @@ import android.graphics.Typeface; import android.os.Handler; import android.support.annotation.NonNull; -import android.support.annotation.Nullable; import android.util.AttributeSet; import android.util.SparseBooleanArray; import android.view.LayoutInflater; import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup; -import android.widget.*; -import org.mozilla.geckoview.GeckoResult; -import org.mozilla.geckoview.GeckoSession; +import android.widget.AdapterView; +import android.widget.ArrayAdapter; +import android.widget.Button; +import android.widget.LinearLayout; +import android.widget.ListView; +import android.widget.RadioButton; +import android.widget.TextView; + import org.mozilla.geckoview.GeckoSession.PromptDelegate.Choice; import org.mozilla.vrbrowser.R; -import org.mozilla.vrbrowser.SessionStore; +import org.mozilla.vrbrowser.WidgetManagerDelegate; import org.mozilla.vrbrowser.WidgetPlacement; import org.mozilla.vrbrowser.audio.AudioEngine; import org.mozilla.vrbrowser.ui.UIWidget; @@ -24,7 +28,7 @@ import java.util.ArrayList; import java.util.Arrays; -public class ChoicePromptWidget extends UIWidget implements GeckoSession.NavigationDelegate { +public class ChoicePromptWidget extends UIWidget implements WidgetManagerDelegate.FocusChangeListener { private static final int DIALOG_CLOSE_DELAY = 250; @@ -61,9 +65,9 @@ public ChoicePromptWidget(Context aContext, AttributeSet aAttrs, int aDefStyle) private void initialize(Context aContext) { inflate(aContext, R.layout.choice_prompt, this); - mAudio = AudioEngine.fromContext(aContext); + mWidgetManager.addFocusChangeListener(this); - SessionStore.get().addNavigationListener(this); + mAudio = AudioEngine.fromContext(aContext); mList = findViewById(R.id.choiceslist); mList.setSoundEffectsEnabled(false); @@ -161,9 +165,9 @@ protected void initializeWidgetPlacement(WidgetPlacement aPlacement) { @Override public void releaseWidget() { - super.releaseWidget(); + mWidgetManager.removeFocusChangeListener(this); - SessionStore.get().removeNavigationListener(this); + super.releaseWidget(); } @Override @@ -394,47 +398,14 @@ public boolean onHover(View view, MotionEvent motionEvent) { } - // NavigationDelegate - + // WidgetManagerDelegate.FocusChangeListener @Override - public void onLocationChange(GeckoSession session, String url) { - if (mPromptDelegate != null) { - mPromptDelegate.onDismissed(getDefaultChoices(mListItems)); - } - } - - @Override - public void onCanGoBack(GeckoSession session, boolean canGoBack) { - - } - - @Override - public void onCanGoForward(GeckoSession session, boolean canGoForward) { - - } - - @Nullable - @Override - public GeckoResult onLoadRequest(@NonNull GeckoSession session, @NonNull String uri, int target, int flags) { - if (mPromptDelegate != null) { - mPromptDelegate.onDismissed(getDefaultChoices(mListItems)); - } - return null; - } - - @Nullable - @Override - public GeckoResult onNewSession(@NonNull GeckoSession session, @NonNull String uri) { - if (mPromptDelegate != null) { - mPromptDelegate.onDismissed(getDefaultChoices(mListItems)); + public void onGlobalFocusChanged(View oldFocus, View newFocus) { + if (oldFocus == this) { + if (mPromptDelegate != null) { + mPromptDelegate.onDismissed(getDefaultChoices(mListItems)); + } } - - return null; - } - - @Override - public GeckoResult onLoadError(GeckoSession session, String uri, int category, int error) { - return null; } } diff --git a/app/src/main/cpp/BrowserWorld.cpp b/app/src/main/cpp/BrowserWorld.cpp index c4d813cc7..01856b2e1 100644 --- a/app/src/main/cpp/BrowserWorld.cpp +++ b/app/src/main/cpp/BrowserWorld.cpp @@ -288,10 +288,12 @@ BrowserWorld::State::UpdateControllers(bool& aRelayoutWidgets) { resizingWidget.reset(); } + const bool pressed = controller.buttonState & ControllerDelegate::BUTTON_TRIGGER || + controller.buttonState & ControllerDelegate::BUTTON_TOUCHPAD; + const bool wasPressed = controller.lastButtonState & ControllerDelegate::BUTTON_TRIGGER || + controller.lastButtonState & ControllerDelegate::BUTTON_TOUCHPAD; if (hitWidget && hitWidget->IsResizing()) { active.push_back(hitWidget.get()); - const bool pressed = controller.buttonState & ControllerDelegate::BUTTON_TRIGGER || - controller.buttonState & ControllerDelegate::BUTTON_TOUCHPAD; bool aResized = false, aResizeEnded = false; hitWidget->HandleResize(hitPoint, pressed, aResized, aResizeEnded); @@ -316,10 +318,6 @@ BrowserWorld::State::UpdateControllers(bool& aRelayoutWidgets) { float theX = 0.0f, theY = 0.0f; hitWidget->ConvertToWidgetCoordinates(hitPoint, theX, theY); const uint32_t handle = hitWidget->GetHandle(); - const bool pressed = controller.buttonState & ControllerDelegate::BUTTON_TRIGGER || - controller.buttonState & ControllerDelegate::BUTTON_TOUCHPAD; - const bool wasPressed = controller.lastButtonState & ControllerDelegate::BUTTON_TRIGGER || - controller.lastButtonState & ControllerDelegate::BUTTON_TOUCHPAD; if (!pressed && wasPressed) { controller.inDeadZone = true; } @@ -358,9 +356,11 @@ BrowserWorld::State::UpdateControllers(bool& aRelayoutWidgets) { } } } else if (controller.widget) { - VRBrowser::HandleMotionEvent(0, controller.index, JNI_FALSE, 0.0f, 0.0f); + VRBrowser::HandleMotionEvent(0, controller.index, pressed, 0.0f, 0.0f); controller.widget = 0; + } else { + VRBrowser::HandleMotionEvent(0, controller.index, pressed, 0.0f, 0.0f); } controller.lastButtonState = controller.buttonState; }