1 /* 2 * Copyright (C) 2023 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.example.android.vdmdemo.host; 18 19 import android.graphics.PointF; 20 import android.hardware.Sensor; 21 import android.hardware.SensorEvent; 22 import android.hardware.SensorEventListener; 23 import android.hardware.SensorManager; 24 import android.os.Bundle; 25 import android.view.GestureDetector; 26 import android.view.MotionEvent; 27 import android.view.View; 28 import android.widget.TextView; 29 30 import androidx.annotation.NonNull; 31 import androidx.annotation.StringRes; 32 import androidx.core.view.GestureDetectorCompat; 33 import androidx.fragment.app.Fragment; 34 35 import dagger.hilt.android.AndroidEntryPoint; 36 37 import javax.inject.Inject; 38 39 /** VDM Host Input fragment for mouse event injection. */ 40 @AndroidEntryPoint(Fragment.class) 41 public abstract class MouseFragment extends Hilt_MouseFragment { 42 43 @Inject 44 InputController mInputController; 45 46 private GestureDetectorCompat mDetector; 47 48 protected GestureListener mGestureListener = new GestureListener(); 49 protected int mNumFingers = 0; 50 51 MouseFragment()52 public MouseFragment() { 53 super(R.layout.fragment_input_mouse); 54 } 55 56 @Override onViewCreated(View view, Bundle savedInstanceState)57 public void onViewCreated(View view, Bundle savedInstanceState) { 58 mDetector = new GestureDetectorCompat(requireContext(), mGestureListener); 59 TextView touchpad = view.requireViewById(R.id.touchpad); 60 touchpad.setText(getViewTextResourceId()); 61 touchpad.setOnTouchListener((v, e) -> { 62 mNumFingers = e.getPointerCount(); 63 return mDetector.onTouchEvent(e); 64 }); 65 } 66 getViewTextResourceId()67 protected abstract @StringRes int getViewTextResourceId(); 68 69 protected class GestureListener extends GestureDetector.SimpleOnGestureListener { 70 71 private static final float SCROLL_THRESHOLD = 80f; 72 private float mScrollX = 0; 73 private float mScrollY = 0; 74 75 @Override onDown(@onNull MotionEvent e)76 public boolean onDown(@NonNull MotionEvent e) { 77 mScrollX = mScrollY = 0; 78 return true; 79 } 80 81 @Override onSingleTapUp(@onNull MotionEvent e)82 public boolean onSingleTapUp(@NonNull MotionEvent e) { 83 mInputController.sendMouseButtonEvent(MotionEvent.BUTTON_PRIMARY); 84 return true; 85 } 86 87 @Override onLongPress(@onNull MotionEvent e)88 public void onLongPress(@NonNull MotionEvent e) { 89 mInputController.sendMouseButtonEvent(MotionEvent.BUTTON_SECONDARY); 90 } 91 92 @Override onScroll( MotionEvent e1, @NonNull MotionEvent e2, float distanceX, float distanceY)93 public boolean onScroll( 94 MotionEvent e1, @NonNull MotionEvent e2, float distanceX, float distanceY) { 95 mScrollX += distanceX; 96 mScrollY += distanceY; 97 if (Math.abs(mScrollX) > SCROLL_THRESHOLD && Math.abs(distanceY) > SCROLL_THRESHOLD) { 98 mInputController.sendMouseScrollEvent(mScrollX, mScrollY); 99 mScrollX = mScrollY = 0; 100 } else if (Math.abs(mScrollX) > SCROLL_THRESHOLD) { 101 mInputController.sendMouseScrollEvent(mScrollX, 0); 102 mScrollX = 0; 103 } else if (Math.abs(mScrollY) > SCROLL_THRESHOLD) { 104 mInputController.sendMouseScrollEvent(0, mScrollY); 105 mScrollY = 0; 106 } 107 return true; 108 } 109 } 110 111 @AndroidEntryPoint(MouseFragment.class) 112 public static final class TouchpadFragment extends Hilt_MouseFragment_TouchpadFragment { 113 TouchpadFragment()114 public TouchpadFragment() { 115 mGestureListener = new GestureListener() { 116 @Override 117 public boolean onScroll( 118 MotionEvent e1, @NonNull MotionEvent e2, float distanceX, float distanceY) { 119 if (mNumFingers == 1) { 120 mInputController.sendMouseRelativeEvent(-distanceX, -distanceY); 121 } else if (mNumFingers == 2) { 122 return super.onScroll(e1, e2, distanceX, distanceY); 123 } 124 return true; 125 } 126 }; 127 } 128 129 @Override getViewTextResourceId()130 protected @StringRes int getViewTextResourceId() { 131 return R.string.touchpad_label; 132 } 133 } 134 135 136 @AndroidEntryPoint(MouseFragment.class) 137 public static final class RemoteFragment extends Hilt_MouseFragment_RemoteFragment { 138 139 private static final float SENSOR_EVENT_THRESHOLD = 0.04f; 140 private static final float SENSOR_EVENT_SCALE = 0.025f; 141 142 private SensorManager mSensorManager; 143 144 private final SensorEventListener mSensorEventListener = new SensorEventListener() { 145 @Override 146 public void onSensorChanged(SensorEvent event) { 147 float x = -event.values[2]; 148 float y = -event.values[0]; 149 PointF displaySize = 150 mInputController.getFocusedDisplaySize().orElse(new PointF(0, 0)); 151 if (Math.abs(x) > SENSOR_EVENT_THRESHOLD && Math.abs(y) > SENSOR_EVENT_THRESHOLD) { 152 x *= SENSOR_EVENT_SCALE * displaySize.x; 153 y *= SENSOR_EVENT_SCALE * displaySize.y; 154 mInputController.sendMouseRelativeEvent(x, y); 155 } 156 } 157 158 @Override 159 public void onAccuracyChanged(Sensor sensor, int accuracy) {} 160 }; 161 162 @Override onResume()163 public void onResume() { 164 super.onResume(); 165 mSensorManager = requireContext().getSystemService(SensorManager.class); 166 mSensorManager.registerListener( 167 mSensorEventListener, 168 mSensorManager.getDefaultSensor(Sensor.TYPE_GYROSCOPE), 169 SensorManager.SENSOR_DELAY_GAME); 170 } 171 172 @Override onPause()173 public void onPause() { 174 super.onPause(); 175 mSensorManager.unregisterListener(mSensorEventListener); 176 } 177 178 @Override getViewTextResourceId()179 protected @StringRes int getViewTextResourceId() { 180 return R.string.remote_control_label; 181 } 182 } 183 } 184