1a28d7b201584823286f26e21a9f30d07c416eb3eKeun-young Park/*
2a28d7b201584823286f26e21a9f30d07c416eb3eKeun-young Park * Copyright (C) 2016 The Android Open Source Project
3a28d7b201584823286f26e21a9f30d07c416eb3eKeun-young Park *
4a28d7b201584823286f26e21a9f30d07c416eb3eKeun-young Park * Licensed under the Apache License, Version 2.0 (the "License");
5a28d7b201584823286f26e21a9f30d07c416eb3eKeun-young Park * you may not use this file except in compliance with the License.
6a28d7b201584823286f26e21a9f30d07c416eb3eKeun-young Park * You may obtain a copy of the License at
7a28d7b201584823286f26e21a9f30d07c416eb3eKeun-young Park *
8a28d7b201584823286f26e21a9f30d07c416eb3eKeun-young Park *      http://www.apache.org/licenses/LICENSE-2.0
9a28d7b201584823286f26e21a9f30d07c416eb3eKeun-young Park *
10a28d7b201584823286f26e21a9f30d07c416eb3eKeun-young Park * Unless required by applicable law or agreed to in writing, software
11a28d7b201584823286f26e21a9f30d07c416eb3eKeun-young Park * distributed under the License is distributed on an "AS IS" BASIS,
12a28d7b201584823286f26e21a9f30d07c416eb3eKeun-young Park * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13a28d7b201584823286f26e21a9f30d07c416eb3eKeun-young Park * See the License for the specific language governing permissions and
14a28d7b201584823286f26e21a9f30d07c416eb3eKeun-young Park * limitations under the License.
15a28d7b201584823286f26e21a9f30d07c416eb3eKeun-young Park */
16a28d7b201584823286f26e21a9f30d07c416eb3eKeun-young Parkpackage com.google.android.car.kitchensink.input;
17a28d7b201584823286f26e21a9f30d07c416eb3eKeun-young Park
189bc6ec968099c014c16c57ceee963e4cf5eef1c0Enrico Granataimport static android.hardware.automotive.vehicle.V2_0.SubscribeFlags.EVENTS_FROM_ANDROID;
199bc6ec968099c014c16c57ceee963e4cf5eef1c0Enrico Granataimport static android.hardware.automotive.vehicle.V2_0.SubscribeFlags.EVENTS_FROM_CAR;
2031966496485e3738e6bcfe775885f5cd9b857b22Pavel Maltsevimport static android.hardware.automotive.vehicle.V2_0.VehicleDisplay.INSTRUMENT_CLUSTER;
219bc6ec968099c014c16c57ceee963e4cf5eef1c0Enrico Granataimport static android.hardware.automotive.vehicle.V2_0.VehicleHwKeyInputAction.ACTION_DOWN;
229bc6ec968099c014c16c57ceee963e4cf5eef1c0Enrico Granataimport static android.hardware.automotive.vehicle.V2_0.VehicleProperty.HW_KEY_INPUT;
2331966496485e3738e6bcfe775885f5cd9b857b22Pavel Maltsev
24a28d7b201584823286f26e21a9f30d07c416eb3eKeun-young Parkimport android.annotation.Nullable;
25e11ad3ae2a65614c54e37be0a40a83029edcda73Pavel Maltsevimport android.annotation.StringRes;
2631966496485e3738e6bcfe775885f5cd9b857b22Pavel Maltsevimport android.hardware.automotive.vehicle.V2_0.IVehicle;
279bc6ec968099c014c16c57ceee963e4cf5eef1c0Enrico Granataimport android.hardware.automotive.vehicle.V2_0.IVehicleCallback;
289bc6ec968099c014c16c57ceee963e4cf5eef1c0Enrico Granataimport android.hardware.automotive.vehicle.V2_0.IVehicleCallback.Stub;
299bc6ec968099c014c16c57ceee963e4cf5eef1c0Enrico Granataimport android.hardware.automotive.vehicle.V2_0.SubscribeOptions;
3031966496485e3738e6bcfe775885f5cd9b857b22Pavel Maltsevimport android.hardware.automotive.vehicle.V2_0.VehicleArea;
3131966496485e3738e6bcfe775885f5cd9b857b22Pavel Maltsevimport android.hardware.automotive.vehicle.V2_0.VehicleDisplay;
3231966496485e3738e6bcfe775885f5cd9b857b22Pavel Maltsevimport android.hardware.automotive.vehicle.V2_0.VehiclePropValue;
3331966496485e3738e6bcfe775885f5cd9b857b22Pavel Maltsevimport android.hardware.automotive.vehicle.V2_0.VehicleProperty;
3431966496485e3738e6bcfe775885f5cd9b857b22Pavel Maltsevimport android.hardware.automotive.vehicle.V2_0.VehiclePropertyGroup;
3531966496485e3738e6bcfe775885f5cd9b857b22Pavel Maltsevimport android.hardware.automotive.vehicle.V2_0.VehiclePropertyStatus;
3631966496485e3738e6bcfe775885f5cd9b857b22Pavel Maltsevimport android.hardware.automotive.vehicle.V2_0.VehiclePropertyType;
37a28d7b201584823286f26e21a9f30d07c416eb3eKeun-young Parkimport android.os.Bundle;
3831966496485e3738e6bcfe775885f5cd9b857b22Pavel Maltsevimport android.os.RemoteException;
39fa3290a37081cfa6d4862d206f6b62293957adedRodrigo Lagosimport android.support.v4.app.Fragment;
403ac2057bd91eb506a4731338f724897cc8f449aeEnrico Granataimport android.text.method.ScrollingMovementMethod;
41a28d7b201584823286f26e21a9f30d07c416eb3eKeun-young Parkimport android.util.Log;
42a28d7b201584823286f26e21a9f30d07c416eb3eKeun-young Parkimport android.view.KeyEvent;
43a28d7b201584823286f26e21a9f30d07c416eb3eKeun-young Parkimport android.view.LayoutInflater;
44a28d7b201584823286f26e21a9f30d07c416eb3eKeun-young Parkimport android.view.View;
45a28d7b201584823286f26e21a9f30d07c416eb3eKeun-young Parkimport android.view.ViewGroup;
46a28d7b201584823286f26e21a9f30d07c416eb3eKeun-young Parkimport android.widget.Button;
47e11ad3ae2a65614c54e37be0a40a83029edcda73Pavel Maltsevimport android.widget.LinearLayout;
4831966496485e3738e6bcfe775885f5cd9b857b22Pavel Maltsevimport android.widget.TextView;
4931966496485e3738e6bcfe775885f5cd9b857b22Pavel Maltsevimport android.widget.Toast;
50e11ad3ae2a65614c54e37be0a40a83029edcda73Pavel Maltsev
513ac2057bd91eb506a4731338f724897cc8f449aeEnrico Granataimport com.android.car.keventreader.EventReaderService;
523ac2057bd91eb506a4731338f724897cc8f449aeEnrico Granataimport com.android.car.keventreader.IEventCallback;
533ac2057bd91eb506a4731338f724897cc8f449aeEnrico Granataimport com.android.car.keventreader.KeypressEvent;
543ac2057bd91eb506a4731338f724897cc8f449aeEnrico Granata
55e11ad3ae2a65614c54e37be0a40a83029edcda73Pavel Maltsevimport com.google.android.car.kitchensink.R;
5631966496485e3738e6bcfe775885f5cd9b857b22Pavel Maltsevimport com.google.android.collect.Lists;
57e11ad3ae2a65614c54e37be0a40a83029edcda73Pavel Maltsev
58e11ad3ae2a65614c54e37be0a40a83029edcda73Pavel Maltsevimport java.util.ArrayList;
59e11ad3ae2a65614c54e37be0a40a83029edcda73Pavel Maltsevimport java.util.Collections;
60e11ad3ae2a65614c54e37be0a40a83029edcda73Pavel Maltsevimport java.util.List;
6131966496485e3738e6bcfe775885f5cd9b857b22Pavel Maltsevimport java.util.NoSuchElementException;
62a28d7b201584823286f26e21a9f30d07c416eb3eKeun-young Park
63a28d7b201584823286f26e21a9f30d07c416eb3eKeun-young Park/**
64a28d7b201584823286f26e21a9f30d07c416eb3eKeun-young Park * Test input event handling to system.
65a28d7b201584823286f26e21a9f30d07c416eb3eKeun-young Park * vehicle hal should have VEHICLE_PROPERTY_HW_KEY_INPUT support for this to work.
66a28d7b201584823286f26e21a9f30d07c416eb3eKeun-young Park */
67a28d7b201584823286f26e21a9f30d07c416eb3eKeun-young Parkpublic class InputTestFragment extends Fragment {
68a28d7b201584823286f26e21a9f30d07c416eb3eKeun-young Park
69a28d7b201584823286f26e21a9f30d07c416eb3eKeun-young Park    private static final String TAG = "CAR.INPUT.KS";
70a28d7b201584823286f26e21a9f30d07c416eb3eKeun-young Park
71e11ad3ae2a65614c54e37be0a40a83029edcda73Pavel Maltsev    private static final Button BREAK_LINE = null;
72e11ad3ae2a65614c54e37be0a40a83029edcda73Pavel Maltsev
73e11ad3ae2a65614c54e37be0a40a83029edcda73Pavel Maltsev    private final List<View> mButtons = new ArrayList<>();
74a28d7b201584823286f26e21a9f30d07c416eb3eKeun-young Park
75c7f46a9104419e071302adcb2a5908177b9fd41cChao Yan    // This is fake data generation property available only in emulated VHAL implementation.
7631966496485e3738e6bcfe775885f5cd9b857b22Pavel Maltsev    private static final int sGenerateFakeDataControllingProperty =
7731966496485e3738e6bcfe775885f5cd9b857b22Pavel Maltsev            0x0666 | VehiclePropertyGroup.VENDOR | VehicleArea.GLOBAL | VehiclePropertyType.MIXED;
78c7f46a9104419e071302adcb2a5908177b9fd41cChao Yan    // The key press command is sent with the fake data generation property. It's matching the
79c7f46a9104419e071302adcb2a5908177b9fd41cChao Yan    // command defined in the emulated VHAL implementation.
80c7f46a9104419e071302adcb2a5908177b9fd41cChao Yan    private static final int sKeyPressCommand = 100;
8131966496485e3738e6bcfe775885f5cd9b857b22Pavel Maltsev
8231966496485e3738e6bcfe775885f5cd9b857b22Pavel Maltsev    private IVehicle mVehicle;
8331966496485e3738e6bcfe775885f5cd9b857b22Pavel Maltsev
843ac2057bd91eb506a4731338f724897cc8f449aeEnrico Granata    private EventReaderService mEventReaderService;
853ac2057bd91eb506a4731338f724897cc8f449aeEnrico Granata
863ac2057bd91eb506a4731338f724897cc8f449aeEnrico Granata    private final IEventCallback.Stub mKeypressEventHandler = new IEventCallback.Stub() {
873ac2057bd91eb506a4731338f724897cc8f449aeEnrico Granata        private String prettyPrint(KeypressEvent event) {
883ac2057bd91eb506a4731338f724897cc8f449aeEnrico Granata            return String.format("Event{source = %s, keycode = %s, key%s}\n",
893ac2057bd91eb506a4731338f724897cc8f449aeEnrico Granata                event.source,
903ac2057bd91eb506a4731338f724897cc8f449aeEnrico Granata                event.keycodeToString(),
913ac2057bd91eb506a4731338f724897cc8f449aeEnrico Granata                event.isKeydown ? "down" : "up");
923ac2057bd91eb506a4731338f724897cc8f449aeEnrico Granata        }
933ac2057bd91eb506a4731338f724897cc8f449aeEnrico Granata
943ac2057bd91eb506a4731338f724897cc8f449aeEnrico Granata        @Override
953ac2057bd91eb506a4731338f724897cc8f449aeEnrico Granata        public void onEvent(KeypressEvent keypressEvent) throws RemoteException {
963ac2057bd91eb506a4731338f724897cc8f449aeEnrico Granata            Log.d(TAG, "received event " + keypressEvent);
979bc6ec968099c014c16c57ceee963e4cf5eef1c0Enrico Granata            synchronized (mInputEventsList) {
989bc6ec968099c014c16c57ceee963e4cf5eef1c0Enrico Granata                mInputEventsList.append(prettyPrint(keypressEvent));
999bc6ec968099c014c16c57ceee963e4cf5eef1c0Enrico Granata            }
1003ac2057bd91eb506a4731338f724897cc8f449aeEnrico Granata        }
1013ac2057bd91eb506a4731338f724897cc8f449aeEnrico Granata    };
1023ac2057bd91eb506a4731338f724897cc8f449aeEnrico Granata
1039bc6ec968099c014c16c57ceee963e4cf5eef1c0Enrico Granata    private final IVehicleCallback.Stub mHalKeyEventHandler = new Stub() {
1049bc6ec968099c014c16c57ceee963e4cf5eef1c0Enrico Granata        private String prettyPrint(VehiclePropValue event) {
1059bc6ec968099c014c16c57ceee963e4cf5eef1c0Enrico Granata            if (event.prop != HW_KEY_INPUT) return "";
1069bc6ec968099c014c16c57ceee963e4cf5eef1c0Enrico Granata            if (event.value == null ||
1079bc6ec968099c014c16c57ceee963e4cf5eef1c0Enrico Granata                event.value.int32Values == null ||
1089bc6ec968099c014c16c57ceee963e4cf5eef1c0Enrico Granata                event.value.int32Values.size() < 2) return "";
1099bc6ec968099c014c16c57ceee963e4cf5eef1c0Enrico Granata            return String.format("Event{source = HAL, keycode = %s, key%s}\n",
1109bc6ec968099c014c16c57ceee963e4cf5eef1c0Enrico Granata                event.value.int32Values.get(1),
1119bc6ec968099c014c16c57ceee963e4cf5eef1c0Enrico Granata                event.value.int32Values.get(0) == ACTION_DOWN ? "down" : "up");
1129bc6ec968099c014c16c57ceee963e4cf5eef1c0Enrico Granata        }
1139bc6ec968099c014c16c57ceee963e4cf5eef1c0Enrico Granata
1149bc6ec968099c014c16c57ceee963e4cf5eef1c0Enrico Granata        @Override
1159bc6ec968099c014c16c57ceee963e4cf5eef1c0Enrico Granata        public void onPropertyEvent(ArrayList<VehiclePropValue> propValues) throws RemoteException {
1169bc6ec968099c014c16c57ceee963e4cf5eef1c0Enrico Granata            synchronized (mInputEventsList) {
1179bc6ec968099c014c16c57ceee963e4cf5eef1c0Enrico Granata                propValues.forEach(vpv -> mInputEventsList.append(prettyPrint(vpv)));
1189bc6ec968099c014c16c57ceee963e4cf5eef1c0Enrico Granata            }
1199bc6ec968099c014c16c57ceee963e4cf5eef1c0Enrico Granata        }
1209bc6ec968099c014c16c57ceee963e4cf5eef1c0Enrico Granata
1219bc6ec968099c014c16c57ceee963e4cf5eef1c0Enrico Granata        @Override
1229bc6ec968099c014c16c57ceee963e4cf5eef1c0Enrico Granata        public void onPropertySet(VehiclePropValue propValue) {}
1239bc6ec968099c014c16c57ceee963e4cf5eef1c0Enrico Granata
1249bc6ec968099c014c16c57ceee963e4cf5eef1c0Enrico Granata        @Override
1259bc6ec968099c014c16c57ceee963e4cf5eef1c0Enrico Granata        public void onPropertySetError(int errorCode, int propId, int areaId) {}
1269bc6ec968099c014c16c57ceee963e4cf5eef1c0Enrico Granata    };
1279bc6ec968099c014c16c57ceee963e4cf5eef1c0Enrico Granata
1283ac2057bd91eb506a4731338f724897cc8f449aeEnrico Granata    private TextView mInputEventsList;
1293ac2057bd91eb506a4731338f724897cc8f449aeEnrico Granata
13031966496485e3738e6bcfe775885f5cd9b857b22Pavel Maltsev    @Override
13131966496485e3738e6bcfe775885f5cd9b857b22Pavel Maltsev    public void onCreate(Bundle savedInstanceState) {
13231966496485e3738e6bcfe775885f5cd9b857b22Pavel Maltsev        super.onCreate(savedInstanceState);
13331966496485e3738e6bcfe775885f5cd9b857b22Pavel Maltsev
13431966496485e3738e6bcfe775885f5cd9b857b22Pavel Maltsev        try {
13531966496485e3738e6bcfe775885f5cd9b857b22Pavel Maltsev            mVehicle = IVehicle.getService();
13631966496485e3738e6bcfe775885f5cd9b857b22Pavel Maltsev        } catch (NoSuchElementException ex) {
13731966496485e3738e6bcfe775885f5cd9b857b22Pavel Maltsev            throw new RuntimeException("Couldn't connect to " + IVehicle.kInterfaceName, ex);
13831966496485e3738e6bcfe775885f5cd9b857b22Pavel Maltsev        } catch (RemoteException e) {
13931966496485e3738e6bcfe775885f5cd9b857b22Pavel Maltsev            throw new RuntimeException("Failed to connect to IVehicle");
14031966496485e3738e6bcfe775885f5cd9b857b22Pavel Maltsev        }
14131966496485e3738e6bcfe775885f5cd9b857b22Pavel Maltsev        Log.d(TAG, "Connected to IVehicle service: " + mVehicle);
1423ac2057bd91eb506a4731338f724897cc8f449aeEnrico Granata
1433ac2057bd91eb506a4731338f724897cc8f449aeEnrico Granata        mEventReaderService = EventReaderService.tryGet();
1443ac2057bd91eb506a4731338f724897cc8f449aeEnrico Granata        Log.d(TAG, "Key Event Reader service: " + mEventReaderService);
1453ac2057bd91eb506a4731338f724897cc8f449aeEnrico Granata        if (mEventReaderService != null) {
1463ac2057bd91eb506a4731338f724897cc8f449aeEnrico Granata            mEventReaderService.registerCallback(mKeypressEventHandler);
1473ac2057bd91eb506a4731338f724897cc8f449aeEnrico Granata        }
1489bc6ec968099c014c16c57ceee963e4cf5eef1c0Enrico Granata
1499bc6ec968099c014c16c57ceee963e4cf5eef1c0Enrico Granata        SubscribeOptions subscribeOption = new SubscribeOptions();
1509bc6ec968099c014c16c57ceee963e4cf5eef1c0Enrico Granata        subscribeOption.propId = HW_KEY_INPUT;
1519bc6ec968099c014c16c57ceee963e4cf5eef1c0Enrico Granata        subscribeOption.flags = EVENTS_FROM_CAR | EVENTS_FROM_ANDROID;
1529bc6ec968099c014c16c57ceee963e4cf5eef1c0Enrico Granata        ArrayList<SubscribeOptions> subscribeOptions = new ArrayList<>();
1539bc6ec968099c014c16c57ceee963e4cf5eef1c0Enrico Granata        subscribeOptions.add(subscribeOption);
1549bc6ec968099c014c16c57ceee963e4cf5eef1c0Enrico Granata        try {
1559bc6ec968099c014c16c57ceee963e4cf5eef1c0Enrico Granata            mVehicle.subscribe(mHalKeyEventHandler, subscribeOptions);
1569bc6ec968099c014c16c57ceee963e4cf5eef1c0Enrico Granata        } catch (RemoteException e) {
1579bc6ec968099c014c16c57ceee963e4cf5eef1c0Enrico Granata            Log.e(TAG, "failed to connect to VHAL for key events", e);
1589bc6ec968099c014c16c57ceee963e4cf5eef1c0Enrico Granata        }
15931966496485e3738e6bcfe775885f5cd9b857b22Pavel Maltsev    }
1600d07c76bbc788fba8c77d8e932330ab22ec6ba27Pavel Maltsev
161a28d7b201584823286f26e21a9f30d07c416eb3eKeun-young Park    @Nullable
162a28d7b201584823286f26e21a9f30d07c416eb3eKeun-young Park    @Override
163a28d7b201584823286f26e21a9f30d07c416eb3eKeun-young Park    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container,
164a28d7b201584823286f26e21a9f30d07c416eb3eKeun-young Park            @Nullable Bundle savedInstanceState) {
165d8c22f21348822a0022ec99444ecb062ebfa00dbKeun-young Park        View view = inflater.inflate(R.layout.input_test, container, false);
166a28d7b201584823286f26e21a9f30d07c416eb3eKeun-young Park
1673ac2057bd91eb506a4731338f724897cc8f449aeEnrico Granata        mInputEventsList = view.findViewById(R.id.events_list);
1683ac2057bd91eb506a4731338f724897cc8f449aeEnrico Granata        mInputEventsList.setMovementMethod(new ScrollingMovementMethod());
1693ac2057bd91eb506a4731338f724897cc8f449aeEnrico Granata
17031966496485e3738e6bcfe775885f5cd9b857b22Pavel Maltsev        TextView steeringWheelLabel = new TextView(getActivity() /*context*/);
17131966496485e3738e6bcfe775885f5cd9b857b22Pavel Maltsev        steeringWheelLabel.setText(R.string.steering_wheel);
17231966496485e3738e6bcfe775885f5cd9b857b22Pavel Maltsev        steeringWheelLabel.setTextSize(getResources().getDimension(R.dimen.car_title2_size));
17331966496485e3738e6bcfe775885f5cd9b857b22Pavel Maltsev
174e11ad3ae2a65614c54e37be0a40a83029edcda73Pavel Maltsev        Collections.addAll(mButtons,
175e11ad3ae2a65614c54e37be0a40a83029edcda73Pavel Maltsev                BREAK_LINE,
176e11ad3ae2a65614c54e37be0a40a83029edcda73Pavel Maltsev                createButton(R.string.home, KeyEvent.KEYCODE_HOME),
177e11ad3ae2a65614c54e37be0a40a83029edcda73Pavel Maltsev                createButton(R.string.volume_up, KeyEvent.KEYCODE_VOLUME_UP),
178e11ad3ae2a65614c54e37be0a40a83029edcda73Pavel Maltsev                createButton(R.string.volume_down, KeyEvent.KEYCODE_VOLUME_DOWN),
179e11ad3ae2a65614c54e37be0a40a83029edcda73Pavel Maltsev                createButton(R.string.volume_mute, KeyEvent.KEYCODE_VOLUME_MUTE),
180e11ad3ae2a65614c54e37be0a40a83029edcda73Pavel Maltsev                createButton(R.string.voice, KeyEvent.KEYCODE_VOICE_ASSIST),
181e11ad3ae2a65614c54e37be0a40a83029edcda73Pavel Maltsev                BREAK_LINE,
182e11ad3ae2a65614c54e37be0a40a83029edcda73Pavel Maltsev                createButton(R.string.music, KeyEvent.KEYCODE_MUSIC),
183e11ad3ae2a65614c54e37be0a40a83029edcda73Pavel Maltsev                createButton(R.string.music_play, KeyEvent.KEYCODE_MEDIA_PLAY),
184e11ad3ae2a65614c54e37be0a40a83029edcda73Pavel Maltsev                createButton(R.string.music_stop, KeyEvent.KEYCODE_MEDIA_STOP),
185e11ad3ae2a65614c54e37be0a40a83029edcda73Pavel Maltsev                createButton(R.string.next_song, KeyEvent.KEYCODE_MEDIA_NEXT),
186e11ad3ae2a65614c54e37be0a40a83029edcda73Pavel Maltsev                createButton(R.string.prev_song, KeyEvent.KEYCODE_MEDIA_PREVIOUS),
187e11ad3ae2a65614c54e37be0a40a83029edcda73Pavel Maltsev                createButton(R.string.tune_right, KeyEvent.KEYCODE_CHANNEL_UP),
188e11ad3ae2a65614c54e37be0a40a83029edcda73Pavel Maltsev                createButton(R.string.tune_left, KeyEvent.KEYCODE_CHANNEL_DOWN),
189e11ad3ae2a65614c54e37be0a40a83029edcda73Pavel Maltsev                BREAK_LINE,
190e11ad3ae2a65614c54e37be0a40a83029edcda73Pavel Maltsev                createButton(R.string.call_send, KeyEvent.KEYCODE_CALL),
19131966496485e3738e6bcfe775885f5cd9b857b22Pavel Maltsev                createButton(R.string.call_end, KeyEvent.KEYCODE_ENDCALL),
19231966496485e3738e6bcfe775885f5cd9b857b22Pavel Maltsev                BREAK_LINE,
19331966496485e3738e6bcfe775885f5cd9b857b22Pavel Maltsev                steeringWheelLabel,
19431966496485e3738e6bcfe775885f5cd9b857b22Pavel Maltsev                BREAK_LINE,
19531966496485e3738e6bcfe775885f5cd9b857b22Pavel Maltsev                createButton(R.string.sw_left, KeyEvent.KEYCODE_DPAD_LEFT, INSTRUMENT_CLUSTER),
19631966496485e3738e6bcfe775885f5cd9b857b22Pavel Maltsev                createButton(R.string.sw_right, KeyEvent.KEYCODE_DPAD_RIGHT,
19731966496485e3738e6bcfe775885f5cd9b857b22Pavel Maltsev                        INSTRUMENT_CLUSTER),
19831966496485e3738e6bcfe775885f5cd9b857b22Pavel Maltsev                createButton(R.string.sw_up, KeyEvent.KEYCODE_DPAD_UP, INSTRUMENT_CLUSTER),
19931966496485e3738e6bcfe775885f5cd9b857b22Pavel Maltsev                createButton(R.string.sw_down, KeyEvent.KEYCODE_DPAD_DOWN, INSTRUMENT_CLUSTER),
20031966496485e3738e6bcfe775885f5cd9b857b22Pavel Maltsev                createButton(R.string.sw_center, KeyEvent.KEYCODE_DPAD_CENTER,
20131966496485e3738e6bcfe775885f5cd9b857b22Pavel Maltsev                        INSTRUMENT_CLUSTER),
20231966496485e3738e6bcfe775885f5cd9b857b22Pavel Maltsev                createButton(R.string.sw_back, KeyEvent.KEYCODE_BACK, INSTRUMENT_CLUSTER)
20331966496485e3738e6bcfe775885f5cd9b857b22Pavel Maltsev        );
20431966496485e3738e6bcfe775885f5cd9b857b22Pavel Maltsev
20531966496485e3738e6bcfe775885f5cd9b857b22Pavel Maltsev        addButtonsToPanel(view.findViewById(R.id.input_buttons), mButtons);
206a28d7b201584823286f26e21a9f30d07c416eb3eKeun-young Park
207d8c22f21348822a0022ec99444ecb062ebfa00dbKeun-young Park        return view;
208a28d7b201584823286f26e21a9f30d07c416eb3eKeun-young Park    }
209a28d7b201584823286f26e21a9f30d07c416eb3eKeun-young Park
210e11ad3ae2a65614c54e37be0a40a83029edcda73Pavel Maltsev    private Button createButton(@StringRes int textResId, int keyCode) {
21131966496485e3738e6bcfe775885f5cd9b857b22Pavel Maltsev        return createButton(textResId, keyCode, VehicleDisplay.MAIN);
21231966496485e3738e6bcfe775885f5cd9b857b22Pavel Maltsev    }
21331966496485e3738e6bcfe775885f5cd9b857b22Pavel Maltsev
21431966496485e3738e6bcfe775885f5cd9b857b22Pavel Maltsev    private Button createButton(@StringRes int textResId, int keyCode, int targetDisplay) {
215e11ad3ae2a65614c54e37be0a40a83029edcda73Pavel Maltsev        Button button = new Button(getContext());
216e11ad3ae2a65614c54e37be0a40a83029edcda73Pavel Maltsev        button.setText(getContext().getString(textResId));
21731966496485e3738e6bcfe775885f5cd9b857b22Pavel Maltsev        button.setTextSize(getResources().getDimension(R.dimen.car_button_text_size));
21831966496485e3738e6bcfe775885f5cd9b857b22Pavel Maltsev        button.setOnClickListener(v -> onButtonClick(keyCode, targetDisplay));
219e11ad3ae2a65614c54e37be0a40a83029edcda73Pavel Maltsev        return button;
220e11ad3ae2a65614c54e37be0a40a83029edcda73Pavel Maltsev    }
221e11ad3ae2a65614c54e37be0a40a83029edcda73Pavel Maltsev
22231966496485e3738e6bcfe775885f5cd9b857b22Pavel Maltsev    private void onButtonClick(int keyCode, int targetDisplay) {
22331966496485e3738e6bcfe775885f5cd9b857b22Pavel Maltsev        VehiclePropValue prop = new VehiclePropValue();
22431966496485e3738e6bcfe775885f5cd9b857b22Pavel Maltsev        prop.prop = sGenerateFakeDataControllingProperty;
22531966496485e3738e6bcfe775885f5cd9b857b22Pavel Maltsev        prop.value.int32Values.addAll(Lists.newArrayList(
2269bc6ec968099c014c16c57ceee963e4cf5eef1c0Enrico Granata                sKeyPressCommand, HW_KEY_INPUT, keyCode, targetDisplay));
22731966496485e3738e6bcfe775885f5cd9b857b22Pavel Maltsev        int status;
22831966496485e3738e6bcfe775885f5cd9b857b22Pavel Maltsev        try {
22931966496485e3738e6bcfe775885f5cd9b857b22Pavel Maltsev            status = mVehicle.set(prop);
23031966496485e3738e6bcfe775885f5cd9b857b22Pavel Maltsev        } catch (RemoteException e) {
23131966496485e3738e6bcfe775885f5cd9b857b22Pavel Maltsev            throw new RuntimeException("Failed to inject key press");
23231966496485e3738e6bcfe775885f5cd9b857b22Pavel Maltsev        }
23331966496485e3738e6bcfe775885f5cd9b857b22Pavel Maltsev
23431966496485e3738e6bcfe775885f5cd9b857b22Pavel Maltsev        if (VehiclePropertyStatus.AVAILABLE != status) {
23531966496485e3738e6bcfe775885f5cd9b857b22Pavel Maltsev            Toast.makeText(getContext(), "Failed to inject key event, status:" + status,
23631966496485e3738e6bcfe775885f5cd9b857b22Pavel Maltsev                    Toast.LENGTH_LONG).show();
237a28d7b201584823286f26e21a9f30d07c416eb3eKeun-young Park        }
238a28d7b201584823286f26e21a9f30d07c416eb3eKeun-young Park    }
239a28d7b201584823286f26e21a9f30d07c416eb3eKeun-young Park
240a28d7b201584823286f26e21a9f30d07c416eb3eKeun-young Park    @Override
241a28d7b201584823286f26e21a9f30d07c416eb3eKeun-young Park    public void onDestroyView() {
242a28d7b201584823286f26e21a9f30d07c416eb3eKeun-young Park        super.onDestroyView();
24331966496485e3738e6bcfe775885f5cd9b857b22Pavel Maltsev        mButtons.clear();
2443ac2057bd91eb506a4731338f724897cc8f449aeEnrico Granata        if (mEventReaderService != null) {
2453ac2057bd91eb506a4731338f724897cc8f449aeEnrico Granata            mEventReaderService.unregisterCallback(mKeypressEventHandler);
2463ac2057bd91eb506a4731338f724897cc8f449aeEnrico Granata        }
2479bc6ec968099c014c16c57ceee963e4cf5eef1c0Enrico Granata        try {
2489bc6ec968099c014c16c57ceee963e4cf5eef1c0Enrico Granata            mVehicle.unsubscribe(mHalKeyEventHandler, HW_KEY_INPUT);
2499bc6ec968099c014c16c57ceee963e4cf5eef1c0Enrico Granata        } catch (RemoteException e) {
2509bc6ec968099c014c16c57ceee963e4cf5eef1c0Enrico Granata            Log.e(TAG, "failed to remove HAL registration for keypress events", e);
2519bc6ec968099c014c16c57ceee963e4cf5eef1c0Enrico Granata        }
252a28d7b201584823286f26e21a9f30d07c416eb3eKeun-young Park    }
253e11ad3ae2a65614c54e37be0a40a83029edcda73Pavel Maltsev
254e11ad3ae2a65614c54e37be0a40a83029edcda73Pavel Maltsev    private void addButtonsToPanel(LinearLayout root, List<View> buttons) {
255e11ad3ae2a65614c54e37be0a40a83029edcda73Pavel Maltsev        LinearLayout panel = null;
256e11ad3ae2a65614c54e37be0a40a83029edcda73Pavel Maltsev        for (View button : buttons) {
2570d07c76bbc788fba8c77d8e932330ab22ec6ba27Pavel Maltsev            if (button == BREAK_LINE || panel == null) {
258e11ad3ae2a65614c54e37be0a40a83029edcda73Pavel Maltsev                panel = new LinearLayout(getContext());
259e11ad3ae2a65614c54e37be0a40a83029edcda73Pavel Maltsev                panel.setOrientation(LinearLayout.HORIZONTAL);
260e11ad3ae2a65614c54e37be0a40a83029edcda73Pavel Maltsev                root.addView(panel);
261e11ad3ae2a65614c54e37be0a40a83029edcda73Pavel Maltsev            } else {
262e11ad3ae2a65614c54e37be0a40a83029edcda73Pavel Maltsev                panel.addView(button);
26331966496485e3738e6bcfe775885f5cd9b857b22Pavel Maltsev                panel.setPadding(0, 10, 10, 0);
264e11ad3ae2a65614c54e37be0a40a83029edcda73Pavel Maltsev            }
265e11ad3ae2a65614c54e37be0a40a83029edcda73Pavel Maltsev        }
266e11ad3ae2a65614c54e37be0a40a83029edcda73Pavel Maltsev    }
267a28d7b201584823286f26e21a9f30d07c416eb3eKeun-young Park}
268