| package org.libsdl.app; |
| |
| |
| import android.content.Context; |
| import android.content.pm.ActivityInfo; |
| import android.hardware.Sensor; |
| import android.hardware.SensorEvent; |
| import android.hardware.SensorEventListener; |
| import android.hardware.SensorManager; |
| import android.os.Build; |
| import android.util.DisplayMetrics; |
| import android.util.Log; |
| import android.view.Display; |
| import android.view.InputDevice; |
| import android.view.KeyEvent; |
| import android.view.MotionEvent; |
| import android.view.Surface; |
| import android.view.SurfaceHolder; |
| import android.view.SurfaceView; |
| import android.view.View; |
| import android.view.WindowManager; |
| |
| |
| /** |
| SDLSurface. This is what we draw on, so we need to know when it's created |
| in order to do anything useful. |
| |
| Because of this, that's where we set up the SDL thread |
| */ |
| public class SDLSurface extends SurfaceView implements SurfaceHolder.Callback, |
| View.OnKeyListener, View.OnTouchListener, SensorEventListener { |
| |
| // Sensors |
| protected SensorManager mSensorManager; |
| protected Display mDisplay; |
| |
| // Keep track of the surface size to normalize touch events |
| protected float mWidth, mHeight; |
| |
| // Is SurfaceView ready for rendering |
| public boolean mIsSurfaceReady; |
| |
| // Startup |
| public SDLSurface(Context context) { |
| super(context); |
| getHolder().addCallback(this); |
| |
| setFocusable(true); |
| setFocusableInTouchMode(true); |
| requestFocus(); |
| setOnKeyListener(this); |
| setOnTouchListener(this); |
| |
| mDisplay = ((WindowManager)context.getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay(); |
| mSensorManager = (SensorManager)context.getSystemService(Context.SENSOR_SERVICE); |
| |
| setOnGenericMotionListener(SDLActivity.getMotionListener()); |
| |
| // Some arbitrary defaults to avoid a potential division by zero |
| mWidth = 1.0f; |
| mHeight = 1.0f; |
| |
| mIsSurfaceReady = false; |
| } |
| |
| public void handlePause() { |
| enableSensor(Sensor.TYPE_ACCELEROMETER, false); |
| } |
| |
| public void handleResume() { |
| setFocusable(true); |
| setFocusableInTouchMode(true); |
| requestFocus(); |
| setOnKeyListener(this); |
| setOnTouchListener(this); |
| enableSensor(Sensor.TYPE_ACCELEROMETER, true); |
| } |
| |
| public Surface getNativeSurface() { |
| return getHolder().getSurface(); |
| } |
| |
| // Called when we have a valid drawing surface |
| @Override |
| public void surfaceCreated(SurfaceHolder holder) { |
| Log.v("SDL", "surfaceCreated()"); |
| SDLActivity.onNativeSurfaceCreated(); |
| } |
| |
| // Called when we lose the surface |
| @Override |
| public void surfaceDestroyed(SurfaceHolder holder) { |
| Log.v("SDL", "surfaceDestroyed()"); |
| |
| // Transition to pause, if needed |
| SDLActivity.mNextNativeState = SDLActivity.NativeState.PAUSED; |
| SDLActivity.handleNativeState(); |
| |
| mIsSurfaceReady = false; |
| SDLActivity.onNativeSurfaceDestroyed(); |
| } |
| |
| // Called when the surface is resized |
| @Override |
| public void surfaceChanged(SurfaceHolder holder, |
| int format, int width, int height) { |
| Log.v("SDL", "surfaceChanged()"); |
| |
| if (SDLActivity.mSingleton == null) { |
| return; |
| } |
| |
| mWidth = width; |
| mHeight = height; |
| int nDeviceWidth = width; |
| int nDeviceHeight = height; |
| try |
| { |
| if (Build.VERSION.SDK_INT >= 17) { |
| DisplayMetrics realMetrics = new DisplayMetrics(); |
| mDisplay.getRealMetrics( realMetrics ); |
| nDeviceWidth = realMetrics.widthPixels; |
| nDeviceHeight = realMetrics.heightPixels; |
| } |
| } catch(Exception ignored) { |
| } |
| |
| synchronized(SDLActivity.getContext()) { |
| // In case we're waiting on a size change after going fullscreen, send a notification. |
| SDLActivity.getContext().notifyAll(); |
| } |
| |
| Log.v("SDL", "Window size: " + width + "x" + height); |
| Log.v("SDL", "Device size: " + nDeviceWidth + "x" + nDeviceHeight); |
| SDLActivity.nativeSetScreenResolution(width, height, nDeviceWidth, nDeviceHeight, mDisplay.getRefreshRate()); |
| SDLActivity.onNativeResize(); |
| |
| // Prevent a screen distortion glitch, |
| // for instance when the device is in Landscape and a Portrait App is resumed. |
| boolean skip = false; |
| int requestedOrientation = SDLActivity.mSingleton.getRequestedOrientation(); |
| |
| if (requestedOrientation == ActivityInfo.SCREEN_ORIENTATION_PORTRAIT || requestedOrientation == ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT) { |
| if (mWidth > mHeight) { |
| skip = true; |
| } |
| } else if (requestedOrientation == ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE || requestedOrientation == ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE) { |
| if (mWidth < mHeight) { |
| skip = true; |
| } |
| } |
| |
| // Special Patch for Square Resolution: Black Berry Passport |
| if (skip) { |
| double min = Math.min(mWidth, mHeight); |
| double max = Math.max(mWidth, mHeight); |
| |
| if (max / min < 1.20) { |
| Log.v("SDL", "Don't skip on such aspect-ratio. Could be a square resolution."); |
| skip = false; |
| } |
| } |
| |
| // Don't skip in MultiWindow. |
| if (skip) { |
| if (Build.VERSION.SDK_INT >= 24) { |
| if (SDLActivity.mSingleton.isInMultiWindowMode()) { |
| Log.v("SDL", "Don't skip in Multi-Window"); |
| skip = false; |
| } |
| } |
| } |
| |
| if (skip) { |
| Log.v("SDL", "Skip .. Surface is not ready."); |
| mIsSurfaceReady = false; |
| return; |
| } |
| |
| /* If the surface has been previously destroyed by onNativeSurfaceDestroyed, recreate it here */ |
| SDLActivity.onNativeSurfaceChanged(); |
| |
| /* Surface is ready */ |
| mIsSurfaceReady = true; |
| |
| SDLActivity.mNextNativeState = SDLActivity.NativeState.RESUMED; |
| SDLActivity.handleNativeState(); |
| } |
| |
| // Key events |
| @Override |
| public boolean onKey(View v, int keyCode, KeyEvent event) { |
| return SDLActivity.handleKeyEvent(v, keyCode, event, null); |
| } |
| |
| // Touch events |
| @Override |
| public boolean onTouch(View v, MotionEvent event) { |
| /* Ref: http://developer.android.com/training/gestures/multi.html */ |
| int touchDevId = event.getDeviceId(); |
| final int pointerCount = event.getPointerCount(); |
| int action = event.getActionMasked(); |
| int pointerFingerId; |
| int i = -1; |
| float x,y,p; |
| |
| /* |
| * Prevent id to be -1, since it's used in SDL internal for synthetic events |
| * Appears when using Android emulator, eg: |
| * adb shell input mouse tap 100 100 |
| * adb shell input touchscreen tap 100 100 |
| */ |
| if (touchDevId < 0) { |
| touchDevId -= 1; |
| } |
| |
| // 12290 = Samsung DeX mode desktop mouse |
| // 12290 = 0x3002 = 0x2002 | 0x1002 = SOURCE_MOUSE | SOURCE_TOUCHSCREEN |
| // 0x2 = SOURCE_CLASS_POINTER |
| if (event.getSource() == InputDevice.SOURCE_MOUSE || event.getSource() == (InputDevice.SOURCE_MOUSE | InputDevice.SOURCE_TOUCHSCREEN)) { |
| int mouseButton = 1; |
| try { |
| Object object = event.getClass().getMethod("getButtonState").invoke(event); |
| if (object != null) { |
| mouseButton = (Integer) object; |
| } |
| } catch(Exception ignored) { |
| } |
| |
| // We need to check if we're in relative mouse mode and get the axis offset rather than the x/y values |
| // if we are. We'll leverage our existing mouse motion listener |
| SDLGenericMotionListener_API12 motionListener = SDLActivity.getMotionListener(); |
| x = motionListener.getEventX(event); |
| y = motionListener.getEventY(event); |
| |
| SDLActivity.onNativeMouse(mouseButton, action, x, y, motionListener.inRelativeMode()); |
| } else { |
| switch(action) { |
| case MotionEvent.ACTION_MOVE: |
| for (i = 0; i < pointerCount; i++) { |
| pointerFingerId = event.getPointerId(i); |
| x = event.getX(i) / mWidth; |
| y = event.getY(i) / mHeight; |
| p = event.getPressure(i); |
| if (p > 1.0f) { |
| // may be larger than 1.0f on some devices |
| // see the documentation of getPressure(i) |
| p = 1.0f; |
| } |
| SDLActivity.onNativeTouch(touchDevId, pointerFingerId, action, x, y, p); |
| } |
| break; |
| |
| case MotionEvent.ACTION_UP: |
| case MotionEvent.ACTION_DOWN: |
| // Primary pointer up/down, the index is always zero |
| i = 0; |
| /* fallthrough */ |
| case MotionEvent.ACTION_POINTER_UP: |
| case MotionEvent.ACTION_POINTER_DOWN: |
| // Non primary pointer up/down |
| if (i == -1) { |
| i = event.getActionIndex(); |
| } |
| |
| pointerFingerId = event.getPointerId(i); |
| x = event.getX(i) / mWidth; |
| y = event.getY(i) / mHeight; |
| p = event.getPressure(i); |
| if (p > 1.0f) { |
| // may be larger than 1.0f on some devices |
| // see the documentation of getPressure(i) |
| p = 1.0f; |
| } |
| SDLActivity.onNativeTouch(touchDevId, pointerFingerId, action, x, y, p); |
| break; |
| |
| case MotionEvent.ACTION_CANCEL: |
| for (i = 0; i < pointerCount; i++) { |
| pointerFingerId = event.getPointerId(i); |
| x = event.getX(i) / mWidth; |
| y = event.getY(i) / mHeight; |
| p = event.getPressure(i); |
| if (p > 1.0f) { |
| // may be larger than 1.0f on some devices |
| // see the documentation of getPressure(i) |
| p = 1.0f; |
| } |
| SDLActivity.onNativeTouch(touchDevId, pointerFingerId, MotionEvent.ACTION_UP, x, y, p); |
| } |
| break; |
| |
| default: |
| break; |
| } |
| } |
| |
| return true; |
| } |
| |
| // Sensor events |
| public void enableSensor(int sensortype, boolean enabled) { |
| // TODO: This uses getDefaultSensor - what if we have >1 accels? |
| if (enabled) { |
| mSensorManager.registerListener(this, |
| mSensorManager.getDefaultSensor(sensortype), |
| SensorManager.SENSOR_DELAY_GAME, null); |
| } else { |
| mSensorManager.unregisterListener(this, |
| mSensorManager.getDefaultSensor(sensortype)); |
| } |
| } |
| |
| @Override |
| public void onAccuracyChanged(Sensor sensor, int accuracy) { |
| // TODO |
| } |
| |
| @Override |
| public void onSensorChanged(SensorEvent event) { |
| if (event.sensor.getType() == Sensor.TYPE_ACCELEROMETER) { |
| |
| // Since we may have an orientation set, we won't receive onConfigurationChanged events. |
| // We thus should check here. |
| int newOrientation; |
| |
| float x, y; |
| switch (mDisplay.getRotation()) { |
| case Surface.ROTATION_90: |
| x = -event.values[1]; |
| y = event.values[0]; |
| newOrientation = SDLActivity.SDL_ORIENTATION_LANDSCAPE; |
| break; |
| case Surface.ROTATION_270: |
| x = event.values[1]; |
| y = -event.values[0]; |
| newOrientation = SDLActivity.SDL_ORIENTATION_LANDSCAPE_FLIPPED; |
| break; |
| case Surface.ROTATION_180: |
| x = -event.values[0]; |
| y = -event.values[1]; |
| newOrientation = SDLActivity.SDL_ORIENTATION_PORTRAIT_FLIPPED; |
| break; |
| case Surface.ROTATION_0: |
| default: |
| x = event.values[0]; |
| y = event.values[1]; |
| newOrientation = SDLActivity.SDL_ORIENTATION_PORTRAIT; |
| break; |
| } |
| |
| if (newOrientation != SDLActivity.mCurrentOrientation) { |
| SDLActivity.mCurrentOrientation = newOrientation; |
| SDLActivity.onNativeOrientationChanged(newOrientation); |
| } |
| |
| SDLActivity.onNativeAccel(-x / SensorManager.GRAVITY_EARTH, |
| y / SensorManager.GRAVITY_EARTH, |
| event.values[2] / SensorManager.GRAVITY_EARTH); |
| |
| |
| } |
| } |
| |
| // Captured pointer events for API 26. |
| public boolean onCapturedPointerEvent(MotionEvent event) |
| { |
| int action = event.getActionMasked(); |
| |
| float x, y; |
| switch (action) { |
| case MotionEvent.ACTION_SCROLL: |
| x = event.getAxisValue(MotionEvent.AXIS_HSCROLL, 0); |
| y = event.getAxisValue(MotionEvent.AXIS_VSCROLL, 0); |
| SDLActivity.onNativeMouse(0, action, x, y, false); |
| return true; |
| |
| case MotionEvent.ACTION_HOVER_MOVE: |
| case MotionEvent.ACTION_MOVE: |
| x = event.getX(0); |
| y = event.getY(0); |
| SDLActivity.onNativeMouse(0, action, x, y, true); |
| return true; |
| |
| case MotionEvent.ACTION_BUTTON_PRESS: |
| case MotionEvent.ACTION_BUTTON_RELEASE: |
| |
| // Change our action value to what SDL's code expects. |
| if (action == MotionEvent.ACTION_BUTTON_PRESS) { |
| action = MotionEvent.ACTION_DOWN; |
| } else { /* MotionEvent.ACTION_BUTTON_RELEASE */ |
| action = MotionEvent.ACTION_UP; |
| } |
| |
| x = event.getX(0); |
| y = event.getY(0); |
| int button = event.getButtonState(); |
| |
| SDLActivity.onNativeMouse(button, action, x, y, true); |
| return true; |
| } |
| |
| return false; |
| } |
| } |