1 /* 2 * Copyright (C) 2024 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.annotation.SuppressLint; 20 import android.graphics.PointF; 21 import android.hardware.input.VirtualStylusButtonEvent; 22 import android.hardware.input.VirtualStylusMotionEvent; 23 import android.os.Bundle; 24 import android.view.MotionEvent; 25 import android.view.View; 26 import android.widget.TextView; 27 28 import androidx.fragment.app.Fragment; 29 30 import com.google.android.material.slider.Slider; 31 import com.google.android.material.switchmaterial.SwitchMaterial; 32 33 import dagger.hilt.android.AndroidEntryPoint; 34 35 import java.util.Optional; 36 37 import javax.inject.Inject; 38 39 40 /** VDM Host Input fragment for stylus event injection. */ 41 @AndroidEntryPoint(Fragment.class) 42 @SuppressLint("NewApi") 43 public final class StylusFragment extends Hilt_StylusFragment { 44 45 @Inject 46 InputController mInputController; 47 48 private int mTiltX = 0; 49 private int mTiltY = 0; 50 private boolean mHover = true; 51 private int mToolType = VirtualStylusMotionEvent.TOOL_TYPE_STYLUS; 52 StylusFragment()53 public StylusFragment() { 54 super(R.layout.fragment_input_stylus); 55 } 56 57 @Override onViewCreated(View view, Bundle savedInstanceState)58 public void onViewCreated(View view, Bundle savedInstanceState) { 59 SwitchMaterial hoverSwitch = view.requireViewById(R.id.hover); 60 hoverSwitch.setOnCheckedChangeListener((v, isChecked) -> mHover = isChecked); 61 62 SwitchMaterial eraserSwitch = view.requireViewById(R.id.eraser); 63 eraserSwitch.setOnCheckedChangeListener((v, isChecked) -> 64 mToolType = isChecked 65 ? VirtualStylusMotionEvent.TOOL_TYPE_ERASER 66 : VirtualStylusMotionEvent.TOOL_TYPE_STYLUS); 67 68 SwitchMaterial buttonPrimarySwitch = view.requireViewById(R.id.button_primary); 69 buttonPrimarySwitch.setOnCheckedChangeListener((v, isChecked) -> 70 sendButtonEvent( 71 isChecked 72 ? VirtualStylusButtonEvent.ACTION_BUTTON_PRESS 73 : VirtualStylusButtonEvent.ACTION_BUTTON_RELEASE, 74 VirtualStylusButtonEvent.BUTTON_PRIMARY)); 75 76 SwitchMaterial buttonSecondarySwitch = view.requireViewById(R.id.button_secondary); 77 buttonSecondarySwitch.setOnCheckedChangeListener((v, isChecked) -> 78 sendButtonEvent( 79 isChecked 80 ? VirtualStylusButtonEvent.ACTION_BUTTON_PRESS 81 : VirtualStylusButtonEvent.ACTION_BUTTON_RELEASE, 82 VirtualStylusButtonEvent.BUTTON_SECONDARY)); 83 84 Slider sliderX = view.requireViewById(R.id.tiltx); 85 sliderX.addOnChangeListener((slider, value, user) -> mTiltX = (int) value); 86 87 Slider sliderY = view.requireViewById(R.id.tilty); 88 sliderY.addOnChangeListener((slider, value, user) -> mTiltY = (int) value); 89 90 TextView touchpad = view.requireViewById(R.id.stylus); 91 touchpad.setOnTouchListener(this::sendMotionEvent); 92 } 93 sendMotionEvent(View view, MotionEvent e)94 private boolean sendMotionEvent(View view, MotionEvent e) { 95 int action = getVirtualStylusEventAction(e.getAction()); 96 Optional<PointF> displaySize = mInputController.getFocusedDisplaySize(); 97 if (action < 0 || !displaySize.isPresent()) { 98 return false; 99 } 100 // Scale the coordinates w.r.t. the display 101 int x = (int) (e.getX() / view.getWidth() * displaySize.get().x); 102 int y = (int) (e.getY() / view.getHeight() * displaySize.get().y); 103 VirtualStylusMotionEvent event = new VirtualStylusMotionEvent.Builder() 104 .setPressure(mHover ? 0 : ((int) (e.getPressure() * 255))) 105 .setX(x) 106 .setY(y) 107 .setTiltX(mTiltX) 108 .setTiltY(mTiltY) 109 .setToolType(mToolType) 110 .setAction(action) 111 .build(); 112 mInputController.sendStylusEventToFocusedDisplay(event); 113 return true; 114 } 115 sendButtonEvent(int action, int button)116 private void sendButtonEvent(int action, int button) { 117 VirtualStylusButtonEvent event = new VirtualStylusButtonEvent.Builder() 118 .setAction(action) 119 .setButtonCode(button) 120 .build(); 121 mInputController.sendStylusEventToFocusedDisplay(event); 122 } 123 getVirtualStylusEventAction(int action)124 private static int getVirtualStylusEventAction(int action) { 125 return switch (action) { 126 case MotionEvent.ACTION_POINTER_DOWN, MotionEvent.ACTION_DOWN -> 127 VirtualStylusMotionEvent.ACTION_DOWN; 128 case MotionEvent.ACTION_POINTER_UP, MotionEvent.ACTION_UP -> 129 VirtualStylusMotionEvent.ACTION_UP; 130 case MotionEvent.ACTION_MOVE -> VirtualStylusMotionEvent.ACTION_MOVE; 131 default -> -1; 132 }; 133 } 134 } 135