• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.os.Bundle;
20 import android.view.KeyEvent;
21 import android.view.MotionEvent;
22 import android.view.View;
23 import android.view.inputmethod.InputMethodManager;
24 
25 import androidx.appcompat.app.AppCompatActivity;
26 import androidx.appcompat.widget.Toolbar;
27 import androidx.fragment.app.Fragment;
28 import androidx.preference.PreferenceManager;
29 
30 import com.example.android.vdmdemo.common.DpadFragment;
31 import com.example.android.vdmdemo.common.NavTouchpadFragment;
32 import com.example.android.vdmdemo.common.RemoteEventProto.InputDeviceType;
33 import com.google.android.material.bottomnavigation.BottomNavigationView;
34 
35 import dagger.hilt.android.AndroidEntryPoint;
36 
37 import javax.inject.Inject;
38 
39 /** VDM Host Input activity allowing the host device to become an input device for the client. */
40 @AndroidEntryPoint(AppCompatActivity.class)
41 public class InputActivity extends Hilt_InputActivity {
42 
43     private static final String TAG = "VdmInputActivity";
44 
45     @Inject InputController mInputController;
46     @Inject PreferenceController mPreferenceController;
47 
48     boolean mOriginalShowPointerIconPreference;
49 
50     @Override
onCreate(Bundle savedInstanceState)51     public void onCreate(Bundle savedInstanceState) {
52         super.onCreate(savedInstanceState);
53 
54         setContentView(R.layout.activity_input);
55         Toolbar toolbar = requireViewById(R.id.main_tool_bar);
56         setSupportActionBar(toolbar);
57         toolbar.setNavigationOnClickListener(v -> finish());
58         setTitle(getTitle() + " " + getString(R.string.input));
59 
60         mOriginalShowPointerIconPreference =
61                 mPreferenceController.getBoolean(R.string.pref_show_pointer_icon);
62 
63         Fragment touchpadFragment = new MouseFragment.TouchpadFragment();
64         Fragment remoteFragment = new MouseFragment.RemoteFragment();
65         Fragment navigationFragment = new NavigationFragment();
66         Fragment stylusFragment = new StylusFragment();
67         Fragment keyboardFragment = new Fragment();
68 
69         BottomNavigationView bottomNavigationView = requireViewById(R.id.bottom_nav);
70         bottomNavigationView.setOnItemSelectedListener(item -> {
71             Fragment fragment;
72             switch (item.getItemId()) {
73                 case R.id.mouse -> {
74                     setShowPointerIcon(true);
75                     fragment = touchpadFragment;
76                 }
77                 case R.id.remote -> {
78                     setShowPointerIcon(true);
79                     fragment = remoteFragment;
80                 }
81                 case R.id.navigation -> {
82                     setShowPointerIcon(mOriginalShowPointerIconPreference);
83                     fragment = navigationFragment;
84                 }
85                 case R.id.stylus -> {
86                     setShowPointerIcon(mOriginalShowPointerIconPreference);
87                     fragment = stylusFragment;
88                 }
89                 case R.id.keyboard -> {
90                     setShowPointerIcon(mOriginalShowPointerIconPreference);
91                     fragment = keyboardFragment;
92                     getSystemService(InputMethodManager.class)
93                             .showSoftInput(getWindow().getDecorView(), 0);
94                 }
95                 default -> {
96                     return false;
97                 }
98             }
99             getSupportFragmentManager()
100                     .beginTransaction()
101                     .replace(R.id.input_fragment_container_view, fragment)
102                     .commit();
103             return true;
104         });
105 
106         if (!mPreferenceController.getBoolean(R.string.internal_pref_virtual_stylus_supported)) {
107             requireViewById(R.id.stylus).setVisibility(View.GONE);
108         }
109         bottomNavigationView.setSelectedItemId(R.id.mouse);
110 
111         requireViewById(R.id.button_back).setOnClickListener(
112                 v -> mInputController.sendMouseButtonEvent(MotionEvent.BUTTON_BACK));
113         requireViewById(R.id.button_forward).setOnClickListener(
114                 v -> mInputController.sendMouseButtonEvent(MotionEvent.BUTTON_FORWARD));
115         requireViewById(R.id.button_home).setOnClickListener(
116                 v -> mInputController.sendHomeToFocusedDisplay());
117     }
118 
setShowPointerIcon(boolean show)119     private void setShowPointerIcon(boolean show) {
120         PreferenceManager.getDefaultSharedPreferences(this)
121                 .edit()
122                 .putBoolean(getString(R.string.pref_show_pointer_icon), show)
123                 .commit();
124     }
125 
126     @Override
onDestroy()127     public void onDestroy() {
128         setShowPointerIcon(mOriginalShowPointerIconPreference);
129         super.onDestroy();
130     }
131 
132     @Override
dispatchKeyEvent(KeyEvent event)133     public boolean dispatchKeyEvent(KeyEvent event) {
134         if ((event.getFlags() & KeyEvent.FLAG_SOFT_KEYBOARD) == 0) {
135             return super.dispatchKeyEvent(event);
136         }
137         if (event.getAction() == KeyEvent.ACTION_UP
138                 && (event.getFlags() & KeyEvent.FLAG_CANCELED) == KeyEvent.FLAG_CANCELED) {
139             // Sending key events to another display makes that display top focused, so the key
140             // events sent to the activity here get dropped if not terminal.
141             // DisplayManager.VIRTUAL_DISPLAY_FLAG_STEAL_TOP_FOCUS_DISABLED would solve this but it
142             // has other limitations.
143             mInputController.sendEventToFocusedDisplay(
144                     InputDeviceType.DEVICE_TYPE_KEYBOARD,
145                     new KeyEvent(
146                             event.getDownTime(),
147                             event.getEventTime(),
148                             KeyEvent.ACTION_DOWN,
149                             event.getKeyCode(),
150                             /* repeat= */ 0));
151         }
152         mInputController.sendEventToFocusedDisplay(InputDeviceType.DEVICE_TYPE_KEYBOARD, event);
153         return true;
154     }
155 
156 
157     @AndroidEntryPoint(Fragment.class)
158     public static final class NavigationFragment extends Hilt_InputActivity_NavigationFragment {
159 
160         @Inject InputController mInputController;
161 
NavigationFragment()162         public NavigationFragment() {
163             super(R.layout.fragment_input_navigation);
164         }
165 
166         @Override
onViewCreated(View view, Bundle savedInstanceState)167         public void onViewCreated(View view, Bundle savedInstanceState) {
168             DpadFragment dpadFragment =
169                     (DpadFragment) getChildFragmentManager().findFragmentById(
170                             R.id.dpad_fragment_container);
171             dpadFragment.setInputEventListener((event) ->
172                     mInputController.sendEventToFocusedDisplay(
173                             InputDeviceType.DEVICE_TYPE_DPAD, event));
174             NavTouchpadFragment navTouchpadFragment =
175                     (NavTouchpadFragment) getChildFragmentManager().findFragmentById(
176                             R.id.nav_touchpad_fragment_container);
177             navTouchpadFragment.setInputEventListener((event) ->
178                     mInputController.sendEventToFocusedDisplay(
179                             InputDeviceType.DEVICE_TYPE_NAVIGATION_TOUCHPAD, event));
180         }
181     }
182 }
183