<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/main"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <ImageView
        android:id="@+id/iv_background"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:scaleType="centerCrop"
        android:src="@drawable/bg_general_day"
        android:visibility="visible" />

    <SurfaceView
        android:id="@+id/surface_view"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:background="#00000000"
        android:visibility="visible" />

</RelativeLayout>

<style name="AppTheme.FullScreen" parent="Theme.AppCompat.Light.NoActionBar">
    <item name="android:windowFullscreen">true</item>
</style>

public class GameActivity extends AppCompatActivity implements Callback,
		OnTouchListener {
 // …

@Override
 protected void onCreate(Bundle savedInstanceState) {
   super.onCreate(savedInstanceState);
   setContentView(R.layout.activity_game);
   ivBackground = (ImageView) findViewById(R.id.iv_background);
   surfaceView = (SurfaceView) findViewById(R.id.surface_view);
   surfaceView.setKeepScreenOn(true);
   holder = surfaceView.getHolder();
   surfaceView.setZOrderOnTop(true);
   surfaceView.setOnTouchListener(this);
   holder.addCallback(this);
   holder.setFormat(PixelFormat.TRANSLUCENT);
   loadRes();
 }

private void loadRes() {
   Resources res = getResources();
   blockerUp = res.getDrawable(R.drawable.img_block_up);
   blockerDown = res.getDrawable(R.drawable.img_block_down);
   coin = res.getDrawable(R.drawable.img_coin);
   soundPool = new SoundPool(5, AudioManager.STREAM_MUSIC, 0);
   AssetManager assetManager = res.getAssets();
   soundIds = new int[5];
   
   try {
   	 soundIds[SOUND_DIE] = soundPool.load(
		assetManager.openFd("sfx_die.ogg"), 1);
   	 soundIds[SOUND_HIT] = soundPool.load(
		assetManager.openFd("sfx_hit.ogg"), 1);
   	 soundIds[SOUND_POINT] = soundPool.load(
		assetManager.openFd("sfx_point.ogg"), 1);
   	 soundIds[SOUND_SWOOSHING] = soundPool.load(
		assetManager.openFd("sfx_swooshing.ogg"), 1);
   	 soundIds[SOUND_WING] = soundPool.load(
		assetManager.openFd("sfx_wing.ogg"), 1);
   } catch (IOException e) {
   }
 }

// …
@Override
 public void surfaceCreated(SurfaceHolder holder) {
   surfaceCreated = true;
   startDrawingThread();
 }

 @Override
 public void surfaceDestroyed(SurfaceHolder holder) {
   surfaceCreated = false;
   stopDrawingThread();
 }

private void startDrawingThread() {
   stopDrawingThread();
   drawingThread = new DrawingThread();
   drawingThread.start();
 }

 private void stopDrawingThread() {
   if (drawingThread != null) {
     drawingThread.interrupt();
     
     try {
         drawingThread.join();
     } catch (InterruptedException e) { }
     
     drawingThread = null;
   }
 }

private class DrawingThread extends Thread {
   @Override
   public void run() {
     super.run();
     workSprites = new LinkedList<Sprite>(sprites);
     
     while (!Thread.interrupted()) {
       long startTime = System.currentTimeMillis();
       Canvas canvas = holder.lockCanvas();
       
       try {
         cleanCanvas(canvas);
         Iterator<Sprite> iterator = workSprites.iterator();

         while (iterator.hasNext()) {
           Sprite sprite = iterator.next();
           
           if (sprite.isAlive()) {
             sprite.onDraw(canvas, globalPaint, currentStatus);
           } else {
             iterator.remove();
           }
         }
       } finally {
         holder.unlockCanvasAndPost(canvas);
       }
       
       long duration = (System.currentTimeMillis() - startTime);
       long gap = GAP - duration;

       if (gap > 0) {
         try {
           sleep(gap);
         } catch (Exception e) {
           break;
         }
       }

       // … mises à jour de l’environnement du jeu

     } // fin du while
   }
}

public interface Sprite {

  void onDraw(Canvas canvas, Paint globalPaint, int status);
  boolean isAlive();
  boolean isHit(Sprite sprite);
  int getScore();

}

public class BirdSprite implements Sprite {

  private static final int[][] DRAWABLE_BIRD = new int[][] {
   new int[] { R.drawable.img_bird_blue_1, R.drawable.img_bird_blue_2,
      R.drawable.img_bird_blue_3 },
   new int[] { R.drawable.img_bird_yellow_1,
      R.drawable.img_bird_yellow_2, R.drawable.img_bird_yellow_3 },
   new int[] { R.drawable.img_bird_red_1, R.drawable.img_bird_red_2,
      R.drawable.img_bird_red_3 } };
  private static Random random = new Random();
  private static final int FLY_COUNT = 6;
  private int count = 0;
  private Drawable birds[] = new Drawable[4];
  private final int X;
  private int width, height, currentHeight;
  private int birdHeight, birdWidth;
  private float currentSpeed, acceleration, tapSpeed;
  private int maxHeight, hitPaddingTop, 
              hitPaddingBottom, hitPaddingRight, hitPaddingLeft;

  public BirdSprite(Context context) {
    Resources res = context.getResources();
    int currentBird = random.nextInt(DRAWABLE_BIRD.length);
    birds[0] = birds[2] = res.getDrawable(DRAWABLE_BIRD[currentBird][0]);
    birds[1] = res.getDrawable(DRAWABLE_BIRD[currentBird][1]);
    birds[3] = res.getDrawable(DRAWABLE_BIRD[currentBird][2]);

    birdHeight = ViewUtil.dipResourceToPx(context, R.dimen.bird_height);
    birdWidth = birdHeight * birds[0].getIntrinsicWidth() / birds[0].getIntrinsicHeight();
    width = ViewUtil.getScreenWidth(context);
    height = ViewUtil.getScreenHeight(context);
    int xPosition = ViewUtil.dipResourceToPx(context, R.dimen.bird_position_x);
    X = width / 2 - birdWidth / 2 - xPosition;
    currentHeight = height / 2 - birdHeight / 2;

    // Chargement des constantes
    acceleration = ViewUtil.dipResourceToFloat(context, 
       R.dimen.bird_acceleration);
    tapSpeed = ViewUtil.dipResourceToFloat(context, R.dimen.bird_tap_speed);
    maxHeight = height - ViewUtil.dipResourceToPx(context, R.dimen.ground_height);
    hitPaddingBottom = ViewUtil.dipResourceToPx(context,
       R.dimen.bird_hit_padding_bottom);
    hitPaddingTop = ViewUtil.dipResourceToPx(context, 
       R.dimen.bird_hit_padding_top);
    hitPaddingLeft = ViewUtil.dipResourceToPx(context, 
       R.dimen.bird_hit_padding_left);
    hitPaddingRight = ViewUtil.dipResourceToPx(context, 
       R.dimen.bird_hit_padding_right);
    currentSpeed = 0;
  }

  public int getHitLeft() {
    return X + hitPaddingLeft;
  }

  public int getHitTop() {
    return currentHeight + hitPaddingTop;
  }

  public int getHitBottom() {
    return currentHeight + birdHeight - hitPaddingBottom;
  }

  public int getHitRight() {
    return X + birdWidth - hitPaddingRight;
  }

  @Override
  public void onDraw(Canvas canvas, Paint globalPaint, int status) {
    if (count >= 4 * FLY_COUNT) {
      count = 0;
    }

    if (status != Sprite.STATUS_NOT_STARTED) {
      currentHeight += currentSpeed;

      synchronized (this) {
        currentSpeed += acceleration;
      }
    }

    if (currentHeight <= 0) {
      currentHeight = 0;
    }

    if (currentHeight + birdHeight > maxHeight) {
      currentHeight = maxHeight - birdHeight;
    }

    Drawable bird = null;

    if (status == Sprite.STATUS_GAME_OVER) {
      bird = birds[0];
    } else {
      bird = birds[(count++) / FLY_COUNT];
    }

    bird.setBounds(X, currentHeight, X + 
       birdWidth, currentHeight + birdHeight);
    bird.draw(canvas);
  }

  @Override
  public boolean isAlive() {
    return true;
  }

  @Override
  public boolean isHit(Sprite sprite) {
    return currentHeight + birdHeight >= maxHeight;
  }

  public void onTap() {
    synchronized (this) {
      currentSpeed = tapSpeed;
    }
  }

  @Override
  public int getScore() {
    return 0;
  }

  public int getX() {
    return X;
  }
}

boolean hit = false;

for (Sprite sprite : workSprites) {
  if (sprite.isHit(birdSprite)) {
    onHit();
    hit = true;
    break;
  }
}

if (hit) {
  workSprites.addLast(splashSprite = new SplashSprite());
  currentStatus = Sprite.STATUS_GAME_OVER;
  continue;
}

if (currentStatus == Sprite.STATUS_NOT_STARTED) {
   continue;
}

@Override
public boolean onTouch(View v, MotionEvent event) {
  if (event.getAction() == MotionEvent.ACTION_DOWN) {
    switch (currentStatus) {
      case Sprite.STATUS_NOT_STARTED:
        currentStatus = Sprite.STATUS_NORMAL;
      case Sprite.STATUS_NORMAL:
        birdSprite.onTap();
        soundPool.play(soundIds[SOUND_WING], 0.5f, 0.5f, 1, 0, 1);
        break;
      case Sprite.STATUS_GAME_OVER:
        switch (gameOverSprite.onTap((int) event.getX(), (int) event.getY())) {
          case PLAY:
            playSwooshing();
            restart();
            break;
          case SHARE:
            share();
            break;
          default:
            break;
        }
        break;
      default:
        break;
    }
  }

  return false;
}

@Override
public boolean isHit(Sprite sprite) {
  if (sprite instanceof BirdSprite) {
    BirdSprite b = (BirdSprite) sprite;
    int bTop = b.getHitTop();
    int bBottom = b.getHitBottom();
    int bRight = b.getHitRight();
    int bLeft = b.getHitLeft();
    int left = (int) currentX;
    int right = (int) currentX + blockWidth;
    return (bTop < currentUpHeight || bBottom > currentUpHeight + gap)
		&& ((bRight > left && bRight < right) || (bLeft > left && bLeft < right));
  } else {
    return false;
  }
}

private void onHit() {
  runOnUiThread(new Runnable() {
	@Override
	public void run() {
	  if (!isFinishing()) {
	     soundPool.play(soundIds[SOUND_HIT], 0.5f, 0.5f, 1, 0, 1);
	  }
	}

public class Scores {

  private static final String PREF_DEFAULT = "com.ssaurel.flyingbird.PREF_DEFAULT";
  private static final String HIGH_SCORE = "high_score";
  public static boolean NEW_BEST_SCORE = false;

  public static int highScore(Context context) {
    SharedPreferences p = context.getSharedPreferences(PREF_DEFAULT,
				Context.MODE_PRIVATE);
    return p.getInt(HIGH_SCORE, 0);
  }

  public static void storeHighScore(Context context, int score) {
    NEW_BEST_SCORE = true;
    SharedPreferences p = context.getSharedPreferences(PREF_DEFAULT,
				Context.MODE_PRIVATE);
    p.edit().
      putInt(HIGH_SCORE, score).
      commit();
  }
}