Merge change 2768 into donut

* changes:
  Add an API demo that shows the effets of setting Bitmaps as being purgeable.
diff --git a/samples/ApiDemos/AndroidManifest.xml b/samples/ApiDemos/AndroidManifest.xml
index d353d31..fb34afa 100644
--- a/samples/ApiDemos/AndroidManifest.xml
+++ b/samples/ApiDemos/AndroidManifest.xml
@@ -1612,6 +1612,20 @@
                 <category android:name="android.intent.category.SAMPLE_CODE" />
             </intent-filter>
         </activity>
+        
+        <activity android:name=".graphics.PurgeableBitmap" android:label="Graphics/PurgeableBitmap/NonPurgeable">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.SAMPLE_CODE" />
+            </intent-filter>
+        </activity>
+
+       <activity-alias android:targetActivity=".graphics.PurgeableBitmap" android:name="Purgeable" android:label="Graphics/PurgeableBitmap/Purgeable">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.SAMPLE_CODE" />
+            </intent-filter>
+        </activity-alias>
 
         <!-- ************************************* -->
         <!--             MEDIA SAMPLES             -->
diff --git a/samples/ApiDemos/src/com/example/android/apis/graphics/PurgeableBitmap.java b/samples/ApiDemos/src/com/example/android/apis/graphics/PurgeableBitmap.java
new file mode 100644
index 0000000..14ec8c4
--- /dev/null
+++ b/samples/ApiDemos/src/com/example/android/apis/graphics/PurgeableBitmap.java
@@ -0,0 +1,118 @@
+/*
+ * Copyright (C) 2009 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.graphics;
+
+import android.app.AlertDialog;
+import android.content.DialogInterface;
+import android.content.pm.ActivityInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Message;
+
+/**
+ * PurgeableBitmap demonstrates the effects of setting Bitmaps as being
+ * purgeable.
+ *
+ * In the NonPurgeable case, an encoded bitstream is decoded to a different
+ * Bitmap over and over again up to 200 times until out-of-memory occurs.
+ * In contrast, the Purgeable case shows that the system can complete decoding
+ * the encoded bitstream 200 times without hitting the out-of-memory case.
+ */
+public class PurgeableBitmap extends GraphicsActivity {
+
+    private PurgeableBitmapView mView;
+    private final RefreshHandler mRedrawHandler = new RefreshHandler();
+
+    class RefreshHandler extends Handler {
+
+        @Override
+        public void handleMessage(Message msg) {
+            int index = mView.update(this);
+            if (index > 0) {
+                showAlertDialog(getDialogMessage(true, index));
+            } else if (index < 0){
+                mView.invalidate();
+                showAlertDialog(getDialogMessage(false, -index));
+            } else {
+              mView.invalidate();
+            }
+        }
+
+        public void sleep(long delayMillis) {
+            this.removeMessages(0);
+            sendMessageDelayed(obtainMessage(0), delayMillis);
+        }
+    }
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        mView = new PurgeableBitmapView(this,  detectIfPurgeableRequest());
+        mRedrawHandler.sleep(0);
+        setContentView(mView);
+    }
+
+    private boolean detectIfPurgeableRequest() {
+        PackageManager pm = getPackageManager();
+        CharSequence labelSeq = null;
+        try {
+          ActivityInfo info = pm.getActivityInfo(this.getComponentName(),
+              PackageManager.GET_META_DATA);
+          labelSeq = info.loadLabel(pm);
+        } catch (NameNotFoundException e) {
+          e.printStackTrace();
+          return false;
+        }
+
+        String[] components = labelSeq.toString().split("/");
+        if (components[components.length - 1].equals("Purgeable")) {
+            return true;
+        } else {
+            return false;
+        }
+    }
+
+    private String getDialogMessage(boolean isOutOfMemory, int index) {
+         StringBuilder sb = new StringBuilder();
+         if (isOutOfMemory) {
+             sb.append("Out of memery occurs when the ");
+             sb.append(index);
+             sb.append("th Bitmap is decoded.");
+         } else {
+             sb.append("Complete decoding ")
+               .append(index)
+               .append(" bitmaps without running out of memory.");
+         }
+         return sb.toString();
+    }
+
+    private void showAlertDialog(String message) {
+      AlertDialog.Builder builder = new AlertDialog.Builder(this);
+      builder.setMessage(message)
+             .setCancelable(false)
+             .setPositiveButton("Yes", new DialogInterface.OnClickListener() {
+                 public void onClick(DialogInterface dialog, int id) {
+                                 }
+             });
+      AlertDialog alert = builder.create();
+      alert.show();
+    }
+
+
+}
diff --git a/samples/ApiDemos/src/com/example/android/apis/graphics/PurgeableBitmapView.java b/samples/ApiDemos/src/com/example/android/apis/graphics/PurgeableBitmapView.java
new file mode 100644
index 0000000..2105628
--- /dev/null
+++ b/samples/ApiDemos/src/com/example/android/apis/graphics/PurgeableBitmapView.java
@@ -0,0 +1,116 @@
+/*
+ * Copyright (C) 2009 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.graphics;
+
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.graphics.BitmapFactory.Options;
+import android.view.View;
+
+import java.io.ByteArrayOutputStream;
+
+/**
+ * PurgeableBitmapView works with PurgeableBitmap to demonstrate the effects of setting
+ * Bitmaps as being purgeable.
+ *
+ * PurgeableBitmapView decodes an encoded bitstream to a Bitmap each time update()
+ * is invoked(), and its onDraw() draws the Bitmap and a number to screen.
+ * The number is used to indicate the number of Bitmaps that has been decoded.
+ */
+public class PurgeableBitmapView extends View {
+    private final byte[] bitstream;
+
+    private Bitmap mBitmap;
+    private final int mArraySize = 200;
+    private final Bitmap[] mBitmapArray = new Bitmap [mArraySize];
+    private final Options mOptions = new Options();
+    private static final int WIDTH = 150;
+    private static final int HEIGHT = 450;
+    private static final int STRIDE = 320;   // must be >= WIDTH
+    private int mDecodingCount = 0;
+    private final Paint mPaint = new Paint();
+    private final int textSize = 32;
+    private static int delay = 100;
+
+    public PurgeableBitmapView(Context context, boolean isPurgeable) {
+        super(context);
+        setFocusable(true);
+        mOptions.inPurgeable = isPurgeable;
+
+        int[] colors = createColors();
+        Bitmap src = Bitmap.createBitmap(colors, 0, STRIDE, WIDTH, HEIGHT,
+                Bitmap.Config.ARGB_8888);
+        bitstream = generateBitstream(src, Bitmap.CompressFormat.JPEG, 80);
+
+        mPaint.setTextSize(textSize);
+        mPaint.setColor(Color.GRAY);
+    }
+
+    private int[] createColors() {
+        int[] colors = new int[STRIDE * HEIGHT];
+        for (int y = 0; y < HEIGHT; y++) {
+            for (int x = 0; x < WIDTH; x++) {
+                int r = x * 255 / (WIDTH - 1);
+                int g = y * 255 / (HEIGHT - 1);
+                int b = 255 - Math.min(r, g);
+                int a = Math.max(r, g);
+                colors[y * STRIDE + x] = (a << 24) | (r << 16) | (g << 8) | b;
+            }
+        }
+        return colors;
+    }
+
+    public int update(PurgeableBitmap.RefreshHandler handler) {
+        try {
+            mBitmapArray[mDecodingCount] = BitmapFactory.decodeByteArray(
+                bitstream, 0, bitstream.length, mOptions);
+            mBitmap = mBitmapArray[mDecodingCount];
+            mDecodingCount++;
+            if (mDecodingCount < mArraySize) {
+                handler.sleep(delay);
+                return 0;
+            } else {
+                return -mDecodingCount;
+            }
+
+        } catch (OutOfMemoryError error) {
+            for (int i = 0; i < mDecodingCount; i++) {
+                mBitmapArray[i].recycle();
+            }
+            return mDecodingCount + 1;
+        }
+    }
+
+    @Override protected void onDraw(Canvas canvas) {
+        canvas.drawColor(Color.WHITE);
+        canvas.drawBitmap(mBitmap, 0, 0, null);
+        canvas.drawText(String.valueOf(mDecodingCount), WIDTH / 2 - 20,
+            HEIGHT / 2, mPaint);
+    }
+
+    private byte[] generateBitstream(Bitmap src, Bitmap.CompressFormat format,
+            int quality) {
+        ByteArrayOutputStream os = new ByteArrayOutputStream();
+        src.compress(format, quality, os);
+        return os.toByteArray();
+    }
+
+}