From b2e74bd53627626eef6e148592a125e98bd2b56d Mon Sep 17 00:00:00 2001 From: Imanol Fernandez Date: Fri, 14 Jun 2019 20:40:56 +0200 Subject: [PATCH] Allow keyboard to be repositioned --- app/CMakeLists.txt | 1 + .../mozilla/vrbrowser/VRBrowserActivity.java | 23 +++ .../vrbrowser/browser/SettingsStore.java | 39 +++++ .../vrbrowser/ui/widgets/KeyboardWidget.java | 50 +++++- .../vrbrowser/ui/widgets/UIWidget.java | 8 + .../mozilla/vrbrowser/ui/widgets/Widget.java | 1 + .../ui/widgets/WidgetManagerDelegate.java | 8 + app/src/main/cpp/BrowserWorld.cpp | 78 ++++++++- app/src/main/cpp/BrowserWorld.h | 2 + app/src/main/cpp/Controller.cpp | 2 + app/src/main/cpp/Controller.h | 1 + app/src/main/cpp/VRBrowser.cpp | 12 ++ app/src/main/cpp/VRBrowser.h | 1 + app/src/main/cpp/Widget.cpp | 6 +- app/src/main/cpp/Widget.h | 3 +- app/src/main/cpp/WidgetMover.cpp | 151 ++++++++++++++++++ app/src/main/cpp/WidgetMover.h | 46 ++++++ app/src/main/cpp/WidgetPlacement.cpp | 7 + app/src/main/cpp/WidgetPlacement.h | 4 +- app/src/main/res/drawable/ic_icon_move.xml | 8 + app/src/main/res/layout/keyboard.xml | 40 +++-- app/src/main/res/values/non_L10n.xml | 1 + 22 files changed, 468 insertions(+), 24 deletions(-) create mode 100644 app/src/main/cpp/WidgetMover.cpp create mode 100644 app/src/main/cpp/WidgetMover.h create mode 100644 app/src/main/res/drawable/ic_icon_move.xml diff --git a/app/CMakeLists.txt b/app/CMakeLists.txt index d637ec8644..cc359c533b 100644 --- a/app/CMakeLists.txt +++ b/app/CMakeLists.txt @@ -41,6 +41,7 @@ add_library( # Sets the name of the library. src/main/cpp/VRLayer.cpp src/main/cpp/VRLayerNode.cpp src/main/cpp/Widget.cpp + src/main/cpp/WidgetMover.cpp src/main/cpp/WidgetPlacement.cpp src/main/cpp/WidgetResizer.cpp ) diff --git a/app/src/common/shared/org/mozilla/vrbrowser/VRBrowserActivity.java b/app/src/common/shared/org/mozilla/vrbrowser/VRBrowserActivity.java index f1f6e56119..c4a10a4963 100644 --- a/app/src/common/shared/org/mozilla/vrbrowser/VRBrowserActivity.java +++ b/app/src/common/shared/org/mozilla/vrbrowser/VRBrowserActivity.java @@ -713,6 +713,17 @@ void handleResize(final int aHandle, final float aWorldWidth, final float aWorld }); } + @Keep + @SuppressWarnings("unused") + void handleMoveEnd(final int aHandle, final float aDeltaX, final float aDeltaY, final float aDeltaZ, final float aRotation) { + runOnUiThread(() -> { + Widget widget = mWidgets.get(aHandle); + if (widget != null) { + widget.handleMoveEvent(aDeltaX, aDeltaY, aDeltaZ, aRotation); + } + }); + } + @Keep @SuppressWarnings("unused") void registerExternalContext(long aContext) { @@ -1017,6 +1028,16 @@ public void finishWidgetResize(final Widget aWidget) { queueRunnable(() -> finishWidgetResizeNative(aWidget.getHandle())); } + @Override + public void startWidgetMove(final Widget aWidget, @WidgetMoveBehaviourFlags int aMoveBehaviour) { + queueRunnable(() -> startWidgetMoveNative(aWidget.getHandle(), aMoveBehaviour)); + } + + @Override + public void finishWidgetMove() { + queueRunnable(() -> finishWidgetMoveNative()); + } + @Override public void addUpdateListener(UpdateListener aUpdateListener) { if (!mWidgetUpdateListeners.contains(aUpdateListener)) { @@ -1215,6 +1236,8 @@ public void setCylinderDensity(final float aDensity) { private native void removeWidgetNative(int aHandle); private native void startWidgetResizeNative(int aHandle); private native void finishWidgetResizeNative(int aHandle); + private native void startWidgetMoveNative(int aHandle, int aMoveBehaviour); + private native void finishWidgetMoveNative(); private native void setWorldBrightnessNative(float aBrigthness); private native void setTemporaryFilePath(String aPath); private native void exitImmersiveNative(); diff --git a/app/src/common/shared/org/mozilla/vrbrowser/browser/SettingsStore.java b/app/src/common/shared/org/mozilla/vrbrowser/browser/SettingsStore.java index 6393701f85..f68e2dede6 100644 --- a/app/src/common/shared/org/mozilla/vrbrowser/browser/SettingsStore.java +++ b/app/src/common/shared/org/mozilla/vrbrowser/browser/SettingsStore.java @@ -5,12 +5,16 @@ import android.graphics.Color; import android.os.StrictMode; import android.preference.PreferenceManager; +import android.util.Log; +import org.json.JSONException; +import org.json.JSONObject; import org.mozilla.geckoview.GeckoSessionSettings; import org.mozilla.telemetry.TelemetryHolder; import org.mozilla.vrbrowser.BuildConfig; import org.mozilla.vrbrowser.R; import org.mozilla.vrbrowser.telemetry.TelemetryWrapper; +import org.mozilla.vrbrowser.ui.widgets.WidgetPlacement; import org.mozilla.vrbrowser.utils.DeviceType; import org.mozilla.vrbrowser.utils.LocaleUtils; import org.mozilla.vrbrowser.utils.StringUtils; @@ -521,5 +525,40 @@ public void setNotificationsEnabled(boolean isEnabled) { editor.putBoolean(mContext.getString(R.string.settings_key_notifications), isEnabled); editor.commit(); } + + + public void restoreKeyboardMove(WidgetPlacement aPlacement) { + try { + String value = mPrefs.getString(mContext.getString(R.string.settings_key_keyboard_move), null); + if (StringUtils.isEmpty(value)) { + return; + } + JSONObject json = new JSONObject(value); + aPlacement.translationX = (float)json.optDouble("x", aPlacement.translationX); + aPlacement.translationY = (float)json.optDouble("y", aPlacement.translationY); + aPlacement.translationZ = (float)json.optDouble("z", aPlacement.translationZ); + aPlacement.rotation = (float)json.optDouble("r", aPlacement.rotation); + } + catch (JSONException ex) { + Log.e(LOGTAG, "Error restoring keyboard move: " + ex.toString()); + } + } + + public void saveKeyboardMove(WidgetPlacement aPlacement) { + try { + SharedPreferences.Editor editor = mPrefs.edit(); + JSONObject json = new JSONObject(); + json.put("x", aPlacement.translationX); + json.put("y", aPlacement.translationY); + json.put("z", aPlacement.translationZ); + json.put("r", aPlacement.rotation); + editor.putString(mContext.getString(R.string.settings_key_keyboard_move), json.toString()); + editor.commit(); + } + catch (JSONException ex) { + Log.e(LOGTAG, "Error saving keyboard move: " + ex.toString()); + } + + } } diff --git a/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/KeyboardWidget.java b/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/KeyboardWidget.java index 40073746e4..dd11f16280 100644 --- a/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/KeyboardWidget.java +++ b/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/KeyboardWidget.java @@ -15,6 +15,7 @@ import android.text.TextWatcher; import android.util.AttributeSet; import android.util.Log; +import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup; import android.view.inputmethod.CursorAnchorInfo; @@ -88,6 +89,7 @@ public class KeyboardWidget extends UIWidget implements CustomKeyboardView.OnKey private int mKeyWidth; private int mKeyboardPopupTopMargin; private ImageButton mCloseKeyboardButton; + private ImageButton mKeyboardMoveButton; private boolean mIsLongPress; private boolean mIsMultiTap; private boolean mIsCapsLock; @@ -97,6 +99,43 @@ public class KeyboardWidget extends UIWidget implements CustomKeyboardView.OnKey private String mComposingDisplayText = ""; private boolean mInternalDeleteHint = false; + private class MoveTouchListener implements OnTouchListener { + @Override + public boolean onTouch(View v, MotionEvent event) { + switch (event.getAction()) { + case MotionEvent.ACTION_POINTER_DOWN: + case MotionEvent.ACTION_DOWN: + v.setPressed(true); + mWidgetManager.startWidgetMove(KeyboardWidget.this, WidgetManagerDelegate.WIDGET_MOVE_BEHAVIOUR_KEYBOARD); + break; + case MotionEvent.ACTION_POINTER_UP: + case MotionEvent.ACTION_UP: + case MotionEvent.ACTION_CANCEL: + v.setPressed(false); + mWidgetManager.finishWidgetMove(); + break; + default: + return false; + + } + return true; + } + } + + private class MoveHoverListener implements OnHoverListener { + @Override + public boolean onHover(View v, MotionEvent event) { + switch (event.getAction()) { + case MotionEvent.ACTION_HOVER_ENTER: + v.setHovered(true); + break; + case MotionEvent.ACTION_HOVER_EXIT: + v.setHovered(false); + } + return false; + } + } + public KeyboardWidget(Context aContext) { super(aContext); initialize(aContext); @@ -179,7 +218,9 @@ private void initialize(Context aContext) { mCapsLockOnIcon = getResources().getDrawable(R.drawable.ic_icon_keyboard_caps, getContext().getTheme()); mCloseKeyboardButton = findViewById(R.id.keyboardCloseButton); mCloseKeyboardButton.setOnClickListener(v -> dismiss()); - + mKeyboardMoveButton = findViewById(R.id.keyboardMoveButton); + mKeyboardMoveButton.setOnTouchListener(new MoveTouchListener()); + mKeyboardMoveButton.setOnHoverListener(new MoveHoverListener()); mKeyWidth = getResources().getDimensionPixelSize(R.dimen.keyboard_key_width); mKeyboardPopupTopMargin = getResources().getDimensionPixelSize(R.dimen.keyboard_key_pressed_padding) * 2; @@ -226,6 +267,13 @@ protected void initializeWidgetPlacement(WidgetPlacement aPlacement) { aPlacement.worldWidth = WidgetPlacement.floatDimension(context, R.dimen.keyboard_world_width); aPlacement.visible = false; aPlacement.cylinder = true; + SettingsStore.getInstance(getContext()).restoreKeyboardMove(mWidgetPlacement); + } + + @Override + public void handleMoveEvent(float aDeltaX, float aDeltaY, float aDeltaZ, float aRotation) { + super.handleMoveEvent(aDeltaX, aDeltaY, aDeltaZ, aRotation); + SettingsStore.getInstance(getContext()).saveKeyboardMove(mWidgetPlacement); } private int getKeyboardWidth(float aAlphabeticWidth) { diff --git a/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/UIWidget.java b/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/UIWidget.java index c1b51d307a..34241be0ff 100644 --- a/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/UIWidget.java +++ b/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/UIWidget.java @@ -172,6 +172,14 @@ public void handleResizeEvent(float aWorldWidth, float aWorldHeight) { mWidgetManager.updateWidget(this); } + @Override + public void handleMoveEvent(float aDeltaX, float aDeltaY, float aDeltaZ, float aRotation) { + mWidgetPlacement.translationX += aDeltaX; + mWidgetPlacement.translationY += aDeltaY; + mWidgetPlacement.translationZ += aDeltaZ; + mWidgetPlacement.rotation = aRotation; + } + @Override public void releaseWidget() { if (mRenderer != null) { diff --git a/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/Widget.java b/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/Widget.java index 640d9f7cad..6fb403c178 100644 --- a/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/Widget.java +++ b/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/Widget.java @@ -20,6 +20,7 @@ public interface Widget { void handleTouchEvent(MotionEvent aEvent); void handleHoverEvent(MotionEvent aEvent); void handleResizeEvent(float aWorldWidth, float aWorldHeight); + void handleMoveEvent(float aDeltaX, float aDeltaY, float aDeltaZ, float aRotation); void releaseWidget(); void setFirstDraw(boolean aIsFirstDraw); boolean getFirstDraw(); diff --git a/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/WidgetManagerDelegate.java b/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/WidgetManagerDelegate.java index 374ee5f718..5c476527e2 100644 --- a/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/WidgetManagerDelegate.java +++ b/app/src/common/shared/org/mozilla/vrbrowser/ui/widgets/WidgetManagerDelegate.java @@ -4,6 +4,7 @@ import org.mozilla.geckoview.GeckoSession; +import androidx.annotation.IntDef; import androidx.annotation.NonNull; public interface WidgetManagerDelegate { @@ -23,12 +24,19 @@ interface WorldClickListener { float DEFAULT_DIM_BRIGHTNESS = 0.25f; float DEFAULT_NO_DIM_BRIGHTNESS = 1.0f; + @IntDef(value = { WIDGET_MOVE_BEHAVIOUR_GENERAL, WIDGET_MOVE_BEHAVIOUR_KEYBOARD}) + public @interface WidgetMoveBehaviourFlags {} + public static final int WIDGET_MOVE_BEHAVIOUR_GENERAL = 0; + public static final int WIDGET_MOVE_BEHAVIOUR_KEYBOARD = 1; + int newWidgetHandle(); 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 startWidgetMove(@NonNull Widget aWidget, @WidgetMoveBehaviourFlags int aMoveBehaviour); + void finishWidgetMove(); void addUpdateListener(@NonNull UpdateListener aUpdateListener); void removeUpdateListener(@NonNull UpdateListener aUpdateListener); void pushBackHandler(@NonNull Runnable aRunnable); diff --git a/app/src/main/cpp/BrowserWorld.cpp b/app/src/main/cpp/BrowserWorld.cpp index 7ce6cb603a..c0f175bd87 100644 --- a/app/src/main/cpp/BrowserWorld.cpp +++ b/app/src/main/cpp/BrowserWorld.cpp @@ -17,6 +17,7 @@ #include "SplashAnimation.h" #include "Pointer.h" #include "Widget.h" +#include "WidgetMover.h" #include "WidgetPlacement.h" #include "Cylinder.h" #include "Quad.h" @@ -73,7 +74,6 @@ const int GestureSwipeLeft = 0; const int GestureSwipeRight = 1; const float kScrollFactor = 20.0f; // Just picked what fell right. -const float kWorldDPIRatio = 2.0f/720.0f; const double kHoverRate = 1.0 / 10.0; class SurfaceObserver; @@ -181,6 +181,7 @@ struct BrowserWorld::State { SplashAnimationPtr splashAnimation; VRVideoPtr vrVideo; PerformanceMonitorPtr monitor; + WidgetMoverPtr movingWidget; State() : paused(true), glInitialized(false), modelsLoaded(false), env(nullptr), cylinderDensity(0.0f), nearClip(0.1f), farClip(300.0f), activity(nullptr), windowsInitialized(false), exitImmersiveRequested(false), loaderDelay(0) { @@ -358,6 +359,7 @@ BrowserWorld::State::UpdateControllers(bool& aRelayoutWidgets) { vrb::Vector start = controller.transformMatrix.MultiplyPosition(controller.beamTransformMatrix.MultiplyPosition(vrb::Vector())); vrb::Vector direction = controller.transformMatrix.MultiplyDirection(controller.beamTransformMatrix.MultiplyDirection(vrb::Vector(0.0f, 0.0f, -1.0f))); + WidgetPtr hitWidget; float hitDistance = farClip; vrb::Vector hitPoint; @@ -398,7 +400,18 @@ BrowserWorld::State::UpdateControllers(bool& aRelayoutWidgets) { controller.buttonState & ControllerDelegate::BUTTON_TOUCHPAD; const bool wasPressed = controller.lastButtonState & ControllerDelegate::BUTTON_TRIGGER || controller.lastButtonState & ControllerDelegate::BUTTON_TOUCHPAD; - if (hitWidget && hitWidget->IsResizing()) { + + if (movingWidget && movingWidget->IsMoving(controller.index)) { + if (!pressed && wasPressed) { + movingWidget->EndMoving(); + } else { + WidgetPlacementPtr updatedPlacement = movingWidget->HandleMove(start, direction); + if (updatedPlacement) { + movingWidget->GetWidget()->SetPlacement(updatedPlacement); + aRelayoutWidgets = true; + } + } + } else if (hitWidget && hitWidget->IsResizing()) { bool aResized = false, aResizeEnded = false; hitWidget->HandleResize(hitPoint, pressed, aResized, aResizeEnded); @@ -425,6 +438,7 @@ BrowserWorld::State::UpdateControllers(bool& aRelayoutWidgets) { if (!pressed && wasPressed) { controller.inDeadZone = true; } + controller.pointerWorldPoint = hitPoint; const bool moved = pressed ? OutOfDeadZone(controller, theX, theY) : (controller.pointerX != theX) || (controller.pointerY != theY); const bool throttled = ThrottleHoverEvent(controller, context->GetTimestamp(), pressed, wasPressed); @@ -815,7 +829,7 @@ BrowserWorld::AddWidget(int32_t aHandle, const WidgetPlacementPtr& aPlacement) { } float worldWidth = aPlacement->worldWidth; if (worldWidth <= 0.0f) { - worldWidth = aPlacement->width * kWorldDPIRatio; + worldWidth = aPlacement->width * WidgetPlacement::WORLD_DPI_RATIO; } const int32_t textureWidth = aPlacement->GetTextureWidth(); @@ -877,7 +891,7 @@ BrowserWorld::UpdateWidget(int32_t aHandle, const WidgetPlacementPtr& aPlacement float newWorldWidth = aPlacement->worldWidth; if (newWorldWidth <= 0.0f) { - newWorldWidth = aPlacement->width * kWorldDPIRatio; + newWorldWidth = aPlacement->width * WidgetPlacement::WORLD_DPI_RATIO; } if (newWorldWidth != worldWidth || oldWidth != aPlacement->width || oldHeight != aPlacement->height) { @@ -923,6 +937,46 @@ BrowserWorld::FinishWidgetResize(int32_t aHandle) { widget->FinishResize(); } +void +BrowserWorld::StartWidgetMove(int32_t aHandle, int32_t aMoveBehavour) { + ASSERT_ON_RENDER_THREAD(); + WidgetPtr widget = m.GetWidget(aHandle); + if (!widget) { + return; + } + + vrb::Vector initialPoint; + int controllerIndex = -1; + + for (Controller& controller: m.controllers->GetControllers()) { + if (!controller.enabled || (controller.index < 0)) { + continue; + } + + if (controller.pointer && controller.pointer->GetHitWidget() == widget) { + controllerIndex = controller.index; + initialPoint = controller.pointerWorldPoint; + break; + } + } + + if (controllerIndex < 0) { + return; + } + + m.movingWidget = WidgetMover::Create(); + m.movingWidget->StartMoving(widget, aMoveBehavour, controllerIndex, initialPoint); +} + +void +BrowserWorld::FinishWidgetMove() { + ASSERT_ON_RENDER_THREAD(); + if (m.movingWidget) { + m.movingWidget->EndMoving(); + } + m.movingWidget = nullptr; +} + void BrowserWorld::UpdateVisibleWidgets() { ASSERT_ON_RENDER_THREAD(); @@ -953,9 +1007,9 @@ BrowserWorld::LayoutWidget(int32_t aHandle) { vrb::Matrix transform = vrb::Matrix::Identity(); - vrb::Vector translation = vrb::Vector(aPlacement->translation.x() * kWorldDPIRatio, - aPlacement->translation.y() * kWorldDPIRatio, - aPlacement->translation.z() * kWorldDPIRatio); + vrb::Vector translation = vrb::Vector(aPlacement->translation.x() * WidgetPlacement::WORLD_DPI_RATIO, + aPlacement->translation.y() * WidgetPlacement::WORLD_DPI_RATIO, + aPlacement->translation.z() * WidgetPlacement::WORLD_DPI_RATIO); const float anchorX = (aPlacement->anchor.x() - 0.5f) * worldWidth; const float anchorY = (aPlacement->anchor.y() - 0.5f) * worldHeight; @@ -1353,6 +1407,16 @@ JNI_METHOD(void, finishWidgetResizeNative) crow::BrowserWorld::Instance().FinishWidgetResize(aHandle); } +JNI_METHOD(void, startWidgetMoveNative) +(JNIEnv*, jobject, jint aHandle, jint aMoveBehaviour) { + crow::BrowserWorld::Instance().StartWidgetMove(aHandle, aMoveBehaviour); +} + +JNI_METHOD(void, finishWidgetMoveNative) +(JNIEnv*, jobject) { + crow::BrowserWorld::Instance().FinishWidgetMove(); +} + JNI_METHOD(void, setWorldBrightnessNative) (JNIEnv*, jobject, jfloat aBrightness) { crow::BrowserWorld::Instance().SetBrightness(aBrightness); diff --git a/app/src/main/cpp/BrowserWorld.h b/app/src/main/cpp/BrowserWorld.h index 1aff0d145a..7cfebf3e11 100644 --- a/app/src/main/cpp/BrowserWorld.h +++ b/app/src/main/cpp/BrowserWorld.h @@ -48,6 +48,8 @@ class BrowserWorld { void RemoveWidget(int32_t aHandle); void StartWidgetResize(int32_t aHandle); void FinishWidgetResize(int32_t aHandle); + void StartWidgetMove(int32_t aHandle, const int32_t aMoveBehavour); + void FinishWidgetMove(); void UpdateVisibleWidgets(); void LayoutWidget(int32_t aHandle); void SetBrightness(const float aBrightness); diff --git a/app/src/main/cpp/Controller.cpp b/app/src/main/cpp/Controller.cpp index f1a2f9965c..654e51d721 100644 --- a/app/src/main/cpp/Controller.cpp +++ b/app/src/main/cpp/Controller.cpp @@ -33,6 +33,7 @@ Controller::operator=(const Controller& aController) { widget = aController.widget; pointerX = aController.pointerX; pointerY = aController.pointerY; + pointerWorldPoint = aController.pointerWorldPoint; buttonState = aController.buttonState; lastButtonState = aController.lastButtonState; touched = aController.touched; @@ -70,6 +71,7 @@ Controller::Reset() { focused = false; widget = 0; pointerX = pointerY = 0.0f; + pointerWorldPoint = vrb::Vector(0.0f, 0.0f, 0.0f); buttonState = lastButtonState = 0; touched = wasTouched = false; touchX = touchY = 0.0f; diff --git a/app/src/main/cpp/Controller.h b/app/src/main/cpp/Controller.h index a8ef4f15e5..ffcae5699c 100644 --- a/app/src/main/cpp/Controller.h +++ b/app/src/main/cpp/Controller.h @@ -26,6 +26,7 @@ struct Controller { uint32_t widget; float pointerX; float pointerY; + vrb::Vector pointerWorldPoint; uint32_t buttonState; uint32_t lastButtonState; bool touched; diff --git a/app/src/main/cpp/VRBrowser.cpp b/app/src/main/cpp/VRBrowser.cpp index 009164fba6..20ffd81c3a 100644 --- a/app/src/main/cpp/VRBrowser.cpp +++ b/app/src/main/cpp/VRBrowser.cpp @@ -24,6 +24,8 @@ const char* kHandleGestureName = "handleGesture"; const char* kHandleGestureSignature = "(I)V"; const char* kHandleResizeName = "handleResize"; const char* kHandleResizeSignature = "(IFF)V"; +const char* kHandleMoveEndName = "handleMoveEnd"; +const char* kHandleMoveEndSignature = "(IFFFF)V"; const char* kHandleBackEventName = "handleBack"; const char* kHandleBackEventSignature = "()V"; const char* kRegisterExternalContextName = "registerExternalContext"; @@ -61,6 +63,7 @@ jmethodID sHandleScrollEvent = nullptr; jmethodID sHandleAudioPose = nullptr; jmethodID sHandleGesture = nullptr; jmethodID sHandleResize = nullptr; +jmethodID sHandleMoveEnd = nullptr; jmethodID sHandleBack = nullptr; jmethodID sRegisterExternalContext = nullptr; jmethodID sPauseCompositor = nullptr; @@ -100,6 +103,7 @@ VRBrowser::InitializeJava(JNIEnv* aEnv, jobject aActivity) { sHandleAudioPose = FindJNIMethodID(sEnv, sBrowserClass, kHandleAudioPoseName, kHandleAudioPoseSignature); sHandleGesture = FindJNIMethodID(sEnv, sBrowserClass, kHandleGestureName, kHandleGestureSignature); sHandleResize = FindJNIMethodID(sEnv, sBrowserClass, kHandleResizeName, kHandleResizeSignature); + sHandleMoveEnd = FindJNIMethodID(sEnv, sBrowserClass, kHandleMoveEndName, kHandleMoveEndSignature); sHandleBack = FindJNIMethodID(sEnv, sBrowserClass, kHandleBackEventName, kHandleBackEventSignature); sRegisterExternalContext = FindJNIMethodID(sEnv, sBrowserClass, kRegisterExternalContextName, kRegisterExternalContextSignature); sPauseCompositor = FindJNIMethodID(sEnv, sBrowserClass, kPauseCompositorName, kPauseCompositorSignature); @@ -134,6 +138,7 @@ VRBrowser::ShutdownJava() { sHandleAudioPose = nullptr; sHandleGesture = nullptr; sHandleResize = nullptr; + sHandleMoveEnd = nullptr; sHandleBack = nullptr; sRegisterExternalContext = nullptr; sPauseCompositor = nullptr; @@ -204,6 +209,13 @@ VRBrowser::HandleResize(jint aWidgetHandle, jfloat aWorldWidth, jfloat aWorldHei CheckJNIException(sEnv, __FUNCTION__); } +void +VRBrowser::HandleMoveEnd(jint aWidgetHandle, jfloat aX, jfloat aY, jfloat aZ, jfloat aRotation) { + if (!ValidateMethodID(sEnv, sActivity, sHandleMoveEnd, __FUNCTION__)) { return; } + sEnv->CallVoidMethod(sActivity, sHandleMoveEnd, aWidgetHandle, aX, aY, aZ, aRotation); + CheckJNIException(sEnv, __FUNCTION__); +} + void VRBrowser::HandleBack() { if (!ValidateMethodID(sEnv, sActivity, sHandleBack, __FUNCTION__)) { return; } diff --git a/app/src/main/cpp/VRBrowser.h b/app/src/main/cpp/VRBrowser.h index 6e485a9f9a..49e74d0e18 100644 --- a/app/src/main/cpp/VRBrowser.h +++ b/app/src/main/cpp/VRBrowser.h @@ -25,6 +25,7 @@ void HandleScrollEvent(jint aWidgetHandle, jint aController, jfloat aX, jfloat a void HandleAudioPose(jfloat qx, jfloat qy, jfloat qz, jfloat qw, jfloat px, jfloat py, jfloat pz); void HandleGesture(jint aType); void HandleResize(jint aWidgetHandle, jfloat aWorldWidth, jfloat aWorldHeight); +void HandleMoveEnd(jint aWidgetHandle, jfloat aX, jfloat aY, jfloat aZ, jfloat aRotation); void HandleBack(); void RegisterExternalContext(jlong aContext); void PauseCompositor(); diff --git a/app/src/main/cpp/Widget.cpp b/app/src/main/cpp/Widget.cpp index 0d1bbdad83..e9eb77a024 100644 --- a/app/src/main/cpp/Widget.cpp +++ b/app/src/main/cpp/Widget.cpp @@ -294,9 +294,9 @@ Widget::ConvertToWidgetCoordinates(const vrb::Vector& point, float& aX, float& a } } -void -Widget::ConvertToWorldCoordinates(const vrb::Vector& aPoint, vrb::Vector& aResult) const { - aResult = m.transform->GetTransform().MultiplyPosition(aPoint); +vrb::Vector +Widget::ConvertToWorldCoordinates(const vrb::Vector& aLocalPoint) const { + return m.transform->GetTransform().MultiplyPosition(aLocalPoint); } const vrb::Matrix diff --git a/app/src/main/cpp/Widget.h b/app/src/main/cpp/Widget.h index 451d793e80..e30d4bf426 100644 --- a/app/src/main/cpp/Widget.h +++ b/app/src/main/cpp/Widget.h @@ -50,7 +50,8 @@ class Widget { void GetWorldSize(float& aWidth, float& aHeight) const; bool TestControllerIntersection(const vrb::Vector& aStartPoint, const vrb::Vector& aDirection, vrb::Vector& aResult, vrb::Vector& aNormal, bool& aIsInWidget, float& aDistance) const; void ConvertToWidgetCoordinates(const vrb::Vector& aPoint, float& aX, float& aY) const; - void ConvertToWorldCoordinates(const vrb::Vector& aPoint, vrb::Vector& aResult) const; + vrb::Vector ConvertToWorldCoordinates(const vrb::Vector& aLocalPoint) const; + vrb::Vector ConvertToWorldCoordinates(const float aWidgetX, const float aWidgetY) const; const vrb::Matrix GetTransform() const; void SetTransform(const vrb::Matrix& aTransform); void ToggleWidget(const bool aEnabled); diff --git a/app/src/main/cpp/WidgetMover.cpp b/app/src/main/cpp/WidgetMover.cpp new file mode 100644 index 0000000000..711672803a --- /dev/null +++ b/app/src/main/cpp/WidgetMover.cpp @@ -0,0 +1,151 @@ +/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * 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/. */ + +#include "WidgetMover.h" +#include "Widget.h" +#include "WidgetPlacement.h" +#include "VRBrowser.h" +#include "vrb/ConcreteClass.h" +#include "vrb/Matrix.h" + +namespace crow { + +// Should match the values defined in WidgetManagerDelegate.WidgetMoveBehaviourFlags +enum class WidgetMoveBehaviour { + GENERAL = 0, + KEYBOARD = 1 +}; + +struct WidgetMover::State { + WidgetPtr widget; + int attachedController; + vrb::Vector initialPoint; + vrb::Matrix initialTransform; + WidgetPlacementPtr initialPlacement; + WidgetPlacementPtr movePlacement; + WidgetMoveBehaviour moveBehaviour; + vrb::Vector endDelta; + float endRotation; + + State() + : widget(nullptr) + , attachedController(-1) + , moveBehaviour(WidgetMoveBehaviour::GENERAL) + , endRotation(0) + {} + + + WidgetPlacementPtr HandleKeyboardMove(const vrb::Vector& aDelta) { + float x = initialPlacement->translation.x() * WidgetPlacement::WORLD_DPI_RATIO; + float y = initialPlacement->translation.y() * WidgetPlacement::WORLD_DPI_RATIO; + const float maxX = 3.0f; + const float minX = -2.0f; + const float maxY = 2.0f; + const float minY = -1.0f; + const float maxAngle = -35.0f * (float)M_PI / 180.0f; + const float angleStartY = 0.8f; + const float minZ = -2.5f; + const float maxZ = -3.2f; + const float thresholdZ = 1.45f; + x += aDelta.x(); + y += aDelta.y(); + + x = fmin(x, maxX); + x = fmax(x, minX); + y = fmin(y, maxY); + y = fmax(y, minY); + + movePlacement->translation.x() = x / WidgetPlacement::WORLD_DPI_RATIO; + movePlacement->translation.y() = y / WidgetPlacement::WORLD_DPI_RATIO; + + float angle = 0.0f; + if (y < angleStartY) { + const float t = 1.0f - (y - minY) / (angleStartY - minY); + angle = t * maxAngle; + } + + float t = 0.0f; + if (y > 1.45f) { + t = 1.0f; + } else { + t = (y - minY) / (1.45f - minY); + } + + movePlacement->translation.z() = (minZ + t * (maxZ - minZ)) / WidgetPlacement::WORLD_DPI_RATIO; + movePlacement->rotation = angle; + endDelta = movePlacement->translation - initialPlacement->translation; + endRotation = angle; + + return movePlacement; + } +}; + +WidgetMoverPtr +WidgetMover::Create() { + return std::make_shared >(); +} + +bool +WidgetMover::IsMoving(const int aControllerIndex) const { + return m.widget != nullptr && m.attachedController == aControllerIndex; +} + +WidgetPlacementPtr +WidgetMover::HandleMove(const vrb::Vector& aStart, const vrb::Vector& aDirection) { + float hitDistance = -1; + vrb::Vector hitPoint; + vrb::Vector hitNormal; + bool isInWidget = false; + m.widget->TestControllerIntersection(aStart, aDirection, hitPoint, hitNormal, isInWidget, hitDistance); + if (hitDistance < 0) { + return nullptr; + }; + + + const vrb::Vector delta = hitPoint - m.initialPoint; + + if (m.moveBehaviour == WidgetMoveBehaviour::KEYBOARD) { + return m.HandleKeyboardMove(delta); + } else { + // General case + vrb::Matrix updatedTransform = m.initialTransform.Translate(vrb::Vector(delta.x(), delta.y(), 0.0f)); + m.widget->SetTransform(updatedTransform); + m.endDelta.x() = delta.x() / WidgetPlacement::WORLD_DPI_RATIO; + m.endDelta.y() = delta.y() / WidgetPlacement::WORLD_DPI_RATIO; + m.endDelta.z() = 0.0f; + m.endRotation = 0.0f; + return nullptr; + } +} + +void +WidgetMover::StartMoving(const WidgetPtr& aWidget, const int32_t aMoveBehaviour, const int32_t aControllerIndex, const vrb::Vector& hitPoint) { + m.widget = aWidget; + m.attachedController = aControllerIndex; + m.initialTransform = aWidget->GetTransform(); + m.initialPoint = hitPoint; + m.initialPlacement = aWidget->GetPlacement(); + m.movePlacement = WidgetPlacement::Create(*m.initialPlacement); + m.moveBehaviour = (WidgetMoveBehaviour) aMoveBehaviour; +} + +void +WidgetMover::EndMoving() { + m.attachedController = -1; + if (m.widget) { + VRBrowser::HandleMoveEnd(m.widget->GetHandle(), m.endDelta.x(), m.endDelta.y(), m.endDelta.z(), m.endRotation); + } + m.widget = nullptr; +} + +WidgetPtr +WidgetMover::GetWidget() const { + return m.widget; +} + +WidgetMover::WidgetMover(State& aState) : m(aState) { +} + +} // namespace crow diff --git a/app/src/main/cpp/WidgetMover.h b/app/src/main/cpp/WidgetMover.h new file mode 100644 index 0000000000..db5b1a29f0 --- /dev/null +++ b/app/src/main/cpp/WidgetMover.h @@ -0,0 +1,46 @@ +/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * 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/. */ + +#ifndef VRBROWSER_WIDGET_MOVER_DOT_H +#define VRBROWSER_WIDGET_MOVER_DOT_H + +#include "vrb/Forward.h" +#include "vrb/MacroUtils.h" + +#include +#include +#include +#include + +namespace crow { + +class Widget; +class WidgetPlacement; +class WidgetMover; +typedef std::shared_ptr WidgetPtr; +typedef std::shared_ptr WidgetPlacementPtr; +typedef std::shared_ptr WidgetMoverPtr; + +class WidgetMover { +public: + static WidgetMoverPtr Create(); + bool IsMoving(const int aControllerIndex) const; + WidgetPlacementPtr HandleMove(const vrb::Vector& aStart, const vrb::Vector& aDirection); + void StartMoving(const WidgetPtr& aWidget, const int32_t aMoveBehavour, const int32_t aControllerIndex, const vrb::Vector& hitPoint); + void EndMoving(); + WidgetPtr GetWidget() const; +protected: + struct State; + WidgetMover(State& aState); + ~WidgetMover() = default; +private: + State& m; + WidgetMover() = delete; + VRB_NO_DEFAULTS(WidgetMover) +}; + +} // namespace crow + +#endif // VRBROWSER_WIDGET_MOVER_DOT_H diff --git a/app/src/main/cpp/WidgetPlacement.cpp b/app/src/main/cpp/WidgetPlacement.cpp index f3ebaf29d8..3622aa794d 100644 --- a/app/src/main/cpp/WidgetPlacement.cpp +++ b/app/src/main/cpp/WidgetPlacement.cpp @@ -7,6 +7,8 @@ namespace crow { +const float WidgetPlacement::WORLD_DPI_RATIO = 2.0f/720.0f; + WidgetPlacementPtr WidgetPlacement::FromJava(JNIEnv* aEnv, jobject& aObject) { if (!aObject || !aEnv) { @@ -59,6 +61,11 @@ WidgetPlacement::FromJava(JNIEnv* aEnv, jobject& aObject) { return result; } +WidgetPlacementPtr +WidgetPlacement::Create(const WidgetPlacement& aPlacement) { + return WidgetPlacementPtr(new WidgetPlacement(aPlacement)); +} + int32_t WidgetPlacement::GetTextureWidth() const{ return (int32_t)ceilf(width * density * textureScale); diff --git a/app/src/main/cpp/WidgetPlacement.h b/app/src/main/cpp/WidgetPlacement.h index 7dba92fe5b..17ddeedf51 100644 --- a/app/src/main/cpp/WidgetPlacement.h +++ b/app/src/main/cpp/WidgetPlacement.h @@ -37,10 +37,12 @@ struct WidgetPlacement { int32_t GetTextureWidth() const; int32_t GetTextureHeight() const; + static const float WORLD_DPI_RATIO; static WidgetPlacementPtr FromJava(JNIEnv* aEnv, jobject& aObject); + static WidgetPlacementPtr Create(const WidgetPlacement& aPlacement); private: WidgetPlacement() = default; - VRB_NO_DEFAULTS(WidgetPlacement) + WidgetPlacement(const WidgetPlacement&) = default; }; } diff --git a/app/src/main/res/drawable/ic_icon_move.xml b/app/src/main/res/drawable/ic_icon_move.xml new file mode 100644 index 0000000000..a95c73c736 --- /dev/null +++ b/app/src/main/res/drawable/ic_icon_move.xml @@ -0,0 +1,8 @@ + + + + + + + diff --git a/app/src/main/res/layout/keyboard.xml b/app/src/main/res/layout/keyboard.xml index 8d8afcea03..e31ab1b53d 100644 --- a/app/src/main/res/layout/keyboard.xml +++ b/app/src/main/res/layout/keyboard.xml @@ -11,17 +11,35 @@ android:layout_height="@dimen/keyboard_height" android:layout_marginTop="0dp" android:orientation="horizontal"> - + + + + + settings_key_keyboard_locale settings_key_crash_restart_count settings_key_crash_restart_count_timestamp + settings_key_keyboard_move https://support.mozilla.org/kb/private-mode-firefox-reality settings_browser_world_width settings_browser_world_height