• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2011 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.apis.view;
18 
19 import com.example.android.apis.R;
20 
21 import android.app.Activity;
22 import android.content.Context;
23 import android.content.res.Resources;
24 import android.hardware.input.InputManager;
25 import android.os.Bundle;
26 import android.util.Log;
27 import android.util.SparseArray;
28 import android.util.SparseIntArray;
29 import android.view.InputDevice;
30 import android.view.KeyEvent;
31 import android.view.LayoutInflater;
32 import android.view.MotionEvent;
33 import android.view.View;
34 import android.view.ViewGroup;
35 import android.view.InputDevice.MotionRange;
36 import android.widget.AdapterView;
37 import android.widget.BaseAdapter;
38 import android.widget.ListView;
39 import android.widget.TextView;
40 import android.widget.Toast;
41 
42 import java.util.ArrayList;
43 import java.util.List;
44 
45 
46 /**
47  * Demonstrates how to process input events received from game controllers.
48  * It also shows how to detect when input devices are added, removed or reconfigured.
49  *
50  * This activity displays button states and joystick positions.
51  * Also writes detailed information about relevant input events to the log.
52  *
53  * The game controller is also uses to control a very simple game.  See {@link GameView}
54  * for the game itself.
55  */
56 public class GameControllerInput extends Activity
57         implements InputManager.InputDeviceListener {
58     private static final String TAG = "GameControllerInput";
59 
60     private InputManager mInputManager;
61     private SparseArray<InputDeviceState> mInputDeviceStates;
62     private GameView mGame;
63     private ListView mSummaryList;
64     private SummaryAdapter mSummaryAdapter;
65 
66     @Override
onCreate(Bundle savedInstanceState)67     protected void onCreate(Bundle savedInstanceState) {
68         super.onCreate(savedInstanceState);
69 
70         mInputManager = (InputManager)getSystemService(Context.INPUT_SERVICE);
71 
72         mInputDeviceStates = new SparseArray<InputDeviceState>();
73         mSummaryAdapter = new SummaryAdapter(this, getResources());
74 
75         setContentView(R.layout.game_controller_input);
76 
77         mGame = (GameView) findViewById(R.id.game);
78 
79         mSummaryList = (ListView) findViewById(R.id.summary);
80         mSummaryList.setAdapter(mSummaryAdapter);
81         mSummaryList.setOnItemClickListener(new AdapterView.OnItemClickListener() {
82             @Override
83             public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
84                 mSummaryAdapter.onItemClick(position);
85             }
86         });
87     }
88 
89     @Override
onResume()90     protected void onResume() {
91         super.onResume();
92 
93         // Register an input device listener to watch when input devices are
94         // added, removed or reconfigured.
95         mInputManager.registerInputDeviceListener(this, null);
96 
97         // Query all input devices.
98         // We do this so that we can see them in the log as they are enumerated.
99         int[] ids = mInputManager.getInputDeviceIds();
100         for (int i = 0; i < ids.length; i++) {
101             getInputDeviceState(ids[i]);
102         }
103     }
104 
105     @Override
onPause()106     protected void onPause() {
107         super.onPause();
108 
109         // Remove the input device listener when the activity is paused.
110         mInputManager.unregisterInputDeviceListener(this);
111     }
112 
113     @Override
onWindowFocusChanged(boolean hasFocus)114     public void onWindowFocusChanged(boolean hasFocus) {
115         super.onWindowFocusChanged(hasFocus);
116 
117         mGame.requestFocus();
118     }
119 
120     @Override
dispatchKeyEvent(KeyEvent event)121     public boolean dispatchKeyEvent(KeyEvent event) {
122         // Update device state for visualization and logging.
123         InputDeviceState state = getInputDeviceState(event.getDeviceId());
124         if (state != null) {
125             switch (event.getAction()) {
126                 case KeyEvent.ACTION_DOWN:
127                     if (state.onKeyDown(event)) {
128                         mSummaryAdapter.show(state);
129                     }
130                     break;
131                 case KeyEvent.ACTION_UP:
132                     if (state.onKeyUp(event)) {
133                         mSummaryAdapter.show(state);
134                     }
135                     break;
136             }
137         }
138         return super.dispatchKeyEvent(event);
139     }
140 
141     @Override
dispatchGenericMotionEvent(MotionEvent event)142     public boolean dispatchGenericMotionEvent(MotionEvent event) {
143         // Check that the event came from a joystick since a generic motion event
144         // could be almost anything.
145         if (isJoystick(event.getSource())
146                 && event.getAction() == MotionEvent.ACTION_MOVE) {
147             // Update device state for visualization and logging.
148             InputDeviceState state = getInputDeviceState(event.getDeviceId());
149             if (state != null && state.onJoystickMotion(event)) {
150                 mSummaryAdapter.show(state);
151             }
152         }
153         return super.dispatchGenericMotionEvent(event);
154     }
155 
getInputDeviceState(int deviceId)156     private InputDeviceState getInputDeviceState(int deviceId) {
157         InputDeviceState state = mInputDeviceStates.get(deviceId);
158         if (state == null) {
159             final InputDevice device = mInputManager.getInputDevice(deviceId);
160             if (device == null) {
161                 return null;
162             }
163             state = new InputDeviceState(device);
164             mInputDeviceStates.put(deviceId, state);
165             Log.i(TAG, "Device enumerated: " + state.mDevice);
166         }
167         return state;
168     }
169 
170     // Implementation of InputManager.InputDeviceListener.onInputDeviceAdded()
171     @Override
onInputDeviceAdded(int deviceId)172     public void onInputDeviceAdded(int deviceId) {
173         InputDeviceState state = getInputDeviceState(deviceId);
174         Log.i(TAG, "Device added: " + state.mDevice);
175     }
176 
177     // Implementation of InputManager.InputDeviceListener.onInputDeviceChanged()
178     @Override
onInputDeviceChanged(int deviceId)179     public void onInputDeviceChanged(int deviceId) {
180         InputDeviceState state = mInputDeviceStates.get(deviceId);
181         if (state != null) {
182             mInputDeviceStates.remove(deviceId);
183             state = getInputDeviceState(deviceId);
184             Log.i(TAG, "Device changed: " + state.mDevice);
185         }
186     }
187 
188     // Implementation of InputManager.InputDeviceListener.onInputDeviceRemoved()
189     @Override
onInputDeviceRemoved(int deviceId)190     public void onInputDeviceRemoved(int deviceId) {
191         InputDeviceState state = mInputDeviceStates.get(deviceId);
192         if (state != null) {
193             Log.i(TAG, "Device removed: " + state.mDevice);
194             mInputDeviceStates.remove(deviceId);
195         }
196     }
197 
isJoystick(int source)198     private static boolean isJoystick(int source) {
199         return (source & InputDevice.SOURCE_CLASS_JOYSTICK) != 0;
200     }
201 
202     /**
203      * Tracks the state of joystick axes and game controller buttons for a particular
204      * input device for diagnostic purposes.
205      */
206     private static class InputDeviceState {
207         private final InputDevice mDevice;
208         private final int[] mAxes;
209         private final float[] mAxisValues;
210         private final SparseIntArray mKeys;
211 
InputDeviceState(InputDevice device)212         public InputDeviceState(InputDevice device) {
213             mDevice = device;
214 
215             int numAxes = 0;
216             final List<MotionRange> ranges = device.getMotionRanges();
217             for (MotionRange range : ranges) {
218                 if ((range.getSource() & InputDevice.SOURCE_CLASS_JOYSTICK) != 0) {
219                     numAxes += 1;
220                 }
221             }
222 
223             mAxes = new int[numAxes];
224             mAxisValues = new float[numAxes];
225             int i = 0;
226             for (MotionRange range : ranges) {
227                 if ((range.getSource() & InputDevice.SOURCE_CLASS_JOYSTICK) != 0) {
228                     mAxes[i++] = range.getAxis();
229                 }
230             }
231 
232             mKeys = new SparseIntArray();
233         }
234 
getDevice()235         public InputDevice getDevice() {
236             return mDevice;
237         }
238 
getAxisCount()239         public int getAxisCount() {
240             return mAxes.length;
241         }
242 
getAxis(int axisIndex)243         public int getAxis(int axisIndex) {
244             return mAxes[axisIndex];
245         }
246 
getAxisValue(int axisIndex)247         public float getAxisValue(int axisIndex) {
248             return mAxisValues[axisIndex];
249         }
250 
getKeyCount()251         public int getKeyCount() {
252             return mKeys.size();
253         }
254 
getKeyCode(int keyIndex)255         public int getKeyCode(int keyIndex) {
256             return mKeys.keyAt(keyIndex);
257         }
258 
isKeyPressed(int keyIndex)259         public boolean isKeyPressed(int keyIndex) {
260             return mKeys.valueAt(keyIndex) != 0;
261         }
262 
onKeyDown(KeyEvent event)263         public boolean onKeyDown(KeyEvent event) {
264             final int keyCode = event.getKeyCode();
265             if (isGameKey(keyCode)) {
266                 if (event.getRepeatCount() == 0) {
267                     final String symbolicName = KeyEvent.keyCodeToString(keyCode);
268                     mKeys.put(keyCode, 1);
269                     Log.i(TAG, mDevice.getName() + " - Key Down: " + symbolicName);
270                 }
271                 return true;
272             }
273             return false;
274         }
275 
onKeyUp(KeyEvent event)276         public boolean onKeyUp(KeyEvent event) {
277             final int keyCode = event.getKeyCode();
278             if (isGameKey(keyCode)) {
279                 int index = mKeys.indexOfKey(keyCode);
280                 if (index >= 0) {
281                     final String symbolicName = KeyEvent.keyCodeToString(keyCode);
282                     mKeys.put(keyCode, 0);
283                     Log.i(TAG, mDevice.getName() + " - Key Up: " + symbolicName);
284                 }
285                 return true;
286             }
287             return false;
288         }
289 
onJoystickMotion(MotionEvent event)290         public boolean onJoystickMotion(MotionEvent event) {
291             StringBuilder message = new StringBuilder();
292             message.append(mDevice.getName()).append(" - Joystick Motion:\n");
293 
294             final int historySize = event.getHistorySize();
295             for (int i = 0; i < mAxes.length; i++) {
296                 final int axis = mAxes[i];
297                 final float value = event.getAxisValue(axis);
298                 mAxisValues[i] = value;
299                 message.append("  ").append(MotionEvent.axisToString(axis)).append(": ");
300 
301                 // Append all historical values in the batch.
302                 for (int historyPos = 0; historyPos < historySize; historyPos++) {
303                     message.append(event.getHistoricalAxisValue(axis, historyPos));
304                     message.append(", ");
305                 }
306 
307                 // Append the current value.
308                 message.append(value);
309                 message.append("\n");
310             }
311             Log.i(TAG, message.toString());
312             return true;
313         }
314 
315         // Check whether this is a key we care about.
316         // In a real game, we would probably let the user configure which keys to use
317         // instead of hardcoding the keys like this.
isGameKey(int keyCode)318         private static boolean isGameKey(int keyCode) {
319             switch (keyCode) {
320                 case KeyEvent.KEYCODE_DPAD_UP:
321                 case KeyEvent.KEYCODE_DPAD_DOWN:
322                 case KeyEvent.KEYCODE_DPAD_LEFT:
323                 case KeyEvent.KEYCODE_DPAD_RIGHT:
324                 case KeyEvent.KEYCODE_DPAD_CENTER:
325                 case KeyEvent.KEYCODE_SPACE:
326                     return true;
327                 default:
328                     return KeyEvent.isGamepadButton(keyCode);
329             }
330         }
331     }
332 
333     /**
334      * A list adapter that displays a summary of the device state.
335      */
336     private static class SummaryAdapter extends BaseAdapter {
337         private static final int BASE_ID_HEADING = 1 << 10;
338         private static final int BASE_ID_DEVICE_ITEM = 2 << 10;
339         private static final int BASE_ID_AXIS_ITEM = 3 << 10;
340         private static final int BASE_ID_KEY_ITEM = 4 << 10;
341 
342         private final Context mContext;
343         private final Resources mResources;
344 
345         private final SparseArray<Item> mDataItems = new SparseArray<Item>();
346         private final ArrayList<Item> mVisibleItems = new ArrayList<Item>();
347 
348         private final Heading mDeviceHeading;
349         private final TextColumn mDeviceNameTextColumn;
350 
351         private final Heading mAxesHeading;
352         private final Heading mKeysHeading;
353 
354         private InputDeviceState mState;
355 
SummaryAdapter(Context context, Resources resources)356         public SummaryAdapter(Context context, Resources resources) {
357             mContext = context;
358             mResources = resources;
359 
360             mDeviceHeading = new Heading(BASE_ID_HEADING | 0,
361                     mResources.getString(R.string.game_controller_input_heading_device));
362             mDeviceNameTextColumn = new TextColumn(BASE_ID_DEVICE_ITEM | 0,
363                     mResources.getString(R.string.game_controller_input_label_device_name));
364 
365             mAxesHeading = new Heading(BASE_ID_HEADING | 1,
366                     mResources.getString(R.string.game_controller_input_heading_axes));
367             mKeysHeading = new Heading(BASE_ID_HEADING | 2,
368                     mResources.getString(R.string.game_controller_input_heading_keys));
369         }
370 
onItemClick(int position)371         public void onItemClick(int position) {
372             if (mState != null) {
373                 Toast toast = Toast.makeText(
374                         mContext, mState.getDevice().toString(), Toast.LENGTH_LONG);
375                 toast.show();
376             }
377         }
378 
show(InputDeviceState state)379         public void show(InputDeviceState state) {
380             mState = state;
381             mVisibleItems.clear();
382 
383             // Populate device information.
384             mVisibleItems.add(mDeviceHeading);
385             mDeviceNameTextColumn.setContent(state.getDevice().getName());
386             mVisibleItems.add(mDeviceNameTextColumn);
387 
388             // Populate axes.
389             mVisibleItems.add(mAxesHeading);
390             final int axisCount = state.getAxisCount();
391             for (int i = 0; i < axisCount; i++) {
392                 final int axis = state.getAxis(i);
393                 final int id = BASE_ID_AXIS_ITEM | axis;
394                 TextColumn column = (TextColumn) mDataItems.get(id);
395                 if (column == null) {
396                     column = new TextColumn(id, MotionEvent.axisToString(axis));
397                     mDataItems.put(id, column);
398                 }
399                 column.setContent(Float.toString(state.getAxisValue(i)));
400                 mVisibleItems.add(column);
401             }
402 
403             // Populate keys.
404             mVisibleItems.add(mKeysHeading);
405             final int keyCount = state.getKeyCount();
406             for (int i = 0; i < keyCount; i++) {
407                 final int keyCode = state.getKeyCode(i);
408                 final int id = BASE_ID_KEY_ITEM | keyCode;
409                 TextColumn column = (TextColumn) mDataItems.get(id);
410                 if (column == null) {
411                     column = new TextColumn(id, KeyEvent.keyCodeToString(keyCode));
412                     mDataItems.put(id, column);
413                 }
414                 column.setContent(mResources.getString(state.isKeyPressed(i)
415                         ? R.string.game_controller_input_key_pressed
416                         : R.string.game_controller_input_key_released));
417                 mVisibleItems.add(column);
418             }
419 
420             notifyDataSetChanged();
421         }
422 
423         @Override
hasStableIds()424         public boolean hasStableIds() {
425             return true;
426         }
427 
428         @Override
getCount()429         public int getCount() {
430             return mVisibleItems.size();
431         }
432 
433         @Override
getItem(int position)434         public Item getItem(int position) {
435             return mVisibleItems.get(position);
436         }
437 
438         @Override
getItemId(int position)439         public long getItemId(int position) {
440             return getItem(position).getItemId();
441         }
442 
443         @Override
getView(int position, View convertView, ViewGroup parent)444         public View getView(int position, View convertView, ViewGroup parent) {
445             return getItem(position).getView(convertView, parent);
446         }
447 
448         private static abstract class Item {
449             private final int mItemId;
450             private final int mLayoutResourceId;
451             private View mView;
452 
Item(int itemId, int layoutResourceId)453             public Item(int itemId, int layoutResourceId) {
454                 mItemId = itemId;
455                 mLayoutResourceId = layoutResourceId;
456             }
457 
getItemId()458             public long getItemId() {
459                 return mItemId;
460             }
461 
getView(View convertView, ViewGroup parent)462             public View getView(View convertView, ViewGroup parent) {
463                 if (mView == null) {
464                     LayoutInflater inflater = (LayoutInflater)
465                             parent.getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
466                     mView = inflater.inflate(mLayoutResourceId, parent, false);
467                     initView(mView);
468                 }
469                 updateView(mView);
470                 return mView;
471             }
472 
initView(View view)473             protected void initView(View view) {
474             }
475 
updateView(View view)476             protected void updateView(View view) {
477             }
478         }
479 
480         private static class Heading extends Item {
481             private final String mLabel;
482 
Heading(int itemId, String label)483             public Heading(int itemId, String label) {
484                 super(itemId, R.layout.game_controller_input_heading);
485                 mLabel = label;
486             }
487 
488             @Override
initView(View view)489             public void initView(View view) {
490                 TextView textView = (TextView) view;
491                 textView.setText(mLabel);
492             }
493         }
494 
495         private static class TextColumn extends Item {
496             private final String mLabel;
497 
498             private String mContent;
499             private TextView mContentView;
500 
TextColumn(int itemId, String label)501             public TextColumn(int itemId, String label) {
502                 super(itemId, R.layout.game_controller_input_text_column);
503                 mLabel = label;
504             }
505 
setContent(String content)506             public void setContent(String content) {
507                 mContent = content;
508             }
509 
510             @Override
initView(View view)511             public void initView(View view) {
512                 TextView textView = (TextView) view.findViewById(R.id.label);
513                 textView.setText(mLabel);
514 
515                 mContentView = (TextView) view.findViewById(R.id.content);
516             }
517 
518             @Override
updateView(View view)519             public void updateView(View view) {
520                 mContentView.setText(mContent);
521             }
522         }
523     }
524 }
525