From 77af9f8ccc494c3ca57cac3dc72c2b23206d596c Mon Sep 17 00:00:00 2001 From: Felipe Erias Date: Fri, 19 Jan 2024 15:00:40 +0900 Subject: [PATCH] Clean up launch of Vision Glass Structure the code so it responds to changes in connected devices, USB permissions, etc. --- .../com/igalia/wolvic/PlatformActivity.java | 203 ++++++++++++------ 1 file changed, 132 insertions(+), 71 deletions(-) diff --git a/app/src/visionglass/java/com/igalia/wolvic/PlatformActivity.java b/app/src/visionglass/java/com/igalia/wolvic/PlatformActivity.java index 7b7f7c32e98..a7c6b33ba21 100644 --- a/app/src/visionglass/java/com/igalia/wolvic/PlatformActivity.java +++ b/app/src/visionglass/java/com/igalia/wolvic/PlatformActivity.java @@ -5,25 +5,21 @@ package com.igalia.wolvic; -import androidx.activity.ComponentActivity; - -import androidx.annotation.Keep; - +import android.annotation.SuppressLint; import android.app.Presentation; +import android.content.BroadcastReceiver; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; -import android.content.res.Resources; -import android.hardware.display.DisplayManager; +import android.content.IntentFilter; import android.hardware.Sensor; import android.hardware.SensorEvent; import android.hardware.SensorEventListener; import android.hardware.SensorManager; -import android.hardware.usb.UsbDevice; +import android.hardware.display.DisplayManager; import android.opengl.GLSurfaceView; import android.os.Build; import android.os.Bundle; -import android.util.DisplayMetrics; import android.util.Log; import android.view.Display; import android.view.KeyEvent; @@ -34,21 +30,28 @@ import android.view.WindowManager; import android.widget.Button; import android.widget.RelativeLayout; -import android.widget.TextView; + +import androidx.activity.ComponentActivity; +import androidx.annotation.Keep; +import androidx.annotation.NonNull; +import androidx.lifecycle.Lifecycle; import com.huawei.usblib.DisplayMode; import com.huawei.usblib.DisplayModeCallback; +import com.huawei.usblib.OnConnectionListener; import com.huawei.usblib.VisionGlass; import com.igalia.wolvic.utils.SystemUtils; import java.util.ArrayList; -import java.util.HashMap; import javax.microedition.khronos.egl.EGLConfig; import javax.microedition.khronos.opengles.GL10; -public class PlatformActivity extends ComponentActivity implements SensorEventListener { +public class PlatformActivity extends ComponentActivity implements SensorEventListener, OnConnectionListener { static String LOGTAG = SystemUtils.createLogtag(PlatformActivity.class); + + private boolean mWasImuStarted; + private boolean mIsAskingForPermission; private DisplayManager mDisplayManager; private Display mPresentationDisplay; private VisionGlassPresentation mActivePresentation; @@ -92,45 +95,96 @@ protected Intent getStoreIntent() { }; private final Runnable activityResumedRunnable = this::activityResumed; + private final BroadcastReceiver mUsbPermissionReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + Log.d(LOGTAG, "mUsbPermissionReceiver onReceive: " + intent.toString()); + + // debug + Log.d(LOGTAG, " Action: " + intent.getAction()); + Log.d(LOGTAG, " Data: " + intent.getData()); + if (intent.getExtras() != null) { + for (String key : intent.getExtras().keySet()) { + Log.d(LOGTAG, " Extra " + key + ": " + intent.getExtras().get(key)); + } + } + + Log.d(LOGTAG, "mUsbPermissionReceiver : mWasImuStarted = " + mWasImuStarted + " mIsAskingForPermission = " + mIsAskingForPermission); + initVisionGlass(); + } + }; + + @SuppressLint("UnspecifiedRegisterReceiverFlag") @Override protected void onCreate(Bundle savedInstanceState) { Log.d(LOGTAG, "PlatformActivity onCreate"); super.onCreate(savedInstanceState); + mDisplayManager = (DisplayManager) getSystemService(Context.DISPLAY_SERVICE); + mSensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE); + + IntentFilter usbPermissionFilter = new IntentFilter(); + usbPermissionFilter.addAction("com.huawei.usblib.USB_PERMISSION"); +// usbPermissionFilter.addAction("android.hardware.usb.action.USB_DEVICE_ATTACHED"); +// usbPermissionFilter.addAction("android.hardware.usb.action.USB_DEVICE_DETACHED"); + registerReceiver(mUsbPermissionReceiver, usbPermissionFilter); + VisionGlass.getInstance().init(getApplication()); + VisionGlass.getInstance().setOnConnectionListener(this); + initVisionGlass(); + } - mDisplayManager = (DisplayManager) getSystemService(Context.DISPLAY_SERVICE); - boolean wasImuStarted = false; - boolean isAskingForPermission = false; - do { - if (VisionGlass.getInstance().isConnected()) { - if (VisionGlass.getInstance().hasUsbPermission()) { - Log.d(LOGTAG, "Device has USB permission -> registering callback for startImu"); - wasImuStarted = true; - VisionGlass.getInstance().startImu((w, x, y, z) -> queueRunnable(() -> setHead(x, y, z, w))); - } else { - if (isAskingForPermission) { - try { - Thread.sleep(100); - } catch (InterruptedException e) { - e.printStackTrace(); - } - } else { - Log.w(LOGTAG, "Device does not have USB permission -> asking"); - VisionGlass.getInstance().requestUsbPermission(); - isAskingForPermission = true; - } - } - } else { - // TODO: show a dialog asking the user to put on the glasses - Log.e(LOGTAG, "Device not connected" + (VisionGlass.getInstance().hasUsbPermission() ? " and does NOT have USB permissions" : "")); + + @Override + public void onConnectionChange(boolean b) { + if (VisionGlass.getInstance().isConnected()) { + Log.d(LOGTAG, "onConnectionChange: Device connected"); + initVisionGlass(); + } else { + Log.d(LOGTAG, "onConnectionChange: Device disconnected"); + if (mWasImuStarted) { + Log.d(LOGTAG, "onConnectionChange: Finish activity"); + finish(); + } + } + } + + private void initVisionGlass() { + Log.d(LOGTAG, "initVisionGlass"); + + if (mWasImuStarted) { + Log.d(LOGTAG, "Duplicated call to init the Vision Glass system"); + updateDisplays(); + return; + } + + if (!VisionGlass.getInstance().isConnected()) { + Log.d(LOGTAG, "Glasses not connected yet"); + return; + } + + if (VisionGlass.getInstance().hasUsbPermission()) { + mIsAskingForPermission = false; + } else { + if (!mIsAskingForPermission) { + Log.d(LOGTAG, "Asking for USB permission"); + mIsAskingForPermission = true; + VisionGlass.getInstance().requestUsbPermission(); } - } while (!wasImuStarted); + return; + } + + Log.d(LOGTAG, "Start IMU"); + mWasImuStarted = true; + VisionGlass.getInstance().startImu((w, x, y, z) -> queueRunnable(() -> setHead(x, y, z, w))); VisionGlass.getInstance().setDisplayMode(DisplayMode.vr3d, new DisplayModeCallback() { @Override - public void onSuccess(DisplayMode displayMode) { Log.d(LOGTAG, "Successfully switched to 3D mode"); } + public void onSuccess(DisplayMode displayMode) { + Log.d(LOGTAG, "Successfully switched to 3D mode"); + updateDisplays(); + } @Override public void onError(String s, int i) { @@ -138,8 +192,6 @@ public void onError(String s, int i) { } }); - mSensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE); - setContentView(R.layout.visionglass_layout); View touchpad = findViewById(R.id.touchpad); @@ -148,7 +200,7 @@ public void onError(String s, int i) { layoutParams.height = getResources().getDisplayMetrics().widthPixels; touchpad.setLayoutParams(layoutParams); - touchpad.setOnClickListener((View.OnClickListener) v -> { + touchpad.setOnClickListener(v -> { // We don't really need the coordinates of the click because we use the position // of the aim in the 3D environment. queueRunnable(() -> touchEvent(false, 0, 0)); @@ -178,22 +230,19 @@ public void onError(String s, int i) { Button homeButton = findViewById(R.id.home_button); // Set click listeners for the buttons - backButton.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - onBackPressed(); - } - }); + backButton.setOnClickListener(v -> onBackPressed()); - homeButton.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - // Handle home button click - Log.d(LOGTAG, "Home button clicked"); - } + homeButton.setOnClickListener(v -> { + // Handle home button click + Log.d(LOGTAG, "Home button clicked"); }); getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); + + // Show the app + if (getLifecycle().getCurrentState() == Lifecycle.State.RESUMED) { + updateDisplays(); + } } // SensorEventListener overrides @@ -236,12 +285,16 @@ public boolean onGenericMotionEvent(MotionEvent aEvent) { protected void onPause() { Log.d(LOGTAG, "PlatformActivity onPause"); super.onPause(); - synchronized (mRenderLock) { - queueRunnable(activityPausedRunnable); - try { - mRenderLock.wait(); - } catch(InterruptedException e) { - Log.e(LOGTAG, "activityPausedRunnable interrupted: " + e.toString()); + + // TODO This check is needed to prevent a crash when pausing before 3D mode has started. + if (mWasImuStarted) { + synchronized (mRenderLock) { + queueRunnable(activityPausedRunnable); + try { + mRenderLock.wait(); + } catch(InterruptedException e) { + Log.e(LOGTAG, "activityPausedRunnable interrupted: " + e); + } } } if (mActivePresentation != null) @@ -258,7 +311,6 @@ protected void onResume() { super.onResume(); updateDisplays(); - showPresentation(); if (mActivePresentation != null && mActivePresentation.mGLView != null) mActivePresentation.mGLView.onResume(); @@ -275,12 +327,14 @@ protected void onResume() { protected void onDestroy() { Log.d(LOGTAG, "PlatformActivity onDestroy"); super.onDestroy(); + unregisterReceiver(mUsbPermissionReceiver); synchronized (mRenderLock) { - queueRunnable(activityDestroyedRunnable); - try { - mRenderLock.wait(); - } catch(InterruptedException e) { - Log.e(LOGTAG, "activityDestroyedRunnable interrupted: " + e.toString()); + if (queueRunnable(activityDestroyedRunnable)) { + try { + mRenderLock.wait(); + } catch (InterruptedException e) { + Log.e(LOGTAG, "activityDestroyedRunnable interrupted: " + e.toString()); + } } } } @@ -306,17 +360,20 @@ void setImmersiveSticky() { } } - void queueRunnable(Runnable aRunnable) { + boolean queueRunnable(@NonNull Runnable aRunnable) { if (mActivePresentation != null) { mActivePresentation.mGLView.queueEvent(aRunnable); + return true; } else { synchronized (mPendingEvents) { mPendingEvents.add(aRunnable); } if (mActivePresentation != null) { notifyPendingEvents(); + return true; } } + return false; } private void notifyPendingEvents() { @@ -332,26 +389,33 @@ private void updateDisplays() { Display[] displays = mDisplayManager.getDisplays(DisplayManager.DISPLAY_CATEGORY_PRESENTATION); if (displays.length == 0) { mPresentationDisplay = null; + Log.d(LOGTAG, "updateDisplays: could not find a Presentation display"); return; } + Log.d(LOGTAG, "updateDisplays: found Presentation display"); mPresentationDisplay = displays[0]; + + runOnUiThread(this::showPresentation); } private final DisplayManager.DisplayListener mDisplayListener = new DisplayManager.DisplayListener() { @Override public void onDisplayAdded(int displayId) { + Log.d(LOGTAG, "display listener: onDisplayAdded"); updateDisplays(); } @Override public void onDisplayChanged(int displayId) { + Log.d(LOGTAG, "display listener: onDisplayChanged"); updateDisplays(); } @Override public void onDisplayRemoved(int displayId) { + Log.d(LOGTAG, "display listener: onDisplayRemoved"); updateDisplays(); } }; @@ -392,6 +456,7 @@ public VisionGlassPresentation(Context context, Display display) { * Sets the preferred display mode id for the presentation. */ public void setPreferredDisplayMode(int modeId) { + Log.d(LOGTAG, "VisionGlassPresentation setPreferredDisplayMode: " + modeId); WindowManager.LayoutParams params = getWindow().getAttributes(); params.preferredDisplayModeId = modeId; getWindow().setAttributes(params); @@ -402,10 +467,6 @@ protected void onCreate(Bundle savedInstanceState) { // Be sure to call the super class. super.onCreate(savedInstanceState); - // Get the resources for the context of the presentation. - // Notice that we are getting the resources from the context of the presentation. - Resources r = getContext().getResources(); - // Inflate the layout. setContentView(R.layout.visionglass_presentation_layout); @@ -437,7 +498,7 @@ public void onDrawFrame(GL10 gl) { @Keep @SuppressWarnings("unused") private void setRenderMode(final int aMode) { - runOnUiThread(() -> setImmersiveSticky()); + runOnUiThread(this::setImmersiveSticky); } private native void activityCreated(Object aAssetManager);