UI Android

/*
 * Copyright (C) 2007 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 app.test;
import android.app.Activity;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Picture;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.util.AttributeSet;
import android.view.Menu;
import android.view.MenuItem;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewParent;
//Need the following import to get access to the app resources, since this
//class is in a sub-package.
/**
* Demonstrates the handling of touch screen and trackball events to
* implement a simple painting app.
*/
public class Test extends GraphicsActivity {
  /** Used as a pulse to gradually fade the contents of the window. */
  private static final int FADE_MSG = 1;
  /** Menu ID for the command to clear the window. */
  private static final int CLEAR_ID = Menu.FIRST;
  /** Menu ID for the command to toggle fading. */
  private static final int FADE_ID = Menu.FIRST+1;
  /** How often to fade the contents of the window (in ms). */
  private static final int FADE_DELAY = 100;
  /** The view responsible for drawing the window. */
  MyView mView;
  /** Is fading mode enabled? */
  boolean mFading;
  @Override protected void onCreate(Bundle savedInstanceState) {
      super.onCreate(savedInstanceState);
      // Create and attach the view that is responsible for painting.
      mView = new MyView(this);
      setContentView(mView);
      mView.requestFocus();
      // Restore the fading option if we are being thawed from a
      // previously saved state.  Note that we are not currently remembering
      // the contents of the bitmap.
      mFading = savedInstanceState != null ? savedInstanceState.getBoolean("fading", true) : true;
  }
  @Override public boolean onCreateOptionsMenu(Menu menu) {
      menu.add(0, CLEAR_ID, 0, "Clear");
      menu.add(0, FADE_ID, 0, "Fade").setCheckable(true);
      return super.onCreateOptionsMenu(menu);
  }
  @Override public boolean onPrepareOptionsMenu(Menu menu) {
      menu.findItem(FADE_ID).setChecked(mFading);
      return super.onPrepareOptionsMenu(menu);
  }
  @Override public boolean onOptionsItemSelected(MenuItem item) {
      switch (item.getItemId()) {
          case CLEAR_ID:
              mView.clear();
              return true;
          case FADE_ID:
              mFading = !mFading;
              if (mFading) {
                  startFading();
              } else {
                  stopFading();
              }
              return true;
          default:
              return super.onOptionsItemSelected(item);
      }
  }
  @Override protected void onResume() {
      super.onResume();
      // If fading mode is enabled, then as long as we are resumed we want
      // to run pulse to fade the contents.
      if (mFading) {
          startFading();
      }
  }
  @Override protected void onSaveInstanceState(Bundle outState) {
      super.onSaveInstanceState(outState);
      // Save away the fading state to restore if needed later.  Note that
      // we do not currently save the contents of the display.
      outState.putBoolean("fading", mFading);
  }
  @Override protected void onPause() {
      super.onPause();
      // Make sure to never run the fading pulse while we are paused or
      // stopped.
      stopFading();
  }
  /**
   * Start up the pulse to fade the screen, clearing any existing pulse to
   * ensure that we don't have multiple pulses running at a time.
   */
  void startFading() {
      mHandler.removeMessages(FADE_MSG);
      mHandler.sendMessageDelayed(
              mHandler.obtainMessage(FADE_MSG), FADE_DELAY);
  }
  /**
   * Stop the pulse to fade the screen.
   */
  void stopFading() {
      mHandler.removeMessages(FADE_MSG);
  }
  private Handler mHandler = new Handler() {
      @Override public void handleMessage(Message msg) {
          switch (msg.what) {
              // Upon receiving the fade pulse, we have the view perform a
              // fade and then enqueue a new message to pulse at the desired
              // next time.
              case FADE_MSG: {
                  mView.fade();
                  mHandler.sendMessageDelayed(
                          mHandler.obtainMessage(FADE_MSG), FADE_DELAY);
                  break;
              }
              default:
                  super.handleMessage(msg);
          }
      }
  };
  public class MyView extends View {
      private static final int FADE_ALPHA = 0x06;
      private static final int MAX_FADE_STEPS = 256/FADE_ALPHA + 4;
      private static final int TRACKBALL_SCALE = 10;
      private Bitmap mBitmap;
      private Canvas mCanvas;
      private final Rect mRect = new Rect();
      private final Paint mPaint;
      private final Paint mFadePaint;
      private float mCurX;
      private float mCurY;
      private int mFadeSteps = MAX_FADE_STEPS;
      public MyView(Context c) {
          super(c);
          setFocusable(true);
          mPaint = new Paint();
          mPaint.setAntiAlias(true);
          mPaint.setARGB(255, 255, 255, 255);
          mFadePaint = new Paint();
          mFadePaint.setDither(true);
          mFadePaint.setARGB(FADE_ALPHA, 0, 0, 0);
      }
      public void clear() {
          if (mCanvas != null) {
              mPaint.setARGB(0xff, 0, 0, 0);
              mCanvas.drawPaint(mPaint);
              invalidate();
              mFadeSteps = MAX_FADE_STEPS;
          }
      }
      public void fade() {
          if (mCanvas != null && mFadeSteps < MAX_FADE_STEPS) {
              mCanvas.drawPaint(mFadePaint);
              invalidate();
              mFadeSteps++;
          }
      }
      @Override protected void onSizeChanged(int w, int h, int oldw,
              int oldh) {
          int curW = mBitmap != null ? mBitmap.getWidth() : 0;
          int curH = mBitmap != null ? mBitmap.getHeight() : 0;
          if (curW >= w && curH >= h) {
              return;
          }
          if (curW < w) curW = w;
          if (curH < h) curH = h;
          Bitmap newBitmap = Bitmap.createBitmap(curW, curH,
                                                 Bitmap.Config.RGB_565);
          Canvas newCanvas = new Canvas();
          newCanvas.setBitmap(newBitmap);
          if (mBitmap != null) {
              newCanvas.drawBitmap(mBitmap, 0, 0, null);
          }
          mBitmap = newBitmap;
          mCanvas = newCanvas;
          mFadeSteps = MAX_FADE_STEPS;
      }
      @Override protected void onDraw(Canvas canvas) {
          if (mBitmap != null) {
              canvas.drawBitmap(mBitmap, 0, 0, null);
          }
      }
      @Override public boolean onTrackballEvent(MotionEvent event) {
          int N = event.getHistorySize();
          final float scaleX = event.getXPrecision() * TRACKBALL_SCALE;
          final float scaleY = event.getYPrecision() * TRACKBALL_SCALE;
          for (int i=0; i              //Log.i("TouchPaint", "Intermediate trackball #" + i
              //        + ": x=" + event.getHistoricalX(i)
              //        + ", y=" + event.getHistoricalY(i));
              mCurX += event.getHistoricalX(i) * scaleX;
              mCurY += event.getHistoricalY(i) * scaleY;
              drawPoint(mCurX, mCurY, 1.0f, 16.0f);
          }
          //Log.i("TouchPaint", "Trackball: x=" + event.getX()
          //        + ", y=" + event.getY());
          mCurX += event.getX() * scaleX;
          mCurY += event.getY() * scaleY;
          drawPoint(mCurX, mCurY, 1.0f, 16.0f);
          return true;
      }
      @Override public boolean onTouchEvent(MotionEvent event) {
          int action = event.getActionMasked();
          if (action != MotionEvent.ACTION_UP && action != MotionEvent.ACTION_CANCEL) {
              int N = event.getHistorySize();
              int P = event.getPointerCount();
              for (int i = 0; i < N; i++) {
                  for (int j = 0; j < P; j++) {
                      mCurX = event.getHistoricalX(j, i);
                      mCurY = event.getHistoricalY(j, i);
                      drawPoint(mCurX, mCurY,
                              event.getHistoricalPressure(j, i),
                              event.getHistoricalTouchMajor(j, i));
                  }
              }
              for (int j = 0; j < P; j++) {
                  mCurX = event.getX(j);
                  mCurY = event.getY(j);
                  drawPoint(mCurX, mCurY, event.getPressure(j), event.getTouchMajor(j));
              }
          }
          return true;
      }
      private void drawPoint(float x, float y, float pressure, float width) {
          //Log.i("TouchPaint", "Drawing: " + x + "x" + y + " p="
          //        + pressure + " width=" + width);
          if (width < 1) width = 1;
          if (mBitmap != null) {
              float radius = width / 2;
              int pressureLevel = (int)(pressure * 255);
              mPaint.setARGB(pressureLevel, 255, 255, 255);
              mCanvas.drawCircle(x, y, radius, mPaint);
              mRect.set((int) (x - radius - 2), (int) (y - radius - 2),
                      (int) (x + radius + 2), (int) (y + radius + 2));
              invalidate(mRect);
          }
          mFadeSteps = 0;
      }
  }
}
class GraphicsActivity extends Activity {
  // set to true to test Picture
  private static final boolean TEST_PICTURE = false;
  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
  }
  @Override
  public void setContentView(View view) {
    if (TEST_PICTURE) {
      ViewGroup vg = new PictureLayout(this);
      vg.addView(view);
      view = vg;
    }
    super.setContentView(view);
  }
}
class PictureLayout extends ViewGroup {
  private final Picture mPicture = new Picture();
  public PictureLayout(Context context) {
    super(context);
  }
  public PictureLayout(Context context, AttributeSet attrs) {
    super(context, attrs);
  }
  @Override
  public void addView(View child) {
    if (getChildCount() > 1) {
      throw new IllegalStateException(
          "PictureLayout can host only one direct child");
    }
    super.addView(child);
  }
  @Override
  public void addView(View child, int index) {
    if (getChildCount() > 1) {
      throw new IllegalStateException(
          "PictureLayout can host only one direct child");
    }
    super.addView(child, index);
  }
  @Override
  public void addView(View child, LayoutParams params) {
    if (getChildCount() > 1) {
      throw new IllegalStateException(
          "PictureLayout can host only one direct child");
    }
    super.addView(child, params);
  }
  @Override
  public void addView(View child, int index, LayoutParams params) {
    if (getChildCount() > 1) {
      throw new IllegalStateException(
          "PictureLayout can host only one direct child");
    }
    super.addView(child, index, params);
  }
  @Override
  protected LayoutParams generateDefaultLayoutParams() {
    return new LayoutParams(LayoutParams.MATCH_PARENT,
        LayoutParams.MATCH_PARENT);
  }
  @Override
  protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    final int count = getChildCount();
    int maxHeight = 0;
    int maxWidth = 0;
    for (int i = 0; i < count; i++) {
      final View child = getChildAt(i);
      if (child.getVisibility() != GONE) {
        measureChild(child, widthMeasureSpec, heightMeasureSpec);
      }
    }
    maxWidth += getPaddingLeft() + getPaddingRight();
    maxHeight += getPaddingTop() + getPaddingBottom();
    Drawable drawable = getBackground();
    if (drawable != null) {
      maxHeight = Math.max(maxHeight, drawable.getMinimumHeight());
      maxWidth = Math.max(maxWidth, drawable.getMinimumWidth());
    }
    setMeasuredDimension(resolveSize(maxWidth, widthMeasureSpec),
        resolveSize(maxHeight, heightMeasureSpec));
  }
  private void drawPict(Canvas canvas, int x, int y, int w, int h, float sx,
      float sy) {
    canvas.save();
    canvas.translate(x, y);
    canvas.clipRect(0, 0, w, h);
    canvas.scale(0.5f, 0.5f);
    canvas.scale(sx, sy, w, h);
    canvas.drawPicture(mPicture);
    canvas.restore();
  }
  @Override
  protected void dispatchDraw(Canvas canvas) {
    super.dispatchDraw(mPicture.beginRecording(getWidth(), getHeight()));
    mPicture.endRecording();
    int x = getWidth() / 2;
    int y = getHeight() / 2;
    if (false) {
      canvas.drawPicture(mPicture);
    } else {
      drawPict(canvas, 0, 0, x, y, 1, 1);
      drawPict(canvas, x, 0, x, y, -1, 1);
      drawPict(canvas, 0, y, x, y, 1, -1);
      drawPict(canvas, x, y, x, y, -1, -1);
    }
  }
  @Override
  public ViewParent invalidateChildInParent(int[] location, Rect dirty) {
    location[0] = getLeft();
    location[1] = getTop();
    dirty.set(0, 0, getWidth(), getHeight());
    return getParent();
  }
  @Override
  protected void onLayout(boolean changed, int l, int t, int r, int b) {
    final int count = super.getChildCount();
    for (int i = 0; i < count; i++) {
      final View child = getChildAt(i);
      if (child.getVisibility() != GONE) {
        final int childLeft = getPaddingLeft();
        final int childTop = getPaddingTop();
        child.layout(childLeft, childTop,
            childLeft + child.getMeasuredWidth(),
            childTop + child.getMeasuredHeight());
      }
    }
  }
}