gt:                 if (mLastInputDevice == null) {
context: /*
   * Copyright (C) 2011 The Android Open Source Project
   *
   * Licensed under the Apache License, Version 2.0 (the "License");
   * you may not use this file except in compliance with the License.
   * You may obtain a copy of the License at
   *
   *      http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   * See the License for the specific language governing permissions and
   * limitations under the License.
   */
  
  package com.example.android.apis.view;
  
  import android.content.Context;
  import android.graphics.Canvas;
  import android.graphics.Paint;
  import android.graphics.Path;
  import android.graphics.Paint.Style;
  import android.os.Handler;
  import android.os.SystemClock;
  import android.util.AttributeSet;
  import android.view.InputDevice;
  import android.view.KeyEvent;
  import android.view.MotionEvent;
  import android.view.View;
  
  import java.util.ArrayList;
  import java.util.List;
  import java.util.Random;
  
  /**
   * A trivial joystick based physics game to demonstrate joystick handling.
   *
   * @see GameControllerInput
   */
  public class GameView extends View {
      private final long ANIMATION_TIME_STEP = 1000 / 60;
      private final int MAX_OBSTACLES = 12;
  
      private final Random mRandom;
      private Ship mShip;
      private final List<Bullet> mBullets;
      private final List<Obstacle> mObstacles;
  
      private long mLastStepTime;
      private InputDevice mLastInputDevice;
  
      private static final int DPAD_STATE_LEFT  = 1 << 0;
      private static final int DPAD_STATE_RIGHT = 1 << 1;
      private static final int DPAD_STATE_UP    = 1 << 2;
      private static final int DPAD_STATE_DOWN  = 1 << 3;
  
      private int mDPadState;
  
      private float mShipSize;
      private float mMaxShipThrust;
      private float mMaxShipSpeed;
  
      private float mBulletSize;
      private float mBulletSpeed;
  
      private float mMinObstacleSize;
      private float mMaxObstacleSize;
      private float mMinObstacleSpeed;
      private float mMaxObstacleSpeed;
  
      private final Runnable mAnimationRunnable = new Runnable() {
          public void run() {
              animateFrame();
          }
      };
  
      public GameView(Context context, AttributeSet attrs) {
          super(context, attrs);
  
          mRandom = new Random();
          mBullets = new ArrayList<Bullet>();
          mObstacles = new ArrayList<Obstacle>();
  
          setFocusable(true);
          setFocusableInTouchMode(true);
  
          float baseSize = getContext().getResources().getDisplayMetrics().density * 5f;
          float baseSpeed = baseSize * 3;
  
          mShipSize = baseSize * 3;
          mMaxShipThrust = baseSpeed * 0.25f;
          mMaxShipSpeed = baseSpeed * 12;
  
          mBulletSize = baseSize;
          mBulletSpeed = baseSpeed * 12;
  
          mMinObstacleSize = baseSize * 2;
          mMaxObstacleSize = baseSize * 12;
          mMinObstacleSpeed = baseSpeed;
          mMaxObstacleSpeed = baseSpeed * 3;
      }
  
      @Override
      protected void onSizeChanged(int w, int h, int oldw, int oldh) {
          super.onSizeChanged(w, h, oldw, oldh);
  
          // Reset the game when the view changes size.
          reset();
      }
  
      @Override
      public boolean onKeyDown(int keyCode, KeyEvent event) {
          ensureInitialized();
  
          // Handle DPad keys and fire button on initial down but not on auto-repeat.
          boolean handled = false;
          if (event.getRepeatCount() == 0) {
              switch (keyCode) {
                  case KeyEvent.KEYCODE_DPAD_LEFT:
                      mShip.setHeadingX(-1);
                      mDPadState |= DPAD_STATE_LEFT;
                      handled = true;
                      break;
                  case KeyEvent.KEYCODE_DPAD_RIGHT:
                      mShip.setHeadingX(1);
                      mDPadState |= DPAD_STATE_RIGHT;
                      handled = true;
                      break;
                  case KeyEvent.KEYCODE_DPAD_UP:
                      mShip.setHeadingY(-1);
                      mDPadState |= DPAD_STATE_UP;
                      handled = true;
                      break;
                  case KeyEvent.KEYCODE_DPAD_DOWN:
                      mShip.setHeadingY(1);
                      mDPadState |= DPAD_STATE_DOWN;
                      handled = true;
                      break;
                  default:
                      if (isFireKey(keyCode)) {
                          fire();
                          handled = true;
                      }
                      break;
              }
          }
          if (handled) {
              step(event.getEventTime());
              return true;
          }
          return super.onKeyDown(keyCode, event);
      }
  
      @Override
      public boolean onKeyUp(int keyCode, KeyEvent event) {
          ensureInitialized();
  
          // Handle keys going up.
          boolean handled = false;
          switch (keyCode) {
              case KeyEvent.KEYCODE_DPAD_LEFT:
                  mShip.setHeadingX(0);
                  mDPadState &= ~DPAD_STATE_LEFT;
                  handled = true;
                  break;
              case KeyEvent.KEYCODE_DPAD_RIGHT:
                  mShip.setHeadingX(0);
                  mDPadState &= ~DPAD_STATE_RIGHT;
                  handled = true;
                  break;
              case KeyEvent.KEYCODE_DPAD_UP:
                  mShip.setHeadingY(0);
                  mDPadState &= ~DPAD_STATE_UP;
                  handled = true;
                  break;
              case KeyEvent.KEYCODE_DPAD_DOWN:
                  mShip.setHeadingY(0);
                  mDPadState &= ~DPAD_STATE_DOWN;
                  handled = true;
                  break;
              default:
                  if (isFireKey(keyCode)) {
                      handled = true;
                  }
                  break;
          }
          if (handled) {
              step(event.getEventTime());
              return true;
          }
          return super.onKeyUp(keyCode, event);
      }
  
      private static boolean isFireKey(int keyCode) {
          return KeyEvent.isGamepadButton(keyCode)
                  || keyCode == KeyEvent.KEYCODE_DPAD_CENTER
                  || keyCode == KeyEvent.KEYCODE_SPACE;
      }
  
      @Override
      public boolean onGenericMotionEvent(MotionEvent event) {
          ensureInitialized();
  
          // Check that the event came from a joystick since a generic motion event
          // could be almost anything.
          if ((event.getSource() & InputDevice.SOURCE_CLASS_JOYSTICK) != 0
                  && event.getAction() == MotionEvent.ACTION_MOVE) {
              // Cache the most recently obtained device information.
              // The device information may change over time but it can be
              // somewhat expensive to query.
              if (mLastInputDevice == null || mLastInputDevice.getId() != event.getDeviceId()) {
                  mLastInputDevice = event.getDevice();
                  // It's possible for the device id to be invalid.
                  // In that case, getDevice() will return null.
  