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.input.VirtualMouseButtonEvent; 21 import android.hardware.input.VirtualMouseRelativeEvent; 22 import android.hardware.input.VirtualMouseScrollEvent; 23 import android.util.Log; 24 import android.view.Display; 25 import android.view.InputEvent; 26 import android.view.MotionEvent; 27 28 import androidx.annotation.GuardedBy; 29 30 import com.example.android.vdmdemo.common.RemoteEventProto.InputDeviceType; 31 32 import java.util.Optional; 33 34 import javax.inject.Inject; 35 import javax.inject.Singleton; 36 37 @Singleton 38 final class InputController { 39 40 private static final String TAG = "InputController"; 41 42 @Inject DisplayRepository mDisplayRepository; 43 44 private final Object mLock = new Object(); 45 46 @GuardedBy("mLock") 47 private int mFocusedRemoteDisplayId = Display.INVALID_DISPLAY; 48 49 @GuardedBy("mLock") 50 private RemoteDisplay mFocusedDisplay; 51 52 @Inject InputController()53 InputController() {} 54 setFocusedRemoteDisplayId(int remoteDisplayId)55 void setFocusedRemoteDisplayId(int remoteDisplayId) { 56 synchronized (mLock) { 57 mFocusedRemoteDisplayId = remoteDisplayId; 58 mFocusedDisplay = 59 mDisplayRepository.getDisplayByRemoteId(remoteDisplayId).orElse(null); 60 } 61 } 62 sendEventToFocusedDisplay(InputDeviceType deviceType, InputEvent inputEvent)63 void sendEventToFocusedDisplay(InputDeviceType deviceType, InputEvent inputEvent) { 64 synchronized (mLock) { 65 getFocusedDisplayLocked().ifPresent(d -> d.processInputEvent(deviceType, inputEvent)); 66 } 67 } 68 sendHomeToFocusedDisplay()69 void sendHomeToFocusedDisplay() { 70 synchronized (mLock) { 71 getFocusedDisplayLocked().ifPresent(d -> d.goHome()); 72 } 73 } 74 sendMouseButtonEvent(int button)75 void sendMouseButtonEvent(int button) { 76 for (int action : new int[]{ 77 MotionEvent.ACTION_BUTTON_PRESS, MotionEvent.ACTION_BUTTON_RELEASE}) { 78 sendMouseEventToFocusedDisplay( 79 new VirtualMouseButtonEvent.Builder() 80 .setButtonCode(button) 81 .setAction(action) 82 .build()); 83 } 84 } 85 sendMouseRelativeEvent(float x, float y)86 void sendMouseRelativeEvent(float x, float y) { 87 sendMouseEventToFocusedDisplay( 88 new VirtualMouseRelativeEvent.Builder() 89 .setRelativeX(x) 90 .setRelativeY(y) 91 .build()); 92 } 93 sendMouseScrollEvent(float x, float y)94 void sendMouseScrollEvent(float x, float y) { 95 sendMouseEventToFocusedDisplay( 96 new VirtualMouseScrollEvent.Builder() 97 .setXAxisMovement(clampMouseScroll(x)) 98 .setYAxisMovement(clampMouseScroll(y)) 99 .build()); 100 } 101 sendMouseEventToFocusedDisplay(Object mouseEvent)102 private void sendMouseEventToFocusedDisplay(Object mouseEvent) { 103 synchronized (mLock) { 104 getFocusedDisplayLocked().ifPresent(d -> d.processVirtualMouseEvent(mouseEvent)); 105 } 106 } 107 sendStylusEventToFocusedDisplay(Object stylusEvent)108 void sendStylusEventToFocusedDisplay(Object stylusEvent) { 109 synchronized (mLock) { 110 getFocusedDisplayLocked().ifPresent(d -> d.processVirtualStylusEvent(stylusEvent)); 111 } 112 } 113 getFocusedDisplaySize()114 Optional<PointF> getFocusedDisplaySize() { 115 synchronized (mLock) { 116 Optional<RemoteDisplay> display = getFocusedDisplayLocked(); 117 return display.isPresent() 118 ? Optional.of(display.get().getDisplaySize()) 119 : Optional.empty(); 120 } 121 } 122 123 @GuardedBy("mLock") getFocusedDisplayLocked()124 private Optional<RemoteDisplay> getFocusedDisplayLocked() { 125 synchronized (mLock) { 126 if (mFocusedDisplay == null) { 127 mFocusedDisplay = mDisplayRepository.getDisplayByRemoteId(mFocusedRemoteDisplayId) 128 .orElse(null); 129 if (mFocusedDisplay == null) { 130 Log.e(TAG, "Failed to inject input event, no focused display " 131 + mFocusedRemoteDisplayId); 132 return Optional.empty(); 133 } 134 } 135 return Optional.of(mFocusedDisplay); 136 } 137 } 138 clampMouseScroll(float val)139 static float clampMouseScroll(float val) { 140 return Math.max(Math.min(val, 1f), -1f); 141 } 142 } 143