• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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