First commit for ImagePixelization.

Change-Id: I02ab79d17cb32773a9bd1a4c50bc76bde8925368
(cherry picked from commit 4ea559806d2c4839e1c4019445c43c5fe41c9a0b)
diff --git a/samples/devbytes/graphics/ImagePixelization/AndroidManifest.xml b/samples/devbytes/graphics/ImagePixelization/AndroidManifest.xml
new file mode 100644
index 0000000..7bbb4f6
--- /dev/null
+++ b/samples/devbytes/graphics/ImagePixelization/AndroidManifest.xml
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+          package="com.example.android.imagepixelization"
+          android:versionCode="1"
+          android:versionName="1.0">
+    <uses-sdk android:minSdkVersion="11"
+              android:targetSdkVersion="17"/>
+    <application android:label="@string/app_name" android:icon="@drawable/ic_launcher">
+        <activity android:name=".ImagePixelization"
+                  android:label="@string/app_name">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN"/>
+                <category android:name="android.intent.category.LAUNCHER"/>
+            </intent-filter>
+        </activity>
+    </application>
+</manifest>
diff --git a/samples/devbytes/graphics/ImagePixelization/res/drawable-hdpi/ic_launcher.png b/samples/devbytes/graphics/ImagePixelization/res/drawable-hdpi/ic_launcher.png
new file mode 100644
index 0000000..96a442e
--- /dev/null
+++ b/samples/devbytes/graphics/ImagePixelization/res/drawable-hdpi/ic_launcher.png
Binary files differ
diff --git a/samples/devbytes/graphics/ImagePixelization/res/drawable-hdpi/image.jpg b/samples/devbytes/graphics/ImagePixelization/res/drawable-hdpi/image.jpg
new file mode 100644
index 0000000..00bdbba
--- /dev/null
+++ b/samples/devbytes/graphics/ImagePixelization/res/drawable-hdpi/image.jpg
Binary files differ
diff --git a/samples/devbytes/graphics/ImagePixelization/res/drawable-ldpi/ic_launcher.png b/samples/devbytes/graphics/ImagePixelization/res/drawable-ldpi/ic_launcher.png
new file mode 100644
index 0000000..9923872
--- /dev/null
+++ b/samples/devbytes/graphics/ImagePixelization/res/drawable-ldpi/ic_launcher.png
Binary files differ
diff --git a/samples/devbytes/graphics/ImagePixelization/res/drawable-mdpi/ic_launcher.png b/samples/devbytes/graphics/ImagePixelization/res/drawable-mdpi/ic_launcher.png
new file mode 100644
index 0000000..359047d
--- /dev/null
+++ b/samples/devbytes/graphics/ImagePixelization/res/drawable-mdpi/ic_launcher.png
Binary files differ
diff --git a/samples/devbytes/graphics/ImagePixelization/res/drawable-xhdpi/ic_launcher.png b/samples/devbytes/graphics/ImagePixelization/res/drawable-xhdpi/ic_launcher.png
new file mode 100644
index 0000000..71c6d76
--- /dev/null
+++ b/samples/devbytes/graphics/ImagePixelization/res/drawable-xhdpi/ic_launcher.png
Binary files differ
diff --git a/samples/devbytes/graphics/ImagePixelization/res/layout/activity_image_pixelization.xml b/samples/devbytes/graphics/ImagePixelization/res/layout/activity_image_pixelization.xml
new file mode 100644
index 0000000..b3e30a8
--- /dev/null
+++ b/samples/devbytes/graphics/ImagePixelization/res/layout/activity_image_pixelization.xml
@@ -0,0 +1,42 @@
+<!-- Copyright (C) 2013 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.
+-->
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+                xmlns:tools="http://schemas.android.com/tools"
+                android:layout_width="match_parent"
+                android:layout_height="match_parent"
+                android:paddingLeft="@dimen/activity_horizontal_margin"
+                android:paddingRight="@dimen/activity_horizontal_margin"
+                android:paddingTop="@dimen/activity_vertical_margin"
+                android:orientation="vertical">
+
+        <ImageView
+                android:id="@+id/pixelView"
+                android:layout_width="match_parent"
+                android:layout_height="match_parent"
+                android:layout_alignParentTop="true"
+                android:layout_above="@+id/seekbar"
+                android:scaleType="fitXY"/>
+
+        <SeekBar
+                android:id="@+id/seekbar"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:layout_margin="10dp"
+                android:max="@integer/seek_max"
+                android:progress="0"
+                android:layout_alignParentBottom = "true"/>
+
+</RelativeLayout>
+
diff --git a/samples/devbytes/graphics/ImagePixelization/res/menu/image_pixelization.xml b/samples/devbytes/graphics/ImagePixelization/res/menu/image_pixelization.xml
new file mode 100644
index 0000000..e6a8d66
--- /dev/null
+++ b/samples/devbytes/graphics/ImagePixelization/res/menu/image_pixelization.xml
@@ -0,0 +1,31 @@
+<!-- Copyright (C) 2013 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.
+-->
+<menu xmlns:android="http://schemas.android.com/apk/res/android" >
+
+    <item   android:id="@+id/builtin_pixelation_checkbox"
+            android:showAsAction="never"
+            android:title="@string/builtin_pixelization"
+            android:checkable="true" />
+
+    <item   android:id="@+id/checkbox"
+            android:showAsAction="never"
+            android:title="@string/async_task"
+            android:checkable="true" />
+
+    <item   android:id="@+id/animate"
+            android:showAsAction="never"
+            android:title="@string/animate_pixelation"/>
+
+</menu>
\ No newline at end of file
diff --git a/samples/devbytes/graphics/ImagePixelization/res/values/dimens.xml b/samples/devbytes/graphics/ImagePixelization/res/values/dimens.xml
new file mode 100644
index 0000000..09d93f5
--- /dev/null
+++ b/samples/devbytes/graphics/ImagePixelization/res/values/dimens.xml
@@ -0,0 +1,20 @@
+<!-- Copyright (C) 2013 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.
+-->
+<resources>
+
+    <dimen name="activity_horizontal_margin">16dp</dimen>
+    <dimen name="activity_vertical_margin">16dp</dimen>
+
+</resources>
diff --git a/samples/devbytes/graphics/ImagePixelization/res/values/integers.xml b/samples/devbytes/graphics/ImagePixelization/res/values/integers.xml
new file mode 100644
index 0000000..98c1438
--- /dev/null
+++ b/samples/devbytes/graphics/ImagePixelization/res/values/integers.xml
@@ -0,0 +1,19 @@
+<!-- Copyright (C) 2013 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.
+-->
+<resources>
+
+     <integer name="seek_max">1000</integer>
+
+</resources>
diff --git a/samples/devbytes/graphics/ImagePixelization/res/values/strings.xml b/samples/devbytes/graphics/ImagePixelization/res/values/strings.xml
new file mode 100644
index 0000000..35a87c8
--- /dev/null
+++ b/samples/devbytes/graphics/ImagePixelization/res/values/strings.xml
@@ -0,0 +1,22 @@
+<!-- Copyright (C) 2013 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.
+-->
+<resources>
+
+    <string name="app_name" >ImagePixelization</string>
+    <string name="animate_pixelation">Animate</string>
+    <string name="async_task">Using AyncTask</string>
+    <string name="builtin_pixelization">Built-in Pixelization</string>
+
+</resources>
diff --git a/samples/devbytes/graphics/ImagePixelization/src/com/example/android/imagepixelization/ImagePixelization.java b/samples/devbytes/graphics/ImagePixelization/src/com/example/android/imagepixelization/ImagePixelization.java
new file mode 100644
index 0000000..e31d95d
--- /dev/null
+++ b/samples/devbytes/graphics/ImagePixelization/src/com/example/android/imagepixelization/ImagePixelization.java
@@ -0,0 +1,315 @@
+/*
+ * Copyright (C) 2013 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.imagepixelization;
+
+import android.animation.ObjectAnimator;
+import android.app.Activity;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.Color;
+import android.graphics.drawable.BitmapDrawable;
+import android.os.AsyncTask;
+import android.os.Build;
+import android.os.Bundle;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.animation.LinearInterpolator;
+import android.widget.ImageView;
+import android.widget.SeekBar;
+
+import java.util.Arrays;
+
+/**
+ * This application shows three different graphics/animation concepts.
+ *
+ * A pixelization effect is applied to an image with varying pixelization
+ * factors to achieve an image that is pixelized to varying degrees. In
+ * order to optimize the amount of image processing performed on the image
+ * being pixelized, the pixelization effect only takes place if a predefined
+ * amount of time has elapsed since the main image was last pixelized. The
+ * effect is also applied when the user stops moving the seekbar.
+ *
+ * This application also shows how to use a ValueAnimator to achieve a
+ * smooth self-animating seekbar.
+ *
+ * Lastly, this application shows a use case of AsyncTask where some
+ * computation heavy processing can be moved onto a background thread,
+ * so as to keep the UI completely responsive to user input.
+ */
+public class ImagePixelization extends Activity {
+
+    final private static int SEEKBAR_ANIMATION_DURATION = 10000;
+    final private static int TIME_BETWEEN_TASKS = 400;
+    final private static int SEEKBAR_STOP_CHANGE_DELTA = 5;
+    final private static float PROGRESS_TO_PIXELIZATION_FACTOR = 4000.0f;
+
+    Bitmap mImageBitmap;
+    ImageView mImageView;
+    SeekBar mSeekBar;
+    boolean mIsChecked = false;
+    boolean mIsBuiltinPixelizationChecked = false;
+    int mLastProgress = 0;
+    long mLastTime = 0;
+    Bitmap mPixelatedBitmap;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.activity_image_pixelization);
+
+        mImageView = (ImageView) findViewById(R.id.pixelView);
+        mSeekBar = (SeekBar)findViewById(R.id.seekbar);
+
+        mImageBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.image);
+        mImageView.setImageBitmap(mImageBitmap);
+
+        mSeekBar.setOnSeekBarChangeListener(mOnSeekBarChangeListener);
+    }
+
+    private SeekBar.OnSeekBarChangeListener mOnSeekBarChangeListener =
+            new SeekBar.OnSeekBarChangeListener() {
+
+        @Override
+        public void onStopTrackingTouch(SeekBar seekBar) {
+            if (Math.abs(mSeekBar.getProgress() - mLastProgress) > SEEKBAR_STOP_CHANGE_DELTA) {
+                invokePixelization();
+            }
+        }
+
+        @Override
+        public void onStartTrackingTouch(SeekBar seekBar) {
+        }
+
+        @Override
+        public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
+            checkIfShouldPixelize();
+        }
+    };
+
+    /**
+     * Checks if enough time has elapsed since the last pixelization call was invoked.
+     * This prevents too many pixelization processes from being invoked at the same time
+     * while previous ones have not yet completed.
+     */
+    public void checkIfShouldPixelize() {
+        if ((System.currentTimeMillis() - mLastTime) > TIME_BETWEEN_TASKS) {
+            invokePixelization();
+        }
+    }
+
+    @Override
+    public boolean onCreateOptionsMenu(Menu menu) {
+        getMenuInflater().inflate(R.menu.image_pixelization, menu);
+        return true;
+    }
+
+    @Override
+    public boolean onOptionsItemSelected (MenuItem item) {
+        switch (item.getItemId()){
+            case R.id.animate:
+                ObjectAnimator animator = ObjectAnimator.ofInt(mSeekBar, "progress", 0,
+                        mSeekBar.getMax());
+                animator.setInterpolator(new LinearInterpolator());
+                animator.setDuration(SEEKBAR_ANIMATION_DURATION);
+                animator.start();
+                break;
+            case R.id.checkbox:
+                if (mIsChecked) {
+                    item.setChecked(false);
+                    mIsChecked = false;
+                } else {
+                    item.setChecked(true);
+                    mIsChecked = true;
+                }
+                break;
+            case R.id.builtin_pixelation_checkbox:
+                mIsBuiltinPixelizationChecked = !mIsBuiltinPixelizationChecked;
+                item.setChecked(mIsBuiltinPixelizationChecked);
+                break;
+            default:
+                break;
+        }
+        return true;
+    }
+
+    /**
+     * A simple pixelization algorithm. This uses a box blur algorithm where all the
+     * pixels within some region are averaged, and that average pixel value is then
+     * applied to all the pixels within that region. A higher pixelization factor
+     * imposes a smaller number of regions of greater size. Similarly, a smaller
+     * pixelization factor imposes a larger number of regions of smaller size.
+     */
+    public BitmapDrawable customImagePixelization(float pixelizationFactor, Bitmap bitmap) {
+
+        int width = bitmap.getWidth();
+        int height = bitmap.getHeight();
+
+        if (mPixelatedBitmap == null || !(width == mPixelatedBitmap.getWidth() && height ==
+                mPixelatedBitmap.getHeight())) {
+            mPixelatedBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
+        }
+
+        int xPixels = (int) (pixelizationFactor * ((float)width));
+        xPixels = xPixels > 0 ? xPixels : 1;
+        int yPixels = (int)  (pixelizationFactor * ((float)height));
+        yPixels = yPixels > 0 ? yPixels : 1;
+        int pixel = 0, red = 0, green = 0, blue = 0, numPixels = 0;
+
+        int[] bitmapPixels = new int[width * height];
+        bitmap.getPixels(bitmapPixels, 0, width, 0, 0, width, height);
+
+        int[] pixels = new int[yPixels * xPixels];
+
+        int maxX, maxY;
+
+        for (int y = 0; y < height; y+=yPixels) {
+            for (int x = 0; x < width; x+=xPixels) {
+
+                numPixels = red = green = blue = 0;
+
+                maxX = Math.min(x + xPixels, width);
+                maxY = Math.min(y + yPixels, height);
+
+                for (int i = x; i < maxX; i++) {
+                    for (int j = y; j < maxY; j++) {
+                        pixel = bitmapPixels[j * width + i];
+                        red += Color.red(pixel);
+                        green += Color.green(pixel);
+                        blue += Color.blue(pixel);
+                        numPixels ++;
+                    }
+                }
+
+                pixel = Color.rgb(red / numPixels, green / numPixels, blue / numPixels);
+
+                Arrays.fill(pixels, pixel);
+
+                int w = Math.min(xPixels, width - x);
+                int h = Math.min(yPixels, height - y);
+
+                mPixelatedBitmap.setPixels(pixels, 0 , w, x , y, w, h);
+            }
+        }
+
+        return new BitmapDrawable(getResources(), mPixelatedBitmap);
+    }
+
+    /**
+     * This method of image pixelization utilizes the bitmap scaling operations built
+     * into the framework. By downscaling the bitmap and upscaling it back to its
+     * original size (while setting the filter flag to false), the same effect can be
+     * achieved with much better performance.
+     */
+    public BitmapDrawable builtInPixelization(float pixelizationFactor, Bitmap bitmap) {
+
+        int width = bitmap.getWidth();
+        int height = bitmap.getHeight();
+
+        int downScaleFactorWidth = (int)(pixelizationFactor * width);
+        downScaleFactorWidth = downScaleFactorWidth > 0 ? downScaleFactorWidth : 1;
+        int downScaleFactorHeight = (int)(pixelizationFactor * height);
+        downScaleFactorHeight = downScaleFactorHeight > 0 ? downScaleFactorHeight : 1;
+
+        int downScaledWidth =  width / downScaleFactorWidth;
+        int downScaledHeight = height / downScaleFactorHeight;
+
+        Bitmap pixelatedBitmap = Bitmap.createScaledBitmap(bitmap, downScaledWidth,
+                downScaledHeight, false);
+
+        /* Bitmap's createScaledBitmap method has a filter parameter that can be set to either
+         * true or false in order to specify either bilinear filtering or point sampling
+         * respectively when the bitmap is scaled up or now.
+         *
+         * Similarly, a BitmapDrawable also has a flag to specify the same thing. When the
+         * BitmapDrawable is applied to an ImageView that has some scaleType, the filtering
+         * flag is taken into consideration. However, for optimization purposes, this flag was
+         * ignored in BitmapDrawables before Jelly Bean MR1.
+         *
+         * Here, it is important to note that prior to JBMR1, two bitmap scaling operations
+         * are required to achieve the pixelization effect. Otherwise, a BitmapDrawable
+         * can be created corresponding to the downscaled bitmap such that when it is
+         * upscaled to fit the ImageView, the upscaling operation is a lot faster since
+         * it uses internal optimizations to fit the ImageView.
+         * */
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
+            BitmapDrawable bitmapDrawable = new BitmapDrawable(getResources(), pixelatedBitmap);
+            bitmapDrawable.setFilterBitmap(false);
+            return bitmapDrawable;
+        } else {
+            Bitmap upscaled = Bitmap.createScaledBitmap(pixelatedBitmap, width, height, false);
+            return new BitmapDrawable(getResources(), upscaled);
+        }
+    }
+
+    /**
+     * Invokes pixelization either on the main thread or on a background thread
+     * depending on whether or not the checkbox was checked.
+     */
+    public void invokePixelization () {
+        mLastTime = System.currentTimeMillis();
+        mLastProgress = mSeekBar.getProgress();
+        if (mIsChecked) {
+            PixelizeImageAsyncTask asyncPixelateTask = new PixelizeImageAsyncTask();
+            asyncPixelateTask.execute(mSeekBar.getProgress() / PROGRESS_TO_PIXELIZATION_FACTOR,
+                    mImageBitmap);
+        } else {
+            mImageView.setImageDrawable(pixelizeImage(mSeekBar.getProgress()
+                    / PROGRESS_TO_PIXELIZATION_FACTOR, mImageBitmap));
+        }
+    }
+
+    /**
+     *  Selects either the custom pixelization algorithm that sets and gets bitmap
+     *  pixels manually or the one that uses built-in bitmap operations.
+     */
+    public BitmapDrawable pixelizeImage(float pixelizationFactor, Bitmap bitmap) {
+        if (mIsBuiltinPixelizationChecked) {
+            return builtInPixelization(pixelizationFactor, bitmap);
+        } else {
+            return customImagePixelization(pixelizationFactor, bitmap);
+        }
+    }
+
+    /**
+     * Implementation of the AsyncTask class showing how to run the
+     * pixelization algorithm in the background, and retrieving the
+     * pixelated image from the resulting operation.
+     */
+    private class PixelizeImageAsyncTask extends AsyncTask<Object, Void, BitmapDrawable> {
+
+        @Override
+        protected BitmapDrawable doInBackground(Object... params) {
+            float pixelizationFactor = (Float)params[0];
+            Bitmap originalBitmap = (Bitmap)params[1];
+            return pixelizeImage(pixelizationFactor, originalBitmap);
+        }
+
+        @Override
+        protected void onPostExecute(BitmapDrawable result) {
+            mImageView.setImageDrawable(result);
+        }
+
+        @Override
+        protected void onPreExecute() {
+
+        }
+
+        @Override
+        protected void onProgressUpdate(Void... values) {
+
+        }
+    }
+}
\ No newline at end of file