am 03491484: am a26b2617: Fix buildDir issue.

* commit '03491484367f61812538fd5f3c294c80cd85cd70':
diff --git a/chips/tests/Android.mk b/camera2/portability/Android.mk
similarity index 62%
rename from chips/tests/Android.mk
rename to camera2/portability/Android.mk
index b01aa0c..a86c511 100644
--- a/chips/tests/Android.mk
+++ b/camera2/portability/Android.mk
@@ -1,4 +1,4 @@
-# Copyright (C) 2011 The Android Open Source Project
+# Copyright 2014 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.
@@ -11,19 +11,13 @@
 # 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.
-LOCAL_PATH:= $(call my-dir)
+
+LOCAL_PATH := $(call my-dir)
 include $(CLEAR_VARS)
 
+LOCAL_MODULE := android-ex-camera2-portability
+LOCAL_MODULE_TAGS := optional
+LOCAL_SDK_VERSION := current
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
-LOCAL_PACKAGE_NAME := ChipsTests
-LOCAL_MODULE_TAGS := tests
-LOCAL_SDK_VERSION := current
-LOCAL_CERTIFICATE := platform
-LOCAL_STATIC_JAVA_LIBRARIES += android-common-chips
-LOCAL_RESOURCE_DIR := frameworks/ex/chips/res/
-LOCAL_AAPT_FLAGS := --auto-add-overlay
-LOCAL_AAPT_FLAGS += --extra-packages com.android.ex.chips
-
-include $(BUILD_PACKAGE)
-
+include $(BUILD_STATIC_JAVA_LIBRARY)
diff --git a/camera2/portability/src/com/android/ex/camera2/portability/AndroidCameraAgentImpl.java b/camera2/portability/src/com/android/ex/camera2/portability/AndroidCameraAgentImpl.java
new file mode 100644
index 0000000..0cd8ede
--- /dev/null
+++ b/camera2/portability/src/com/android/ex/camera2/portability/AndroidCameraAgentImpl.java
@@ -0,0 +1,1372 @@
+/*
+ * 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.android.ex.camera2.portability;
+
+import android.annotation.TargetApi;
+import android.graphics.SurfaceTexture;
+import android.hardware.Camera;
+import android.hardware.Camera.AutoFocusCallback;
+import android.hardware.Camera.AutoFocusMoveCallback;
+import android.hardware.Camera.ErrorCallback;
+import android.hardware.Camera.FaceDetectionListener;
+import android.hardware.Camera.OnZoomChangeListener;
+import android.hardware.Camera.Parameters;
+import android.hardware.Camera.PictureCallback;
+import android.hardware.Camera.PreviewCallback;
+import android.hardware.Camera.ShutterCallback;
+import android.os.Build;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Looper;
+import android.os.Message;
+import android.view.SurfaceHolder;
+
+import com.android.ex.camera2.portability.debug.Log;
+
+import java.io.IOException;
+import java.util.StringTokenizer;
+
+/**
+ * A class to implement {@link CameraAgent} of the Android camera framework.
+ */
+class AndroidCameraAgentImpl implements CameraAgent {
+    private static final Log.Tag TAG = new Log.Tag("AndroidCamMgrImpl");
+
+    private Parameters mParameters;
+    private boolean mParametersIsDirty;
+    private AndroidCameraCapabilities mCapabilities;
+
+    private final CameraHandler mCameraHandler;
+    private final HandlerThread mCameraHandlerThread;
+    private final CameraStateHolder mCameraState;
+    private final DispatchThread mDispatchThread;
+
+    private Handler mCameraExceptionCallbackHandler;
+    private CameraExceptionCallback mCameraExceptionCallback =
+        new CameraExceptionCallback() {
+            @Override
+            public void onCameraException(RuntimeException e) {
+                throw e;
+            }
+        };
+
+    AndroidCameraAgentImpl() {
+        mCameraHandlerThread = new HandlerThread("Camera Handler Thread");
+        mCameraHandlerThread.start();
+        mCameraHandler = new CameraHandler(mCameraHandlerThread.getLooper());
+        mCameraExceptionCallbackHandler = mCameraHandler;
+        mCameraState = new CameraStateHolder();
+        mDispatchThread = new DispatchThread(mCameraHandler, mCameraHandlerThread);
+        mDispatchThread.start();
+    }
+
+    @Override
+    public void setCameraDefaultExceptionCallback(CameraExceptionCallback callback,
+            Handler handler) {
+        synchronized (mCameraExceptionCallback) {
+            mCameraExceptionCallback = callback;
+            mCameraExceptionCallbackHandler = handler;
+        }
+    }
+
+    @Override
+    public void recycle() {
+        closeCamera(null, true);
+        mDispatchThread.end();
+    }
+
+    @Override
+    public CameraDeviceInfo getCameraDeviceInfo() {
+        return AndroidCameraDeviceInfo.create();
+    }
+
+    private static class AndroidCameraDeviceInfo implements CameraDeviceInfo {
+        private final Camera.CameraInfo[] mCameraInfos;
+        private final int mNumberOfCameras;
+        private final int mFirstBackCameraId;
+        private final int mFirstFrontCameraId;
+
+        private AndroidCameraDeviceInfo(Camera.CameraInfo[] info, int numberOfCameras,
+                int firstBackCameraId, int firstFrontCameraId) {
+
+            mCameraInfos = info;
+            mNumberOfCameras = numberOfCameras;
+            mFirstBackCameraId = firstBackCameraId;
+            mFirstFrontCameraId = firstFrontCameraId;
+        }
+
+        public static AndroidCameraDeviceInfo create() {
+            int numberOfCameras;
+            Camera.CameraInfo[] cameraInfos;
+            try {
+                numberOfCameras = Camera.getNumberOfCameras();
+                cameraInfos = new Camera.CameraInfo[numberOfCameras];
+                for (int i = 0; i < numberOfCameras; i++) {
+                    cameraInfos[i] = new Camera.CameraInfo();
+                    Camera.getCameraInfo(i, cameraInfos[i]);
+                }
+            } catch (RuntimeException ex) {
+                return null;
+            }
+
+            int firstFront = NO_DEVICE;
+            int firstBack = NO_DEVICE;
+            // Get the first (smallest) back and first front camera id.
+            for (int i = numberOfCameras - 1; i >= 0; i--) {
+                if (cameraInfos[i].facing == Camera.CameraInfo.CAMERA_FACING_BACK) {
+                    firstBack = i;
+                } else {
+                    if (cameraInfos[i].facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
+                        firstFront = i;
+                    }
+                }
+            }
+
+            return new AndroidCameraDeviceInfo(cameraInfos, numberOfCameras, firstBack, firstFront);
+        }
+
+        @Override
+        public Camera.CameraInfo[] getCameraInfos() {
+            return mCameraInfos;
+        }
+
+        @Override
+        public int getNumberOfCameras() {
+            return mNumberOfCameras;
+        }
+
+        @Override
+        public int getFirstBackCameraId() {
+            return mFirstBackCameraId;
+        }
+
+        @Override
+        public int getFirstFrontCameraId() {
+            return mFirstFrontCameraId;
+        }
+    }
+
+    /**
+     * The handler on which the actual camera operations happen.
+     */
+    private class CameraHandler extends HistoryHandler {
+
+        // Used to retain a copy of Parameters for setting parameters.
+        private Parameters mParamsToSet;
+        private Camera mCamera;
+        private int mCameraId;
+
+        private class CaptureCallbacks {
+            public final ShutterCallback mShutter;
+            public final PictureCallback mRaw;
+            public final PictureCallback mPostView;
+            public final PictureCallback mJpeg;
+
+            CaptureCallbacks(ShutterCallback shutter, PictureCallback raw, PictureCallback postView,
+                    PictureCallback jpeg) {
+                mShutter = shutter;
+                mRaw = raw;
+                mPostView = postView;
+                mJpeg = jpeg;
+            }
+        }
+
+        CameraHandler(Looper looper) {
+            super(looper);
+        }
+
+        private void startFaceDetection() {
+            mCamera.startFaceDetection();
+        }
+
+        private void stopFaceDetection() {
+            mCamera.stopFaceDetection();
+        }
+
+        private void setFaceDetectionListener(FaceDetectionListener listener) {
+            mCamera.setFaceDetectionListener(listener);
+        }
+
+        private void setPreviewTexture(Object surfaceTexture) {
+            try {
+                mCamera.setPreviewTexture((SurfaceTexture) surfaceTexture);
+            } catch (IOException e) {
+                Log.e(TAG, "Could not set preview texture", e);
+            }
+        }
+
+        @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
+        private void enableShutterSound(boolean enable) {
+            mCamera.enableShutterSound(enable);
+        }
+
+        @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
+        private void setAutoFocusMoveCallback(
+                android.hardware.Camera camera, Object cb) {
+            try {
+                camera.setAutoFocusMoveCallback((AutoFocusMoveCallback) cb);
+            } catch (RuntimeException ex) {
+                Log.w(TAG, ex.getMessage());
+            }
+        }
+
+        private void capture(final CaptureCallbacks cb) {
+            try {
+                mCamera.takePicture(cb.mShutter, cb.mRaw, cb.mPostView, cb.mJpeg);
+            } catch (RuntimeException e) {
+                // TODO: output camera state and focus state for debugging.
+                Log.e(TAG, "take picture failed.");
+                throw e;
+            }
+        }
+
+        public void requestTakePicture(
+                final ShutterCallback shutter,
+                final PictureCallback raw,
+                final PictureCallback postView,
+                final PictureCallback jpeg) {
+            final CaptureCallbacks callbacks = new CaptureCallbacks(shutter, raw, postView, jpeg);
+            obtainMessage(CameraActions.CAPTURE_PHOTO, callbacks).sendToTarget();
+        }
+
+        /**
+         * This method does not deal with the API level check.  Everyone should
+         * check first for supported operations before sending message to this handler.
+         */
+        @Override
+        public void handleMessage(final Message msg) {
+            super.handleMessage(msg);
+            try {
+                switch (msg.what) {
+                    case CameraActions.OPEN_CAMERA: {
+                        final CameraOpenCallback openCallback = (CameraOpenCallback) msg.obj;
+                        final int cameraId = msg.arg1;
+                        if (mCameraState.getState() != CameraStateHolder.CAMERA_UNOPENED) {
+                            openCallback.onDeviceOpenedAlready(cameraId, generateHistoryString(cameraId));
+                            break;
+                        }
+
+                        mCamera = android.hardware.Camera.open(cameraId);
+                        if (mCamera != null) {
+                            mCameraId = cameraId;
+                            mParametersIsDirty = true;
+
+                            // Get a instance of Camera.Parameters for later use.
+                            mParamsToSet = mCamera.getParameters();
+                            mCapabilities = new AndroidCameraCapabilities(mParamsToSet);
+
+                            mCameraState.setState(CameraStateHolder.CAMERA_IDLE);
+                            if (openCallback != null) {
+                                openCallback.onCameraOpened(
+                                        new AndroidCameraProxyImpl(cameraId, mCamera,
+                                                mCapabilities));
+                            }
+                        } else {
+                            if (openCallback != null) {
+                                openCallback.onDeviceOpenFailure(cameraId, generateHistoryString(cameraId));
+                            }
+                        }
+                        break;
+                    }
+
+                    case CameraActions.RELEASE: {
+                        if (mCamera != null) {
+                            mCamera.release();
+                            mCameraState.setState(CameraStateHolder.CAMERA_UNOPENED);
+                            mCamera = null;
+                        } else {
+                            Log.w(TAG, "Releasing camera without any camera opened.");
+                        }
+                        break;
+                    }
+
+                    case CameraActions.RECONNECT: {
+                        final CameraOpenCallbackForward cbForward =
+                                (CameraOpenCallbackForward) msg.obj;
+                        final int cameraId = msg.arg1;
+                        try {
+                            mCamera.reconnect();
+                        } catch (IOException ex) {
+                            if (cbForward != null) {
+                                cbForward.onReconnectionFailure(AndroidCameraAgentImpl.this,
+                                        generateHistoryString(mCameraId));
+                            }
+                            break;
+                        }
+
+                        mCameraState.setState(CameraStateHolder.CAMERA_IDLE);
+                        if (cbForward != null) {
+                            cbForward.onCameraOpened(
+                                    new AndroidCameraProxyImpl(cameraId, mCamera, mCapabilities));
+                        }
+                        break;
+                    }
+
+                    case CameraActions.UNLOCK: {
+                        mCamera.unlock();
+                        mCameraState.setState(CameraStateHolder.CAMERA_UNLOCKED);
+                        break;
+                    }
+
+                    case CameraActions.LOCK: {
+                        mCamera.lock();
+                        mCameraState.setState(CameraStateHolder.CAMERA_IDLE);
+                        break;
+                    }
+
+                    case CameraActions.SET_PREVIEW_TEXTURE_ASYNC: {
+                        setPreviewTexture(msg.obj);
+                        break;
+                    }
+
+                    case CameraActions.SET_PREVIEW_DISPLAY_ASYNC: {
+                        try {
+                            mCamera.setPreviewDisplay((SurfaceHolder) msg.obj);
+                        } catch (IOException e) {
+                            throw new RuntimeException(e);
+                        }
+                        break;
+                    }
+
+                    case CameraActions.START_PREVIEW_ASYNC: {
+                        final CameraStartPreviewCallbackForward cbForward =
+                            (CameraStartPreviewCallbackForward) msg.obj;
+                        mCamera.startPreview();
+                        if (cbForward != null) {
+                            cbForward.onPreviewStarted();
+                        }
+                        break;
+                    }
+
+                    case CameraActions.STOP_PREVIEW: {
+                        mCamera.stopPreview();
+                        break;
+                    }
+
+                    case CameraActions.SET_PREVIEW_CALLBACK_WITH_BUFFER: {
+                        mCamera.setPreviewCallbackWithBuffer((PreviewCallback) msg.obj);
+                        break;
+                    }
+
+                    case CameraActions.SET_ONE_SHOT_PREVIEW_CALLBACK: {
+                        mCamera.setOneShotPreviewCallback((PreviewCallback) msg.obj);
+                        break;
+                    }
+
+                    case CameraActions.ADD_CALLBACK_BUFFER: {
+                        mCamera.addCallbackBuffer((byte[]) msg.obj);
+                        break;
+                    }
+
+                    case CameraActions.AUTO_FOCUS: {
+                        mCameraState.setState(CameraStateHolder.CAMERA_FOCUSING);
+                        mCamera.autoFocus((AutoFocusCallback) msg.obj);
+                        break;
+                    }
+
+                    case CameraActions.CANCEL_AUTO_FOCUS: {
+                        mCamera.cancelAutoFocus();
+                        mCameraState.setState(CameraStateHolder.CAMERA_IDLE);
+                        break;
+                    }
+
+                    case CameraActions.SET_AUTO_FOCUS_MOVE_CALLBACK: {
+                        setAutoFocusMoveCallback(mCamera, msg.obj);
+                        break;
+                    }
+
+                    case CameraActions.SET_DISPLAY_ORIENTATION: {
+                        mCamera.setDisplayOrientation(msg.arg1);
+                        break;
+                    }
+
+                    case CameraActions.SET_ZOOM_CHANGE_LISTENER: {
+                        mCamera.setZoomChangeListener((OnZoomChangeListener) msg.obj);
+                        break;
+                    }
+
+                    case CameraActions.SET_FACE_DETECTION_LISTENER: {
+                        setFaceDetectionListener((FaceDetectionListener) msg.obj);
+                        break;
+                    }
+
+                    case CameraActions.START_FACE_DETECTION: {
+                        startFaceDetection();
+                        break;
+                    }
+
+                    case CameraActions.STOP_FACE_DETECTION: {
+                        stopFaceDetection();
+                        break;
+                    }
+
+                    case CameraActions.SET_ERROR_CALLBACK: {
+                        mCamera.setErrorCallback((ErrorCallback) msg.obj);
+                        break;
+                    }
+
+                    case CameraActions.APPLY_SETTINGS: {
+                        mParametersIsDirty = true;
+                        CameraSettings settings = (CameraSettings) msg.obj;
+                        applyToParameters(settings);
+                        mCamera.setParameters(mParamsToSet);
+                        break;
+                    }
+
+                    case CameraActions.SET_PARAMETERS: {
+                        mParametersIsDirty = true;
+                        mParamsToSet.unflatten((String) msg.obj);
+                        mCamera.setParameters(mParamsToSet);
+                        break;
+                    }
+
+                    case CameraActions.GET_PARAMETERS: {
+                        if (mParametersIsDirty) {
+                            mParameters = mCamera.getParameters();
+                            mParametersIsDirty = false;
+                        }
+                        break;
+                    }
+
+                    case CameraActions.SET_PREVIEW_CALLBACK: {
+                        mCamera.setPreviewCallback((PreviewCallback) msg.obj);
+                        break;
+                    }
+
+                    case CameraActions.ENABLE_SHUTTER_SOUND: {
+                        enableShutterSound((msg.arg1 == 1) ? true : false);
+                        break;
+                    }
+
+                    case CameraActions.REFRESH_PARAMETERS: {
+                        mParametersIsDirty = true;
+                        break;
+                    }
+
+                    case CameraActions.CAPTURE_PHOTO: {
+                        mCameraState.setState(CameraStateHolder.CAMERA_CAPTURING);
+                        capture((CaptureCallbacks) msg.obj);
+                        break;
+                    }
+
+                    default: {
+                        throw new RuntimeException("Invalid CameraProxy message=" + msg.what);
+                    }
+                }
+            } catch (final RuntimeException e) {
+                if (msg.what != CameraActions.RELEASE && mCamera != null) {
+                    try {
+                        mCamera.release();
+                        mCameraState.setState(CameraStateHolder.CAMERA_UNOPENED);
+                    } catch (Exception ex) {
+                        Log.e(TAG, "Fail to release the camera.");
+                    }
+                    mCamera = null;
+                } else {
+                    if (mCamera == null) {
+                        if (msg.what == CameraActions.OPEN_CAMERA) {
+                            final int cameraId = msg.arg1;
+                            if (msg.obj != null) {
+                                ((CameraOpenCallback) msg.obj).onDeviceOpenFailure(
+                                        msg.arg1, generateHistoryString(cameraId));
+                            }
+                        } else {
+                            Log.w(TAG, "Cannot handle message " + msg.what + ", mCamera is null.");
+                        }
+                        return;
+                    }
+                }
+                synchronized (mCameraExceptionCallback) {
+                    mCameraExceptionCallbackHandler.post(new Runnable() {
+                        @Override
+                        public void run() {
+                                mCameraExceptionCallback.onCameraException(e);
+                            }
+                        });
+                }
+            }
+        }
+
+        private void applyToParameters(final CameraSettings settings) {
+            final CameraCapabilities.Stringifier stringifier = mCapabilities.getStringifier();
+            Size photoSize = settings.getCurrentPhotoSize();
+            mParamsToSet.setPictureSize(photoSize.width(), photoSize.height());
+            Size previewSize = settings.getCurrentPreviewSize();
+            mParamsToSet.setPreviewSize(previewSize.width(), previewSize.height());
+            if (settings.getPreviewFrameRate() == -1) {
+                mParamsToSet.setPreviewFpsRange(settings.getPreviewFpsRangeMin(),
+                        settings.getPreviewFpsRangeMax());
+            } else {
+                mParamsToSet.setPreviewFrameRate(settings.getPreviewFrameRate());
+            }
+            mParamsToSet.setPreviewFormat(settings.getCurrentPreviewFormat());
+            mParamsToSet.setJpegQuality(settings.getPhotoJpegCompressionQuality());
+            if (mCapabilities.supports(CameraCapabilities.Feature.ZOOM)) {
+                // Should use settings.getCurrentZoomRatio() instead here.
+                mParamsToSet.setZoom(settings.getCurrentZoomIndex());
+            }
+            mParamsToSet.setRotation((int) settings.getCurrentPhotoRotationDegrees());
+            mParamsToSet.setExposureCompensation(settings.getExposureCompensationIndex());
+            if (mCapabilities.supports(CameraCapabilities.Feature.AUTO_EXPOSURE_LOCK)) {
+                mParamsToSet.setAutoExposureLock(settings.isAutoExposureLocked());
+            }
+            mParamsToSet.setFocusMode(stringifier.stringify(settings.getCurrentFocusMode()));
+            if (mCapabilities.supports(CameraCapabilities.Feature.AUTO_WHITE_BALANCE_LOCK)) {
+                mParamsToSet.setAutoWhiteBalanceLock(settings.isAutoWhiteBalanceLocked());
+            }
+            if (mCapabilities.supports(CameraCapabilities.Feature.FOCUS_AREA)) {
+                if (settings.getFocusAreas().size() != 0) {
+                    mParamsToSet.setFocusAreas(settings.getFocusAreas());
+                }
+            }
+            if (mCapabilities.supports(CameraCapabilities.Feature.METERING_AREA)) {
+                if (settings.getMeteringAreas().size() != 0) {
+                    mParamsToSet.setMeteringAreas(settings.getMeteringAreas());
+                }
+            }
+            if (settings.getCurrentFlashMode() != CameraCapabilities.FlashMode.NO_FLASH) {
+                mParamsToSet.setFlashMode(stringifier.stringify(settings.getCurrentFlashMode()));
+            }
+            if (settings.getCurrentSceneMode() != CameraCapabilities.SceneMode.NO_SCENE_MODE) {
+                if (settings.getCurrentSceneMode() != null) {
+                    mParamsToSet
+                            .setSceneMode(stringifier.stringify(settings.getCurrentSceneMode()));
+                }
+            }
+            mParamsToSet.setRecordingHint(settings.isRecordingHintEnabled());
+            Size jpegThumbSize = settings.getExifThumbnailSize();
+            mParamsToSet.setJpegThumbnailSize(jpegThumbSize.width(), jpegThumbSize.height());
+            mParamsToSet.setPictureFormat(settings.getCurrentPhotoFormat());
+
+            CameraSettings.GpsData gpsData = settings.getGpsData();
+            if (gpsData == null) {
+                mParamsToSet.removeGpsData();
+            } else {
+                mParamsToSet.setGpsTimestamp(gpsData.timeStamp);
+                if (gpsData.processingMethod != null) {
+                    // It's a hack since we always use GPS time stamp but does
+                    // not use other fields sometimes. Setting processing
+                    // method to null means the other fields should not be used.
+                    mParamsToSet.setGpsAltitude(gpsData.altitude);
+                    mParamsToSet.setGpsLatitude(gpsData.latitude);
+                    mParamsToSet.setGpsLongitude(gpsData.longitude);
+                    mParamsToSet.setGpsProcessingMethod(gpsData.processingMethod);
+                }
+            }
+
+        }
+    }
+
+    @Override
+    public void openCamera(final Handler handler, final int cameraId,
+                           final CameraOpenCallback callback) {
+        mDispatchThread.runJob(new Runnable() {
+            @Override
+            public void run() {
+                mCameraHandler.obtainMessage(CameraActions.OPEN_CAMERA, cameraId, 0,
+                        CameraOpenCallbackForward.getNewInstance(handler, callback)).sendToTarget();
+            }
+        });
+    }
+
+    @Override
+    public void closeCamera(CameraProxy camera, boolean synced) {
+        if (synced) {
+            final WaitDoneBundle bundle = new WaitDoneBundle();
+
+            mDispatchThread.runJobSync(new Runnable() {
+                @Override
+                public void run() {
+                    mCameraHandler.obtainMessage(CameraActions.RELEASE).sendToTarget();
+                    mCameraHandler.post(bundle.mUnlockRunnable);
+                }
+            }, bundle.mWaitLock, CAMERA_OPERATION_TIMEOUT_MS, "camera release");
+        } else {
+            mDispatchThread.runJob(new Runnable() {
+                @Override
+                public void run() {
+                    mCameraHandler.removeCallbacksAndMessages(null);
+                    mCameraHandler.obtainMessage(CameraActions.RELEASE).sendToTarget();
+                }
+            });
+        }
+    }
+
+    /**
+     * A class which implements {@link CameraAgent.CameraProxy} and
+     * camera handler thread.
+     */
+    private class AndroidCameraProxyImpl implements CameraAgent.CameraProxy {
+        private final int mCameraId;
+        /* TODO: remove this Camera instance. */
+        private final Camera mCamera;
+        private final AndroidCameraCapabilities mCapabilities;
+
+        private AndroidCameraProxyImpl(int cameraId, Camera camera,
+                AndroidCameraCapabilities capabilities) {
+            mCamera = camera;
+            mCameraId = cameraId;
+            mCapabilities = capabilities;
+        }
+
+        @Override
+        public android.hardware.Camera getCamera() {
+            return mCamera;
+        }
+
+        @Override
+        public int getCameraId() {
+            return mCameraId;
+        }
+
+        @Override
+        public CameraCapabilities getCapabilities() {
+            return new AndroidCameraCapabilities(mCapabilities);
+        }
+
+        @Override
+        public void reconnect(final Handler handler, final CameraOpenCallback cb) {
+            mDispatchThread.runJob(new Runnable() {
+                @Override
+                public void run() {
+                    mCameraHandler.obtainMessage(CameraActions.RECONNECT, mCameraId, 0,
+                            CameraOpenCallbackForward.getNewInstance(handler, cb)).sendToTarget();
+                }
+            });
+        }
+
+        @Override
+        public void unlock() {
+            final WaitDoneBundle bundle = new WaitDoneBundle();
+            mDispatchThread.runJobSync(new Runnable() {
+                @Override
+                public void run() {
+                    mCameraHandler.sendEmptyMessage(CameraActions.UNLOCK);
+                    mCameraHandler.post(bundle.mUnlockRunnable);
+                }
+            }, bundle.mWaitLock, CAMERA_OPERATION_TIMEOUT_MS, "camera unlock");
+        }
+
+        @Override
+        public void lock() {
+            mDispatchThread.runJob(new Runnable() {
+                @Override
+                public void run() {
+                    mCameraHandler.sendEmptyMessage(CameraActions.LOCK);
+                }
+            });
+        }
+
+        @Override
+        public void setPreviewTexture(final SurfaceTexture surfaceTexture) {
+            mDispatchThread.runJob(new Runnable() {
+                @Override
+                public void run() {
+                    mCameraHandler
+                            .obtainMessage(CameraActions.SET_PREVIEW_TEXTURE_ASYNC, surfaceTexture)
+                            .sendToTarget();
+                }
+            });
+        }
+
+        @Override
+        public void setPreviewTextureSync(final SurfaceTexture surfaceTexture) {
+            final WaitDoneBundle bundle = new WaitDoneBundle();
+            mDispatchThread.runJobSync(new Runnable() {
+                @Override
+                public void run() {
+                    mCameraHandler
+                            .obtainMessage(CameraActions.SET_PREVIEW_TEXTURE_ASYNC, surfaceTexture)
+                            .sendToTarget();
+                    mCameraHandler.post(bundle.mUnlockRunnable);
+                }
+            }, bundle.mWaitLock, CAMERA_OPERATION_TIMEOUT_MS, "set preview texture");
+        }
+
+        @Override
+        public void setPreviewDisplay(final SurfaceHolder surfaceHolder) {
+            mDispatchThread.runJob(new Runnable() {
+                @Override
+                public void run() {
+                    mCameraHandler
+                            .obtainMessage(CameraActions.SET_PREVIEW_DISPLAY_ASYNC, surfaceHolder)
+                            .sendToTarget();
+                }
+            });
+        }
+
+        @Override
+        public void startPreview() {
+            mDispatchThread.runJob(new Runnable() {
+                @Override
+                public void run() {
+                    mCameraHandler
+                            .obtainMessage(CameraActions.START_PREVIEW_ASYNC, null).sendToTarget();
+                }
+            });
+        }
+
+        @Override
+        public void startPreviewWithCallback(final Handler handler,
+                final CameraStartPreviewCallback cb) {
+            mDispatchThread.runJob(new Runnable() {
+                @Override
+                public void run() {
+                    mCameraHandler.obtainMessage(CameraActions.START_PREVIEW_ASYNC,
+                        CameraStartPreviewCallbackForward.getNewInstance(handler, cb)).sendToTarget();
+                }
+            });
+        }
+
+        @Override
+        public void stopPreview() {
+            final WaitDoneBundle bundle = new WaitDoneBundle();
+            mDispatchThread.runJobSync(new Runnable() {
+                @Override
+                public void run() {
+                    mCameraHandler.sendEmptyMessage(CameraActions.STOP_PREVIEW);
+                    mCameraHandler.post(bundle.mUnlockRunnable);
+                }
+            }, bundle.mWaitLock, CAMERA_OPERATION_TIMEOUT_MS, "stop preview");
+        }
+
+        @Override
+        public void setPreviewDataCallback(
+                final Handler handler, final CameraPreviewDataCallback cb) {
+            mDispatchThread.runJob(new Runnable() {
+                @Override
+                public void run() {
+                    mCameraHandler.obtainMessage(CameraActions.SET_PREVIEW_CALLBACK,
+                            PreviewCallbackForward.getNewInstance(
+                                    handler, AndroidCameraProxyImpl.this, cb))
+                            .sendToTarget();
+                }
+            });
+        }
+        @Override
+        public void setOneShotPreviewCallback(final Handler handler,
+                final CameraPreviewDataCallback cb) {
+            mDispatchThread.runJob(new Runnable() {
+                @Override
+                public void run() {
+                    mCameraHandler.obtainMessage(CameraActions.SET_ONE_SHOT_PREVIEW_CALLBACK,
+                            PreviewCallbackForward
+                                    .getNewInstance(handler, AndroidCameraProxyImpl.this, cb))
+                            .sendToTarget();
+                }
+            });
+        }
+
+        @Override
+        public void setPreviewDataCallbackWithBuffer(
+                final Handler handler, final CameraPreviewDataCallback cb) {
+            mDispatchThread.runJob(new Runnable() {
+                @Override
+                public void run() {
+                    mCameraHandler.obtainMessage(CameraActions.SET_PREVIEW_CALLBACK_WITH_BUFFER,
+                            PreviewCallbackForward
+                                    .getNewInstance(handler, AndroidCameraProxyImpl.this, cb))
+                            .sendToTarget();
+                }
+            });
+        }
+
+        @Override
+        public void addCallbackBuffer(final byte[] callbackBuffer) {
+            mDispatchThread.runJob(new Runnable() {
+                @Override
+                public void run() {
+                    mCameraHandler.obtainMessage(CameraActions.ADD_CALLBACK_BUFFER, callbackBuffer)
+                            .sendToTarget();
+                }
+            });
+        }
+
+        @Override
+        public void autoFocus(final Handler handler, final CameraAFCallback cb) {
+            final AutoFocusCallback afCallback = new AutoFocusCallback() {
+                @Override
+                public void onAutoFocus(final boolean b, Camera camera) {
+                    if (mCameraState.getState() != CameraStateHolder.CAMERA_FOCUSING) {
+                        Log.w(TAG, "onAutoFocus callback returning when not focusing");
+                    } else {
+                        mCameraState.setState(CameraStateHolder.CAMERA_IDLE);
+                    }
+                    handler.post(new Runnable() {
+                        @Override
+                        public void run() {
+                            cb.onAutoFocus(b, AndroidCameraProxyImpl.this);
+                        }
+                    });
+                }
+            };
+            mDispatchThread.runJob(new Runnable() {
+                @Override
+                public void run() {
+                    mCameraState.waitForStates(CameraStateHolder.CAMERA_IDLE);
+                    mCameraHandler.obtainMessage(CameraActions.AUTO_FOCUS, afCallback)
+                            .sendToTarget();
+                }
+            });
+        }
+
+        @Override
+        public void cancelAutoFocus() {
+            mDispatchThread.runJob(new Runnable() {
+                @Override
+                public void run() {
+                    mCameraHandler.removeMessages(CameraActions.AUTO_FOCUS);
+                    mCameraHandler.sendEmptyMessage(CameraActions.CANCEL_AUTO_FOCUS);
+                }
+            });
+        }
+
+        @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
+        @Override
+        public void setAutoFocusMoveCallback(
+                final Handler handler, final CameraAFMoveCallback cb) {
+            mDispatchThread.runJob(new Runnable() {
+                @Override
+                public void run() {
+                    mCameraHandler.obtainMessage(CameraActions.SET_AUTO_FOCUS_MOVE_CALLBACK,
+                            AFMoveCallbackForward.getNewInstance(
+                                    handler, AndroidCameraProxyImpl.this, cb))
+                            .sendToTarget();
+                }
+            });
+        }
+
+        @Override
+        public void takePicture(
+                final Handler handler, final CameraShutterCallback shutter,
+                final CameraPictureCallback raw, final CameraPictureCallback post,
+                final CameraPictureCallback jpeg) {
+            final PictureCallback jpegCallback = new PictureCallback() {
+                @Override
+                public void onPictureTaken(final byte[] data, Camera camera) {
+                    if (mCameraState.getState() != CameraStateHolder.CAMERA_CAPTURING) {
+                        Log.w(TAG, "picture callback returning when not capturing");
+                    } else {
+                        mCameraState.setState(CameraStateHolder.CAMERA_IDLE);
+                    }
+                    handler.post(new Runnable() {
+                        @Override
+                        public void run() {
+                            jpeg.onPictureTaken(data, AndroidCameraProxyImpl.this);
+                        }
+                    });
+                }
+            };
+
+            mDispatchThread.runJob(new Runnable() {
+                @Override
+                public void run() {
+                    mCameraState.waitForStates(
+                            CameraStateHolder.CAMERA_IDLE | CameraStateHolder.CAMERA_UNLOCKED);
+                    mCameraHandler.requestTakePicture(ShutterCallbackForward
+                                    .getNewInstance(handler, AndroidCameraProxyImpl.this, shutter),
+                            PictureCallbackForward
+                                    .getNewInstance(handler, AndroidCameraProxyImpl.this, raw),
+                            PictureCallbackForward
+                                    .getNewInstance(handler, AndroidCameraProxyImpl.this, post),
+                            jpegCallback
+                    );
+                }
+            });
+        }
+
+        @Override
+        public void setDisplayOrientation(final int degrees) {
+            mDispatchThread.runJob(new Runnable() {
+                @Override
+                public void run() {
+                    mCameraHandler.obtainMessage(CameraActions.SET_DISPLAY_ORIENTATION, degrees, 0)
+                            .sendToTarget();
+                }
+            });
+        }
+
+        @Override
+        public void setZoomChangeListener(final OnZoomChangeListener listener) {
+            mDispatchThread.runJob(new Runnable() {
+                @Override
+                public void run() {
+                    mCameraHandler.obtainMessage(CameraActions.SET_ZOOM_CHANGE_LISTENER, listener)
+                            .sendToTarget();
+                }
+            });
+        }
+
+        @Override
+        public void setFaceDetectionCallback(final Handler handler,
+                final CameraFaceDetectionCallback cb) {
+            mDispatchThread.runJob(new Runnable() {
+                @Override
+                public void run() {
+                    mCameraHandler.obtainMessage(CameraActions.SET_FACE_DETECTION_LISTENER,
+                            FaceDetectionCallbackForward
+                                    .getNewInstance(handler, AndroidCameraProxyImpl.this, cb))
+                            .sendToTarget();
+                }
+            });
+        }
+
+        @Override
+        public void startFaceDetection() {
+            mDispatchThread.runJob(new Runnable() {
+                @Override
+                public void run() {
+                    mCameraHandler.sendEmptyMessage(CameraActions.START_FACE_DETECTION);
+                }
+            });
+        }
+
+        @Override
+        public void stopFaceDetection() {
+            mDispatchThread.runJob(new Runnable() {
+                @Override
+                public void run() {
+                    mCameraHandler.sendEmptyMessage(CameraActions.STOP_FACE_DETECTION);
+                }
+            });
+        }
+
+        @Override
+        public void setErrorCallback(final Handler handler, final CameraErrorCallback cb) {
+            mDispatchThread.runJob(new Runnable() {
+                @Override
+                public void run() {
+                    mCameraHandler.obtainMessage(CameraActions.SET_ERROR_CALLBACK,
+                            ErrorCallbackForward.getNewInstance(
+                                    handler, AndroidCameraProxyImpl.this, cb))
+                            .sendToTarget();
+                }
+            });
+        }
+
+        @Override
+        public void setParameters(final Parameters params) {
+            if (params == null) {
+                Log.v(TAG, "null parameters in setParameters()");
+                return;
+            }
+            final String flattenedParameters = params.flatten();
+            mDispatchThread.runJob(new Runnable() {
+                @Override
+                public void run() {
+                    mCameraState.waitForStates(
+                            CameraStateHolder.CAMERA_IDLE | CameraStateHolder.CAMERA_UNLOCKED);
+                    mCameraHandler.obtainMessage(CameraActions.SET_PARAMETERS, flattenedParameters)
+                            .sendToTarget();
+                }
+            });
+        }
+
+        @Override
+        public Parameters getParameters() {
+            final WaitDoneBundle bundle = new WaitDoneBundle();
+            mDispatchThread.runJobSync(new Runnable() {
+                @Override
+                public void run() {
+                    mCameraHandler.sendEmptyMessage(CameraActions.GET_PARAMETERS);
+                    mCameraHandler.post(bundle.mUnlockRunnable);
+                }
+            }, bundle.mWaitLock, CAMERA_OPERATION_TIMEOUT_MS, "get parameters");
+            return mParameters;
+        }
+
+        @Override
+        public CameraSettings getSettings() {
+            return new AndroidCameraSettings(mCapabilities, getParameters());
+        }
+
+        @Override
+        public boolean applySettings(final CameraSettings settings) {
+            if (settings == null) {
+                Log.v(TAG, "null parameters in applySettings()");
+                return false;
+            }
+            if (!mCapabilities.supports(settings)) {
+                return false;
+            }
+
+            final CameraSettings copyOfSettings = new CameraSettings(settings);
+            mDispatchThread.runJob(new Runnable() {
+                @Override
+                public void run() {
+                    mCameraState.waitForStates(
+                            CameraStateHolder.CAMERA_IDLE | CameraStateHolder.CAMERA_UNLOCKED);
+                    mCameraHandler.obtainMessage(CameraActions.APPLY_SETTINGS, copyOfSettings)
+                            .sendToTarget();
+                }
+            });
+            return true;
+        }
+
+        @Override
+        public void refreshSettings() {
+            mDispatchThread.runJob(new Runnable() {
+                @Override
+                public void run() {
+                    mCameraHandler.sendEmptyMessage(CameraActions.REFRESH_PARAMETERS);
+                }
+            });
+        }
+
+        @Override
+        public void enableShutterSound(final boolean enable) {
+            mDispatchThread.runJob(new Runnable() {
+                @Override
+                public void run() {
+                    mCameraHandler
+                            .obtainMessage(CameraActions.ENABLE_SHUTTER_SOUND, (enable ? 1 : 0), 0)
+                            .sendToTarget();
+                }
+            });
+        }
+
+        @Override
+        public String dumpDeviceSettings() {
+            String flattened = mParameters.flatten();
+            StringTokenizer tokenizer = new StringTokenizer(flattened, ";");
+            String dumpedSettings = new String();
+            while (tokenizer.hasMoreElements()) {
+                dumpedSettings += tokenizer.nextToken() + '\n';
+            }
+
+            return dumpedSettings;
+        }
+    }
+
+    private static class WaitDoneBundle {
+        public Runnable mUnlockRunnable;
+        private final Object mWaitLock;
+
+        WaitDoneBundle() {
+            mWaitLock = new Object();
+            mUnlockRunnable = new Runnable() {
+                @Override
+                public void run() {
+                    synchronized (mWaitLock) {
+                        mWaitLock.notifyAll();
+                    }
+                }
+            };
+        }
+    }
+
+    /**
+     * A helper class to forward AutoFocusCallback to another thread.
+     */
+    private static class AFCallbackForward implements AutoFocusCallback {
+        private final Handler mHandler;
+        private final CameraProxy mCamera;
+        private final CameraAFCallback mCallback;
+
+        /**
+         * Returns a new instance of {@link AFCallbackForward}.
+         *
+         * @param handler The handler in which the callback will be invoked in.
+         * @param camera  The {@link CameraProxy} which the callback is from.
+         * @param cb      The callback to be invoked.
+         * @return        The instance of the {@link AFCallbackForward},
+         *                or null if any parameter is null.
+         */
+        public static AFCallbackForward getNewInstance(
+                Handler handler, CameraProxy camera, CameraAFCallback cb) {
+            if (handler == null || camera == null || cb == null) {
+                return null;
+            }
+            return new AFCallbackForward(handler, camera, cb);
+        }
+
+        private AFCallbackForward(
+                Handler h, CameraProxy camera, CameraAFCallback cb) {
+            mHandler = h;
+            mCamera = camera;
+            mCallback = cb;
+        }
+
+        @Override
+        public void onAutoFocus(final boolean b, Camera camera) {
+            mHandler.post(new Runnable() {
+                @Override
+                public void run() {
+                    mCallback.onAutoFocus(b, mCamera);
+                }
+            });
+        }
+    }
+
+    /**
+     * A helper class to forward ErrorCallback to another thread.
+     */
+    private static class ErrorCallbackForward implements Camera.ErrorCallback {
+        private final Handler mHandler;
+        private final CameraProxy mCamera;
+        private final CameraErrorCallback mCallback;
+
+        /**
+         * Returns a new instance of {@link AFCallbackForward}.
+         *
+         * @param handler The handler in which the callback will be invoked in.
+         * @param camera  The {@link CameraProxy} which the callback is from.
+         * @param cb      The callback to be invoked.
+         * @return        The instance of the {@link AFCallbackForward},
+         *                or null if any parameter is null.
+         */
+        public static ErrorCallbackForward getNewInstance(
+                Handler handler, CameraProxy camera, CameraErrorCallback cb) {
+            if (handler == null || camera == null || cb == null) {
+                return null;
+            }
+            return new ErrorCallbackForward(handler, camera, cb);
+        }
+
+        private ErrorCallbackForward(
+                Handler h, CameraProxy camera, CameraErrorCallback cb) {
+            mHandler = h;
+            mCamera = camera;
+            mCallback = cb;
+        }
+
+        @Override
+        public void onError(final int error, Camera camera) {
+            mHandler.post(new Runnable() {
+                @Override
+                public void run() {
+                    mCallback.onError(error, mCamera);
+                }
+            });
+        }
+    }
+
+    /** A helper class to forward AutoFocusMoveCallback to another thread. */
+    @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
+    private static class AFMoveCallbackForward implements AutoFocusMoveCallback {
+        private final Handler mHandler;
+        private final CameraAFMoveCallback mCallback;
+        private final CameraProxy mCamera;
+
+        /**
+         * Returns a new instance of {@link AFMoveCallbackForward}.
+         *
+         * @param handler The handler in which the callback will be invoked in.
+         * @param camera  The {@link CameraProxy} which the callback is from.
+         * @param cb      The callback to be invoked.
+         * @return        The instance of the {@link AFMoveCallbackForward},
+         *                or null if any parameter is null.
+         */
+        public static AFMoveCallbackForward getNewInstance(
+                Handler handler, CameraProxy camera, CameraAFMoveCallback cb) {
+            if (handler == null || camera == null || cb == null) {
+                return null;
+            }
+            return new AFMoveCallbackForward(handler, camera, cb);
+        }
+
+        private AFMoveCallbackForward(
+                Handler h, CameraProxy camera, CameraAFMoveCallback cb) {
+            mHandler = h;
+            mCamera = camera;
+            mCallback = cb;
+        }
+
+        @Override
+        public void onAutoFocusMoving(
+                final boolean moving, android.hardware.Camera camera) {
+            mHandler.post(new Runnable() {
+                @Override
+                public void run() {
+                    mCallback.onAutoFocusMoving(moving, mCamera);
+                }
+            });
+        }
+    }
+
+    /**
+     * A helper class to forward ShutterCallback to to another thread.
+     */
+    private static class ShutterCallbackForward implements ShutterCallback {
+        private final Handler mHandler;
+        private final CameraShutterCallback mCallback;
+        private final CameraProxy mCamera;
+
+        /**
+         * Returns a new instance of {@link ShutterCallbackForward}.
+         *
+         * @param handler The handler in which the callback will be invoked in.
+         * @param camera  The {@link CameraProxy} which the callback is from.
+         * @param cb      The callback to be invoked.
+         * @return        The instance of the {@link ShutterCallbackForward},
+         *                or null if any parameter is null.
+         */
+        public static ShutterCallbackForward getNewInstance(
+                Handler handler, CameraProxy camera, CameraShutterCallback cb) {
+            if (handler == null || camera == null || cb == null) {
+                return null;
+            }
+            return new ShutterCallbackForward(handler, camera, cb);
+        }
+
+        private ShutterCallbackForward(
+                Handler h, CameraProxy camera, CameraShutterCallback cb) {
+            mHandler = h;
+            mCamera = camera;
+            mCallback = cb;
+        }
+
+        @Override
+        public void onShutter() {
+            mHandler.post(new Runnable() {
+                @Override
+                public void run() {
+                    mCallback.onShutter(mCamera);
+                }
+            });
+        }
+    }
+
+    /**
+     * A helper class to forward PictureCallback to another thread.
+     */
+    private static class PictureCallbackForward implements PictureCallback {
+        private final Handler mHandler;
+        private final CameraPictureCallback mCallback;
+        private final CameraProxy mCamera;
+
+        /**
+         * Returns a new instance of {@link PictureCallbackForward}.
+         *
+         * @param handler The handler in which the callback will be invoked in.
+         * @param camera  The {@link CameraProxy} which the callback is from.
+         * @param cb      The callback to be invoked.
+         * @return        The instance of the {@link PictureCallbackForward},
+         *                or null if any parameters is null.
+         */
+        public static PictureCallbackForward getNewInstance(
+                Handler handler, CameraProxy camera, CameraPictureCallback cb) {
+            if (handler == null || camera == null || cb == null) {
+                return null;
+            }
+            return new PictureCallbackForward(handler, camera, cb);
+        }
+
+        private PictureCallbackForward(
+                Handler h, CameraProxy camera, CameraPictureCallback cb) {
+            mHandler = h;
+            mCamera = camera;
+            mCallback = cb;
+        }
+
+        @Override
+        public void onPictureTaken(
+                final byte[] data, android.hardware.Camera camera) {
+            mHandler.post(new Runnable() {
+                @Override
+                public void run() {
+                    mCallback.onPictureTaken(data, mCamera);
+                }
+            });
+        }
+    }
+
+    /**
+     * A helper class to forward PreviewCallback to another thread.
+     */
+    private static class PreviewCallbackForward implements PreviewCallback {
+        private final Handler mHandler;
+        private final CameraPreviewDataCallback mCallback;
+        private final CameraProxy mCamera;
+
+        /**
+         * Returns a new instance of {@link PreviewCallbackForward}.
+         *
+         * @param handler The handler in which the callback will be invoked in.
+         * @param camera  The {@link CameraProxy} which the callback is from.
+         * @param cb      The callback to be invoked.
+         * @return        The instance of the {@link PreviewCallbackForward},
+         *                or null if any parameters is null.
+         */
+        public static PreviewCallbackForward getNewInstance(
+                Handler handler, CameraProxy camera, CameraPreviewDataCallback cb) {
+            if (handler == null || camera == null || cb == null) {
+                return null;
+            }
+            return new PreviewCallbackForward(handler, camera, cb);
+        }
+
+        private PreviewCallbackForward(
+                Handler h, CameraProxy camera, CameraPreviewDataCallback cb) {
+            mHandler = h;
+            mCamera = camera;
+            mCallback = cb;
+        }
+
+        @Override
+        public void onPreviewFrame(
+                final byte[] data, android.hardware.Camera camera) {
+            mHandler.post(new Runnable() {
+                @Override
+                public void run() {
+                    mCallback.onPreviewFrame(data, mCamera);
+                }
+            });
+        }
+    }
+
+    private static class FaceDetectionCallbackForward implements FaceDetectionListener {
+        private final Handler mHandler;
+        private final CameraFaceDetectionCallback mCallback;
+        private final CameraProxy mCamera;
+
+        /**
+         * Returns a new instance of {@link FaceDetectionCallbackForward}.
+         *
+         * @param handler The handler in which the callback will be invoked in.
+         * @param camera  The {@link CameraProxy} which the callback is from.
+         * @param cb      The callback to be invoked.
+         * @return        The instance of the {@link FaceDetectionCallbackForward},
+         *                or null if any parameter is null.
+         */
+        public static FaceDetectionCallbackForward getNewInstance(
+                Handler handler, CameraProxy camera, CameraFaceDetectionCallback cb) {
+            if (handler == null || camera == null || cb == null) {
+                return null;
+            }
+            return new FaceDetectionCallbackForward(handler, camera, cb);
+        }
+
+        private FaceDetectionCallbackForward(
+                Handler h, CameraProxy camera, CameraFaceDetectionCallback cb) {
+            mHandler = h;
+            mCamera = camera;
+            mCallback = cb;
+        }
+
+        @Override
+        public void onFaceDetection(
+                final Camera.Face[] faces, Camera camera) {
+            mHandler.post(new Runnable() {
+                @Override
+                public void run() {
+                    mCallback.onFaceDetection(faces, mCamera);
+                }
+            });
+        }
+    }
+}
diff --git a/camera2/portability/src/com/android/ex/camera2/portability/AndroidCameraCapabilities.java b/camera2/portability/src/com/android/ex/camera2/portability/AndroidCameraCapabilities.java
new file mode 100644
index 0000000..28d1fd1
--- /dev/null
+++ b/camera2/portability/src/com/android/ex/camera2/portability/AndroidCameraCapabilities.java
@@ -0,0 +1,490 @@
+/*
+ * Copyright (C) 2014 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.android.ex.camera2.portability;
+
+import android.hardware.Camera;
+
+import com.android.ex.camera2.portability.debug.Log;
+
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+
+/**
+ * The subclass of {@link CameraCapabilities} for Android Camera 1 API.
+ */
+class AndroidCameraCapabilities extends CameraCapabilities {
+
+    private static Log.Tag TAG = new Log.Tag("AndroidCameraCapabilities");
+
+    private FpsComparator mFpsComparator = new FpsComparator();
+    private SizeComparator mSizeComparator = new SizeComparator();
+
+    AndroidCameraCapabilities(Camera.Parameters p) {
+        super(new AndroidCameraCapabilityStringifier());
+        mMaxExposureCompensation = p.getMaxExposureCompensation();
+        mMinExposureCompensation = p.getMinExposureCompensation();
+        mExposureCompensationStep = p.getExposureCompensationStep();
+        mMaxNumOfFacesSupported = p.getMaxNumDetectedFaces();
+        mMaxNumOfMeteringArea = p.getMaxNumMeteringAreas();
+        mPreferredPreviewSizeForVideo = new Size(p.getPreferredPreviewSizeForVideo());
+        mSupportedPreviewFormats.addAll(p.getSupportedPreviewFormats());
+        mSupportedPhotoFormats.addAll(p.getSupportedPictureFormats());
+        mMaxZoomIndex = p.getMaxZoom();
+        mZoomRatioList.addAll(p.getZoomRatios());
+        mMaxZoomRatio = mZoomRatioList.get(mMaxZoomIndex);
+        mHorizontalViewAngle = p.getHorizontalViewAngle();
+        mVerticalViewAngle = p.getVerticalViewAngle();
+        buildPreviewFpsRange(p);
+        buildPreviewSizes(p);
+        buildVideoSizes(p);
+        buildPictureSizes(p);
+        buildSceneModes(p);
+        buildFlashModes(p);
+        buildFocusModes(p);
+        buildWhiteBalances(p);
+
+        if (p.isZoomSupported()) {
+            mSupportedFeatures.add(Feature.ZOOM);
+        }
+        if (p.isVideoSnapshotSupported()) {
+            mSupportedFeatures.add(Feature.VIDEO_SNAPSHOT);
+        }
+        if (p.isAutoExposureLockSupported()) {
+            mSupportedFeatures.add(Feature.AUTO_EXPOSURE_LOCK);
+        }
+        if (p.isAutoWhiteBalanceLockSupported()) {
+            mSupportedFeatures.add(Feature.AUTO_WHITE_BALANCE_LOCK);
+        }
+        if (supports(FocusMode.AUTO)) {
+            mMaxNumOfFocusAreas = p.getMaxNumFocusAreas();
+            if (mMaxNumOfFocusAreas > 0) {
+                mSupportedFeatures.add(Feature.FOCUS_AREA);
+            }
+        }
+        if (mMaxNumOfMeteringArea > 0) {
+            mSupportedFeatures.add(Feature.METERING_AREA);
+        }
+    }
+
+    AndroidCameraCapabilities(AndroidCameraCapabilities src) {
+        super(src);
+    }
+
+    private void buildPreviewFpsRange(Camera.Parameters p) {
+        List<int[]> supportedPreviewFpsRange = p.getSupportedPreviewFpsRange();
+        if (supportedPreviewFpsRange != null) {
+            mSupportedPreviewFpsRange.addAll(supportedPreviewFpsRange);
+        }
+        Collections.sort(mSupportedPreviewFpsRange, mFpsComparator);
+    }
+
+    private void buildPreviewSizes(Camera.Parameters p) {
+        List<Camera.Size> supportedPreviewSizes = p.getSupportedPreviewSizes();
+        if (supportedPreviewSizes != null) {
+            for (Camera.Size s : supportedPreviewSizes) {
+                mSupportedPreviewSizes.add(new Size(s.width, s.height));
+            }
+        }
+        Collections.sort(mSupportedPreviewSizes, mSizeComparator);
+    }
+
+    private void buildVideoSizes(Camera.Parameters p) {
+        List<Camera.Size> supportedVideoSizes = p.getSupportedVideoSizes();
+        if (supportedVideoSizes != null) {
+            for (Camera.Size s : supportedVideoSizes) {
+                mSupportedVideoSizes.add(new Size(s.width, s.height));
+            }
+        }
+        Collections.sort(mSupportedVideoSizes, mSizeComparator);
+    }
+
+    private void buildPictureSizes(Camera.Parameters p) {
+        List<Camera.Size> supportedPictureSizes = p.getSupportedPictureSizes();
+        if (supportedPictureSizes != null) {
+            for (Camera.Size s : supportedPictureSizes) {
+                mSupportedPhotoSizes.add(new Size(s.width, s.height));
+            }
+        }
+        Collections.sort(mSupportedPhotoSizes, mSizeComparator);
+
+    }
+
+    private void buildSceneModes(Camera.Parameters p) {
+        List<String> supportedSceneModes = p.getSupportedSceneModes();
+        if (supportedSceneModes != null) {
+            for (String scene : supportedSceneModes) {
+                if (Camera.Parameters.SCENE_MODE_AUTO.equals(scene)) {
+                    mSupportedSceneModes.add(SceneMode.AUTO);
+                } else if (Camera.Parameters.SCENE_MODE_ACTION.equals(scene)) {
+                    mSupportedSceneModes.add(SceneMode.ACTION);
+                } else if (Camera.Parameters.SCENE_MODE_BARCODE.equals(scene)) {
+                    mSupportedSceneModes.add(SceneMode.BARCODE);
+                } else if (Camera.Parameters.SCENE_MODE_BEACH.equals(scene)) {
+                    mSupportedSceneModes.add(SceneMode.BEACH);
+                } else if (Camera.Parameters.SCENE_MODE_CANDLELIGHT.equals(scene)) {
+                    mSupportedSceneModes.add(SceneMode.CANDLELIGHT);
+                } else if (Camera.Parameters.SCENE_MODE_FIREWORKS.equals(scene)) {
+                    mSupportedSceneModes.add(SceneMode.FIREWORKS);
+                } else if (Camera.Parameters.SCENE_MODE_HDR.equals(scene)) {
+                    mSupportedSceneModes.add(SceneMode.HDR);
+                } else if (Camera.Parameters.SCENE_MODE_LANDSCAPE.equals(scene)) {
+                    mSupportedSceneModes.add(SceneMode.LANDSCAPE);
+                } else if (Camera.Parameters.SCENE_MODE_NIGHT.equals(scene)) {
+                    mSupportedSceneModes.add(SceneMode.NIGHT);
+                } else if (Camera.Parameters.SCENE_MODE_NIGHT_PORTRAIT.equals(scene)) {
+                    mSupportedSceneModes.add(SceneMode.NIGHT_PORTRAIT);
+                } else if (Camera.Parameters.SCENE_MODE_PARTY.equals(scene)) {
+                    mSupportedSceneModes.add(SceneMode.PARTY);
+                } else if (Camera.Parameters.SCENE_MODE_PORTRAIT.equals(scene)) {
+                    mSupportedSceneModes.add(SceneMode.PORTRAIT);
+                } else if (Camera.Parameters.SCENE_MODE_SNOW.equals(scene)) {
+                    mSupportedSceneModes.add(SceneMode.SNOW);
+                } else if (Camera.Parameters.SCENE_MODE_SPORTS.equals(scene)) {
+                    mSupportedSceneModes.add(SceneMode.SPORTS);
+                } else if (Camera.Parameters.SCENE_MODE_STEADYPHOTO.equals(scene)) {
+                    mSupportedSceneModes.add(SceneMode.STEADYPHOTO);
+                } else if (Camera.Parameters.SCENE_MODE_SUNSET.equals(scene)) {
+                    mSupportedSceneModes.add(SceneMode.SUNSET);
+                } else if (Camera.Parameters.SCENE_MODE_THEATRE.equals(scene)) {
+                    mSupportedSceneModes.add(SceneMode.THEATRE);
+                }
+            }
+        }
+    }
+
+    private void buildFlashModes(Camera.Parameters p) {
+        List<String> supportedFlashModes = p.getSupportedFlashModes();
+        if (supportedFlashModes == null) {
+            // Camera 1 will return NULL if no flash mode is supported.
+            mSupportedFlashModes.add(FlashMode.NO_FLASH);
+        } else {
+            for (String flash : supportedFlashModes) {
+                if (Camera.Parameters.FLASH_MODE_AUTO.equals(flash)) {
+                    mSupportedFlashModes.add(FlashMode.AUTO);
+                } else if (Camera.Parameters.FLASH_MODE_OFF.equals(flash)) {
+                    mSupportedFlashModes.add(FlashMode.OFF);
+                } else if (Camera.Parameters.FLASH_MODE_ON.equals(flash)) {
+                    mSupportedFlashModes.add(FlashMode.ON);
+                } else if (Camera.Parameters.FLASH_MODE_RED_EYE.equals(flash)) {
+                    mSupportedFlashModes.add(FlashMode.RED_EYE);
+                }
+            }
+        }
+    }
+
+    private void buildFocusModes(Camera.Parameters p) {
+        List<String> supportedFocusModes = p.getSupportedFocusModes();
+        if (supportedFocusModes != null) {
+            for (String focus : supportedFocusModes) {
+                if (Camera.Parameters.FOCUS_MODE_AUTO.equals(focus)) {
+                    mSupportedFocusModes.add(FocusMode.AUTO);
+                } else if (Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE.equals(focus)) {
+                    mSupportedFocusModes.add(FocusMode.CONTINUOUS_PICTURE);
+                } else if (Camera.Parameters.FOCUS_MODE_CONTINUOUS_VIDEO.equals(focus)) {
+                    mSupportedFocusModes.add(FocusMode.CONTINUOUS_VIDEO);
+                } else if (Camera.Parameters.FOCUS_MODE_EDOF.equals(focus)) {
+                    mSupportedFocusModes.add(FocusMode.EXTENDED_DOF);
+                } else if (Camera.Parameters.FOCUS_MODE_FIXED.equals(focus)) {
+                    mSupportedFocusModes.add(FocusMode.FIXED);
+                } else if (Camera.Parameters.FOCUS_MODE_INFINITY.equals(focus)) {
+                    mSupportedFocusModes.add(FocusMode.INFINITY);
+                } else if (Camera.Parameters.FOCUS_MODE_MACRO.equals(focus)) {
+                    mSupportedFocusModes.add(FocusMode.MACRO);
+                }
+            }
+        }
+    }
+
+    private void buildWhiteBalances(Camera.Parameters p) {
+        List<String> supportedWhiteBalances = p.getSupportedFocusModes();
+        if (supportedWhiteBalances != null) {
+            for (String wb : supportedWhiteBalances) {
+                if (Camera.Parameters.WHITE_BALANCE_AUTO.equals(wb)) {
+                    mSupportedWhiteBalances.add(WhiteBalance.AUTO);
+                } else if (Camera.Parameters.WHITE_BALANCE_CLOUDY_DAYLIGHT.equals(wb)) {
+                    mSupportedWhiteBalances.add(WhiteBalance.CLOUDY_DAYLIGHT);
+                } else if (Camera.Parameters.WHITE_BALANCE_DAYLIGHT.equals(wb)) {
+                    mSupportedWhiteBalances.add(WhiteBalance.DAYLIGHT);
+                } else if (Camera.Parameters.WHITE_BALANCE_FLUORESCENT.equals(wb)) {
+                    mSupportedWhiteBalances.add(WhiteBalance.FLUORESCENT);
+                } else if (Camera.Parameters.WHITE_BALANCE_INCANDESCENT.equals(wb)) {
+                    mSupportedWhiteBalances.add(WhiteBalance.INCANDESCENT);
+                } else if (Camera.Parameters.WHITE_BALANCE_SHADE.equals(wb)) {
+                    mSupportedWhiteBalances.add(WhiteBalance.SHADE);
+                } else if (Camera.Parameters.WHITE_BALANCE_TWILIGHT.equals(wb)) {
+                    mSupportedWhiteBalances.add(WhiteBalance.TWILIGHT);
+                } else if (Camera.Parameters.WHITE_BALANCE_WARM_FLUORESCENT.equals(wb)) {
+                    mSupportedWhiteBalances.add(WhiteBalance.WARM_FLUORESCENT);
+                }
+            }
+        }
+    }
+
+    private static class FpsComparator implements Comparator<int[]> {
+        @Override
+        public int compare(int[] fps1, int[] fps2) {
+            return (fps1[0] == fps2[0] ? fps1[1] - fps2[1] : fps1[0] - fps2[0]);
+        }
+    }
+
+    private static class SizeComparator implements Comparator<Size> {
+
+        @Override
+        public int compare(Size size1, Size size2) {
+            return (size1.width() == size2.width() ? size1.height() - size2.height() :
+                    size1.width() - size2.width());
+        }
+    }
+
+    private static class AndroidCameraCapabilityStringifier implements Stringifier {
+
+        @Override
+        public String stringify(FocusMode focus) {
+            if (focus == null) {
+                return null;
+            }
+
+            switch (focus) {
+                case AUTO:
+                    return Camera.Parameters.FOCUS_MODE_AUTO;
+                case CONTINUOUS_PICTURE:
+                    return Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE;
+                case CONTINUOUS_VIDEO:
+                    return Camera.Parameters.FOCUS_MODE_CONTINUOUS_VIDEO;
+                case EXTENDED_DOF:
+                    return Camera.Parameters.FOCUS_MODE_EDOF;
+                case FIXED:
+                    return Camera.Parameters.FOCUS_MODE_FIXED;
+                case INFINITY:
+                    return Camera.Parameters.FOCUS_MODE_INFINITY;
+                case MACRO:
+                    return Camera.Parameters.FOCUS_MODE_MACRO;
+            }
+            return null;
+        }
+
+        @Override
+        public FocusMode focusModeFromString(String val) {
+            if (val == null) {
+                return null;
+            }
+
+            if (Camera.Parameters.FOCUS_MODE_AUTO.equals(val)) {
+                return FocusMode.AUTO;
+            } else if (Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE.equals(val)) {
+                return FocusMode.CONTINUOUS_PICTURE;
+            } else if (Camera.Parameters.FOCUS_MODE_CONTINUOUS_VIDEO.equals(val)) {
+                return FocusMode.CONTINUOUS_VIDEO;
+            } else if (Camera.Parameters.FOCUS_MODE_EDOF.equals(val)) {
+                return FocusMode.EXTENDED_DOF;
+            } else if (Camera.Parameters.FOCUS_MODE_FIXED.equals(val)) {
+                return FocusMode.FIXED;
+            } else if (Camera.Parameters.FOCUS_MODE_INFINITY.equals(val)) {
+                return FocusMode.INFINITY;
+            } else if (Camera.Parameters.FOCUS_MODE_MACRO.equals(val)) {
+                return FocusMode.MACRO;
+            } else {
+                return null;
+            }
+        }
+
+        @Override
+        public String stringify(FlashMode flash) {
+            if (flash == null) {
+                return null;
+            }
+
+            switch (flash) {
+                case NO_FLASH:
+                    return null;
+                case AUTO:
+                    return Camera.Parameters.FLASH_MODE_AUTO;
+                case OFF:
+                    return Camera.Parameters.FLASH_MODE_OFF;
+                case ON:
+                    return Camera.Parameters.FLASH_MODE_ON;
+                case TORCH:
+                    return Camera.Parameters.FLASH_MODE_TORCH;
+                case RED_EYE:
+                    return Camera.Parameters.FLASH_MODE_RED_EYE;
+            }
+            return null;
+        }
+
+        @Override
+        public FlashMode flashModeFromString(String val) {
+            if (val == null) {
+                return FlashMode.NO_FLASH;
+            } else if (Camera.Parameters.FLASH_MODE_AUTO.equals(val)) {
+                return FlashMode.AUTO;
+            } else if (Camera.Parameters.FLASH_MODE_OFF.equals(val)) {
+                return FlashMode.OFF;
+            } else if (Camera.Parameters.FLASH_MODE_ON.equals(val)) {
+                return FlashMode.ON;
+            } else if (Camera.Parameters.FLASH_MODE_TORCH.equals(val)) {
+                return FlashMode.TORCH;
+            } else if (Camera.Parameters.FLASH_MODE_RED_EYE.equals(val)) {
+                return FlashMode.RED_EYE;
+            } else {
+                return null;
+            }
+        }
+
+        @Override
+        public String stringify(SceneMode scene) {
+            if (scene == null) {
+                return null;
+            }
+
+            switch (scene) {
+                case AUTO:
+                    return Camera.Parameters.SCENE_MODE_AUTO;
+                case ACTION:
+                    return Camera.Parameters.SCENE_MODE_ACTION;
+                case BARCODE:
+                    return Camera.Parameters.SCENE_MODE_BARCODE;
+                case BEACH:
+                    return Camera.Parameters.SCENE_MODE_BEACH;
+                case CANDLELIGHT:
+                    return Camera.Parameters.SCENE_MODE_CANDLELIGHT;
+                case FIREWORKS:
+                    return Camera.Parameters.SCENE_MODE_FIREWORKS;
+                case HDR:
+                    return Camera.Parameters.SCENE_MODE_HDR;
+                case LANDSCAPE:
+                    return Camera.Parameters.SCENE_MODE_LANDSCAPE;
+                case NIGHT:
+                    return Camera.Parameters.SCENE_MODE_NIGHT;
+                case NIGHT_PORTRAIT:
+                    return Camera.Parameters.SCENE_MODE_NIGHT_PORTRAIT;
+                case PARTY:
+                    return Camera.Parameters.SCENE_MODE_PARTY;
+                case PORTRAIT:
+                    return Camera.Parameters.SCENE_MODE_PORTRAIT;
+                case SNOW:
+                    return Camera.Parameters.SCENE_MODE_SNOW;
+                case SPORTS:
+                    return Camera.Parameters.SCENE_MODE_SPORTS;
+                case STEADYPHOTO:
+                    return Camera.Parameters.SCENE_MODE_STEADYPHOTO;
+                case SUNSET:
+                    return Camera.Parameters.SCENE_MODE_SUNSET;
+                case THEATRE:
+                    return Camera.Parameters.SCENE_MODE_THEATRE;
+            }
+            return null;
+        }
+
+        @Override
+        public SceneMode sceneModeFromString(String val) {
+            if (val == null) {
+                return SceneMode.NO_SCENE_MODE;
+            } else if (Camera.Parameters.SCENE_MODE_AUTO.equals(val)) {
+                return SceneMode.AUTO;
+            } else if (Camera.Parameters.SCENE_MODE_ACTION.equals(val)) {
+                return SceneMode.ACTION;
+            } else if (Camera.Parameters.SCENE_MODE_BARCODE.equals(val)) {
+                return SceneMode.BARCODE;
+            } else if (Camera.Parameters.SCENE_MODE_BEACH.equals(val)) {
+                return SceneMode.BEACH;
+            } else if (Camera.Parameters.SCENE_MODE_CANDLELIGHT.equals(val)) {
+                return SceneMode.CANDLELIGHT;
+            } else if (Camera.Parameters.SCENE_MODE_FIREWORKS.equals(val)) {
+                return SceneMode.FIREWORKS;
+            } else if (Camera.Parameters.SCENE_MODE_HDR.equals(val)) {
+                return SceneMode.HDR;
+            } else if (Camera.Parameters.SCENE_MODE_LANDSCAPE.equals(val)) {
+                return SceneMode.LANDSCAPE;
+            } else if (Camera.Parameters.SCENE_MODE_NIGHT.equals(val)) {
+                return SceneMode.NIGHT;
+            } else if (Camera.Parameters.SCENE_MODE_NIGHT_PORTRAIT.equals(val)) {
+                return SceneMode.NIGHT_PORTRAIT;
+            } else if (Camera.Parameters.SCENE_MODE_PARTY.equals(val)) {
+                return SceneMode.PARTY;
+            } else if (Camera.Parameters.SCENE_MODE_PORTRAIT.equals(val)) {
+                return SceneMode.PORTRAIT;
+            } else if (Camera.Parameters.SCENE_MODE_SNOW.equals(val)) {
+                return SceneMode.SNOW;
+            } else if (Camera.Parameters.SCENE_MODE_SPORTS.equals(val)) {
+                return SceneMode.SPORTS;
+            } else if (Camera.Parameters.SCENE_MODE_STEADYPHOTO.equals(val)) {
+                return SceneMode.STEADYPHOTO;
+            } else if (Camera.Parameters.SCENE_MODE_SUNSET.equals(val)) {
+                return SceneMode.SUNSET;
+            } else if (Camera.Parameters.SCENE_MODE_THEATRE.equals(val)) {
+                return SceneMode.THEATRE;
+            } else {
+                return null;
+            }
+        }
+
+        @Override
+        public String stringify(WhiteBalance wb) {
+            if (wb == null) {
+                return null;
+            }
+
+            switch (wb) {
+                case AUTO:
+                    return Camera.Parameters.WHITE_BALANCE_AUTO;
+                case CLOUDY_DAYLIGHT:
+                    return Camera.Parameters.WHITE_BALANCE_CLOUDY_DAYLIGHT;
+                case DAYLIGHT:
+                    return Camera.Parameters.WHITE_BALANCE_DAYLIGHT;
+                case FLUORESCENT:
+                    return Camera.Parameters.WHITE_BALANCE_FLUORESCENT;
+                case INCANDESCENT:
+                    return Camera.Parameters.WHITE_BALANCE_INCANDESCENT;
+                case SHADE:
+                    return Camera.Parameters.WHITE_BALANCE_SHADE;
+                case TWILIGHT:
+                    return Camera.Parameters.WHITE_BALANCE_TWILIGHT;
+                case WARM_FLUORESCENT:
+                    return Camera.Parameters.WHITE_BALANCE_WARM_FLUORESCENT;
+            }
+            return null;
+        }
+
+        @Override
+        public WhiteBalance whiteBalanceFromString(String val) {
+            if (val == null) {
+                return null;
+            }
+
+            if (Camera.Parameters.WHITE_BALANCE_AUTO.equals(val)) {
+                return WhiteBalance.AUTO;
+            } else if (Camera.Parameters.WHITE_BALANCE_CLOUDY_DAYLIGHT.equals(val)) {
+                return WhiteBalance.CLOUDY_DAYLIGHT;
+            } else if (Camera.Parameters.WHITE_BALANCE_DAYLIGHT.equals(val)) {
+                return WhiteBalance.DAYLIGHT;
+            } else if (Camera.Parameters.WHITE_BALANCE_FLUORESCENT.equals(val)) {
+                return WhiteBalance.FLUORESCENT;
+            } else if (Camera.Parameters.WHITE_BALANCE_INCANDESCENT.equals(val)) {
+                return WhiteBalance.INCANDESCENT;
+            } else if (Camera.Parameters.WHITE_BALANCE_SHADE.equals(val)) {
+                return WhiteBalance.SHADE;
+            } else if (Camera.Parameters.WHITE_BALANCE_TWILIGHT.equals(val)) {
+                return WhiteBalance.TWILIGHT;
+            } else if (Camera.Parameters.WHITE_BALANCE_WARM_FLUORESCENT.equals(val)) {
+                return WhiteBalance.WARM_FLUORESCENT;
+            } else {
+                return null;
+            }
+        }
+    }
+}
diff --git a/camera2/portability/src/com/android/ex/camera2/portability/AndroidCameraSettings.java b/camera2/portability/src/com/android/ex/camera2/portability/AndroidCameraSettings.java
new file mode 100644
index 0000000..a4039bd
--- /dev/null
+++ b/camera2/portability/src/com/android/ex/camera2/portability/AndroidCameraSettings.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2014 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.android.ex.camera2.portability;
+
+import android.hardware.Camera;
+
+/**
+ * The subclass of {@link CameraSettings} for Android Camera 1 API.
+ */
+public class AndroidCameraSettings extends CameraSettings {
+    private static final String TRUE = "true";
+    private static final String RECORDING_HINT = "recording-hint";
+
+    public AndroidCameraSettings(CameraCapabilities capabilities, Camera.Parameters params) {
+        CameraCapabilities.Stringifier stringifier = capabilities.getStringifier();
+
+        // Preview
+        Camera.Size paramPreviewSize = params.getPreviewSize();
+        setPreviewSize(new Size(paramPreviewSize.width, paramPreviewSize.height));
+        setPreviewFrameRate(params.getPreviewFrameRate());
+        int[] previewFpsRange = new int[2];
+        params.getPreviewFpsRange(previewFpsRange);
+        setPreviewFpsRange(previewFpsRange[Camera.Parameters.PREVIEW_FPS_MIN_INDEX],
+                previewFpsRange[Camera.Parameters.PREVIEW_FPS_MAX_INDEX]);
+        setPreviewFormat(params.getPreviewFormat());
+
+        // Capture: Focus, flash, zoom, exposure, scene mode.
+        if (capabilities.supports(CameraCapabilities.Feature.ZOOM)) {
+            setZoomRatio(params.getZoomRatios().get(params.getZoom()) / 100f);
+            setZoomIndex(params.getZoom());
+        } else {
+            setZoomRatio(1.0f);
+            setZoomIndex(0);
+        }
+        setExposureCompensationIndex(params.getExposureCompensation());
+        setFlashMode(stringifier.flashModeFromString(params.getFlashMode()));
+        setFocusMode(stringifier.focusModeFromString(params.getFocusMode()));
+        setSceneMode(stringifier.sceneModeFromString(params.getSceneMode()));
+
+        // Video capture.
+        if (capabilities.supports(CameraCapabilities.Feature.VIDEO_STABILIZATION)) {
+            setVideoStabilization(isVideoStabilizationEnabled());
+        }
+        setRecordingHintEnabled(TRUE.equals(params.get(RECORDING_HINT)));
+
+        // Output: Photo size, compression quality, rotation.
+        setPhotoRotationDegrees(0f);
+        setPhotoJpegCompressionQuality(params.getJpegQuality());
+        Camera.Size paramPictureSize = params.getPictureSize();
+        setPhotoSize(new Size(paramPictureSize.width, paramPictureSize.height));
+        setPhotoFormat(params.getPictureFormat());
+    }
+}
diff --git a/camera2/portability/src/com/android/ex/camera2/portability/CameraActions.java b/camera2/portability/src/com/android/ex/camera2/portability/CameraActions.java
new file mode 100644
index 0000000..aae122b
--- /dev/null
+++ b/camera2/portability/src/com/android/ex/camera2/portability/CameraActions.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2014 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.android.ex.camera2.portability;
+
+class CameraActions {
+    // Camera initialization/finalization
+    public static final int OPEN_CAMERA = 1;
+    public static final int RELEASE =     2;
+    public static final int RECONNECT =   3;
+    public static final int UNLOCK =      4;
+    public static final int LOCK =        5;
+    // Preview
+    public static final int SET_PREVIEW_TEXTURE_ASYNC =        101;
+    public static final int START_PREVIEW_ASYNC =              102;
+    public static final int STOP_PREVIEW =                     103;
+    public static final int SET_PREVIEW_CALLBACK_WITH_BUFFER = 104;
+    public static final int ADD_CALLBACK_BUFFER =              105;
+    public static final int SET_PREVIEW_DISPLAY_ASYNC =        106;
+    public static final int SET_PREVIEW_CALLBACK =             107;
+    public static final int SET_ONE_SHOT_PREVIEW_CALLBACK =    108;
+    // Parameters
+    public static final int SET_PARAMETERS =     201;
+    public static final int GET_PARAMETERS =     202;
+    public static final int REFRESH_PARAMETERS = 203;
+    public static final int APPLY_SETTINGS =     204;
+    // Focus, Zoom
+    public static final int AUTO_FOCUS =                   301;
+    public static final int CANCEL_AUTO_FOCUS =            302;
+    public static final int SET_AUTO_FOCUS_MOVE_CALLBACK = 303;
+    public static final int SET_ZOOM_CHANGE_LISTENER =     304;
+    // Face detection
+    public static final int SET_FACE_DETECTION_LISTENER = 461;
+    public static final int START_FACE_DETECTION =        462;
+    public static final int STOP_FACE_DETECTION =         463;
+    public static final int SET_ERROR_CALLBACK =          464;
+    // Presentation
+    public static final int ENABLE_SHUTTER_SOUND =    501;
+    public static final int SET_DISPLAY_ORIENTATION = 502;
+    // Capture
+    public static final int CAPTURE_PHOTO = 601;
+}
diff --git a/camera2/portability/src/com/android/ex/camera2/portability/CameraAgent.java b/camera2/portability/src/com/android/ex/camera2/portability/CameraAgent.java
new file mode 100644
index 0000000..db3b1d7
--- /dev/null
+++ b/camera2/portability/src/com/android/ex/camera2/portability/CameraAgent.java
@@ -0,0 +1,591 @@
+/*
+ * Copyright (C) 2012 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.android.ex.camera2.portability;
+
+import android.annotation.TargetApi;
+import android.graphics.SurfaceTexture;
+import android.hardware.Camera;
+import android.hardware.Camera.OnZoomChangeListener;
+import android.os.Build;
+import android.os.Handler;
+import android.os.Looper;
+import android.view.SurfaceHolder;
+
+/**
+ * An interface which provides possible camera device operations.
+ *
+ * The client should call {@code CameraAgent.openCamera} to get an instance
+ * of {@link CameraAgent.CameraProxy} to control the camera. Classes
+ * implementing this interface should have its own one unique {@code Thread}
+ * other than the main thread for camera operations. Camera device callbacks
+ * are wrapped since the client should not deal with
+ * {@code android.hardware.Camera} directly.
+ *
+ * TODO: provide callback interfaces for:
+ * {@code android.hardware.Camera.ErrorCallback},
+ * {@code android.hardware.Camera.OnZoomChangeListener}, and
+ */
+public interface CameraAgent {
+    public static final long CAMERA_OPERATION_TIMEOUT_MS = 2500;
+
+    public static class CameraStartPreviewCallbackForward
+            implements CameraStartPreviewCallback {
+        private final Handler mHandler;
+        private final CameraStartPreviewCallback mCallback;
+
+        public static CameraStartPreviewCallbackForward getNewInstance(
+                Handler handler, CameraStartPreviewCallback cb) {
+            if (handler == null || cb == null) {
+                return null;
+            }
+            return new CameraStartPreviewCallbackForward(handler, cb);
+        }
+
+        private CameraStartPreviewCallbackForward(Handler h,
+                CameraStartPreviewCallback cb) {
+            mHandler = h;
+            mCallback = cb;
+        }
+
+        @Override
+        public void onPreviewStarted() {
+            mHandler.post(new Runnable() {
+                @Override
+                public void run() {
+                    mCallback.onPreviewStarted();
+                }
+            });
+        }
+    }
+
+    /**
+     * A callback helps to invoke the original callback on another
+     * {@link android.os.Handler}.
+     */
+    public static class CameraOpenCallbackForward implements CameraOpenCallback {
+        private final Handler mHandler;
+        private final CameraOpenCallback mCallback;
+
+        /**
+         * Returns a new instance of {@link FaceDetectionCallbackForward}.
+         *
+         * @param handler The handler in which the callback will be invoked in.
+         * @param cb The callback to be invoked.
+         * @return The instance of the {@link FaceDetectionCallbackForward}, or
+         *         null if any parameter is null.
+         */
+        public static CameraOpenCallbackForward getNewInstance(
+                Handler handler, CameraOpenCallback cb) {
+            if (handler == null || cb == null) {
+                return null;
+            }
+            return new CameraOpenCallbackForward(handler, cb);
+        }
+
+        private CameraOpenCallbackForward(Handler h, CameraOpenCallback cb) {
+            // Given that we are using the main thread handler, we can create it
+            // here instead of holding onto the PhotoModule objects. In this
+            // way, we can avoid memory leak.
+            mHandler = new Handler(Looper.getMainLooper());
+            mCallback = cb;
+        }
+
+        @Override
+        public void onCameraOpened(final CameraProxy camera) {
+            mHandler.post(new Runnable() {
+                @Override
+                public void run() {
+                    mCallback.onCameraOpened(camera);
+                }
+            });
+        }
+
+        @Override
+        public void onCameraDisabled(final int cameraId) {
+            mHandler.post(new Runnable() {
+                @Override
+                public void run() {
+                    mCallback.onCameraDisabled(cameraId);
+                }
+            });
+        }
+
+        @Override
+        public void onDeviceOpenFailure(final int cameraId, final String info) {
+            mHandler.post(new Runnable() {
+                @Override
+                public void run() {
+                    mCallback.onDeviceOpenFailure(cameraId, info);
+                }
+            });
+        }
+
+        @Override
+        public void onDeviceOpenedAlready(final int cameraId, final String info) {
+            mHandler.post(new Runnable() {
+                @Override
+                public void run() {
+                    mCallback.onDeviceOpenedAlready(cameraId, info);
+                }
+            });
+        }
+
+        @Override
+        public void onReconnectionFailure(final CameraAgent mgr, final String info) {
+            mHandler.post(new Runnable() {
+                @Override
+                public void run() {
+                    mCallback.onReconnectionFailure(mgr, info);
+                }
+            });
+        }
+    }
+
+    /**
+     * A handler for all camera api runtime exceptions.
+     * The default behavior is to throw the runtime exception.
+     */
+    public interface CameraExceptionCallback {
+        public void onCameraException(RuntimeException e);
+    }
+
+    /**
+     * An interface which wraps
+     * {@link android.hardware.Camera.ErrorCallback}
+     */
+    public interface CameraErrorCallback {
+        public void onError(int error, CameraProxy camera);
+    }
+
+    /**
+     * An interface which wraps
+     * {@link android.hardware.Camera.AutoFocusCallback}.
+     */
+    public interface CameraAFCallback {
+        public void onAutoFocus(boolean focused, CameraProxy camera);
+    }
+
+    /**
+     * An interface which wraps
+     * {@link android.hardware.Camera.AutoFocusMoveCallback}.
+     */
+    public interface CameraAFMoveCallback {
+        public void onAutoFocusMoving(boolean moving, CameraProxy camera);
+    }
+
+    /**
+     * An interface which wraps
+     * {@link android.hardware.Camera.ShutterCallback}.
+     */
+    public interface CameraShutterCallback {
+        public void onShutter(CameraProxy camera);
+    }
+
+    /**
+     * An interface which wraps
+     * {@link android.hardware.Camera.PictureCallback}.
+     */
+    public interface CameraPictureCallback {
+        public void onPictureTaken(byte[] data, CameraProxy camera);
+    }
+
+    /**
+     * An interface which wraps
+     * {@link android.hardware.Camera.PreviewCallback}.
+     */
+    public interface CameraPreviewDataCallback {
+        public void onPreviewFrame(byte[] data, CameraProxy camera);
+    }
+
+    /**
+     * An interface which wraps
+     * {@link android.hardware.Camera.FaceDetectionListener}.
+     */
+    public interface CameraFaceDetectionCallback {
+        /**
+         * Callback for face detection.
+         *
+         * @param faces   Recognized face in the preview.
+         * @param camera  The camera which the preview image comes from.
+         */
+        public void onFaceDetection(Camera.Face[] faces, CameraProxy camera);
+    }
+
+    /**
+     * An interface to be called when the camera preview has started.
+     */
+    public interface CameraStartPreviewCallback {
+        /**
+         * Callback when the preview starts.
+         */
+        public void onPreviewStarted();
+    }
+
+    /**
+     * An interface to be called for any events when opening or closing the
+     * camera device. This error callback is different from the one defined
+     * in the framework, {@link android.hardware.Camera.ErrorCallback}, which
+     * is used after the camera is opened.
+     */
+    public interface CameraOpenCallback {
+        /**
+         * Callback when camera open succeeds.
+         */
+        public void onCameraOpened(CameraProxy camera);
+
+        /**
+         * Callback when {@link com.android.camera.CameraDisabledException} is
+         * caught.
+         *
+         * @param cameraId The disabled camera.
+         */
+        public void onCameraDisabled(int cameraId);
+
+        /**
+         * Callback when {@link com.android.camera.CameraHardwareException} is
+         * caught.
+         *
+         * @param cameraId The camera with the hardware failure.
+         * @param info The extra info regarding this failure.
+         */
+        public void onDeviceOpenFailure(int cameraId, String info);
+
+        /**
+         * Callback when trying to open the camera which is already opened.
+         *
+         * @param cameraId The camera which is causing the open error.
+         */
+        public void onDeviceOpenedAlready(int cameraId, String info);
+
+        /**
+         * Callback when {@link java.io.IOException} is caught during
+         * {@link android.hardware.Camera#reconnect()}.
+         *
+         * @param mgr The {@link CameraAgent}
+         *            with the reconnect failure.
+         */
+        public void onReconnectionFailure(CameraAgent mgr, String info);
+    }
+
+    /**
+     * Opens the camera of the specified ID asynchronously. The camera device
+     * will be opened in the camera handler thread and will be returned through
+     * the {@link CameraAgent.CameraOpenCallback#
+     * onCameraOpened(com.android.camera.cameradevice.CameraAgent.CameraProxy)}.
+     *
+     * @param handler The {@link android.os.Handler} in which the callback
+     *                was handled.
+     * @param callback The callback for the result.
+     * @param cameraId The camera ID to open.
+     */
+    public void openCamera(Handler handler, int cameraId, CameraOpenCallback callback);
+
+    /**
+     * Closes the camera device.
+     *
+     * @param camera The camera to close. {@code null} means all.
+     * @param synced Whether this call should be synchronous.
+     */
+    public void closeCamera(CameraProxy camera, boolean synced);
+
+    /**
+     * Sets a callback for handling camera api runtime exceptions on
+     * a handler.
+     */
+    public void setCameraDefaultExceptionCallback(CameraExceptionCallback callback,
+            Handler handler);
+
+    /**
+     * Recycles the resources used by this instance. CameraAgent will be in
+     * an unusable state after calling this.
+     */
+    public void recycle();
+
+    /**
+     * @return The camera devices info.
+     */
+    public CameraDeviceInfo getCameraDeviceInfo();
+
+    /**
+     * An interface that takes camera operation requests and post messages to the
+     * camera handler thread. All camera operations made through this interface is
+     * asynchronous by default except those mentioned specifically.
+     */
+    public interface CameraProxy {
+
+        /**
+         * Returns the underlying {@link android.hardware.Camera} object used
+         * by this proxy. This method should only be used when handing the
+         * camera device over to {@link android.media.MediaRecorder} for
+         * recording.
+         */
+        @Deprecated
+        public android.hardware.Camera getCamera();
+
+        /**
+         * @return The camera ID associated to by this
+         * {@link CameraAgent.CameraProxy}.
+         */
+        public int getCameraId();
+
+        /**
+         * @return The camera capabilities.
+         */
+        public CameraCapabilities getCapabilities();
+
+        /**
+         * Reconnects to the camera device. On success, the camera device will
+         * be returned through {@link CameraAgent
+         * .CameraOpenCallback#onCameraOpened(com.android.camera.cameradevice.CameraAgent
+         * .CameraProxy)}.
+         * @see android.hardware.Camera#reconnect()
+         *
+         * @param handler The {@link android.os.Handler} in which the callback
+         *                was handled.
+         * @param cb The callback when any error happens.
+         */
+        public void reconnect(Handler handler, CameraOpenCallback cb);
+
+        /**
+         * Unlocks the camera device.
+         *
+         * @see android.hardware.Camera#unlock()
+         */
+        public void unlock();
+
+        /**
+         * Locks the camera device.
+         * @see android.hardware.Camera#lock()
+         */
+        public void lock();
+
+        /**
+         * Sets the {@link android.graphics.SurfaceTexture} for preview.
+         *
+         * @param surfaceTexture The {@link SurfaceTexture} for preview.
+         */
+        public void setPreviewTexture(final SurfaceTexture surfaceTexture);
+
+        /**
+         * Blocks until a {@link android.graphics.SurfaceTexture} has been set
+         * for preview.
+         *
+         * @param surfaceTexture The {@link SurfaceTexture} for preview.
+         */
+        public void setPreviewTextureSync(final SurfaceTexture surfaceTexture);
+
+        /**
+         * Sets the {@link android.view.SurfaceHolder} for preview.
+         *
+         * @param surfaceHolder The {@link SurfaceHolder} for preview.
+         */
+        public void setPreviewDisplay(final SurfaceHolder surfaceHolder);
+
+        /**
+         * Starts the camera preview.
+         */
+        public void startPreview();
+
+        /**
+         * Starts the camera preview and executes a callback on a handler once
+         * the preview starts.
+         */
+        public void startPreviewWithCallback(Handler h, CameraStartPreviewCallback cb);
+
+        /**
+         * Stops the camera preview synchronously.
+         * {@code stopPreview()} must be synchronous to ensure that the caller can
+         * continues to release resources related to camera preview.
+         */
+        public void stopPreview();
+
+        /**
+         * Sets the callback for preview data.
+         *
+         * @param handler    The {@link android.os.Handler} in which the callback was handled.
+         * @param cb         The callback to be invoked when the preview data is available.
+         * @see  android.hardware.Camera#setPreviewCallback(android.hardware.Camera.PreviewCallback)
+         */
+        public void setPreviewDataCallback(Handler handler, CameraPreviewDataCallback cb);
+
+        /**
+         * Sets the one-time callback for preview data.
+         *
+         * @param handler    The {@link android.os.Handler} in which the callback was handled.
+         * @param cb         The callback to be invoked when the preview data for
+         *                   next frame is available.
+         * @see  android.hardware.Camera#setPreviewCallback(android.hardware.Camera.PreviewCallback)
+         */
+        public void setOneShotPreviewCallback(Handler handler, CameraPreviewDataCallback cb);
+
+        /**
+         * Sets the callback for preview data.
+         *
+         * @param handler The handler in which the callback will be invoked.
+         * @param cb      The callback to be invoked when the preview data is available.
+         * @see android.hardware.Camera#setPreviewCallbackWithBuffer(android.hardware.Camera.PreviewCallback)
+         */
+        public void setPreviewDataCallbackWithBuffer(Handler handler, CameraPreviewDataCallback cb);
+
+        /**
+         * Adds buffer for the preview callback.
+         *
+         * @param callbackBuffer The buffer allocated for the preview data.
+         */
+        public void addCallbackBuffer(byte[] callbackBuffer);
+
+        /**
+         * Starts the auto-focus process. The result will be returned through the callback.
+         *
+         * @param handler The handler in which the callback will be invoked.
+         * @param cb      The auto-focus callback.
+         */
+        public void autoFocus(Handler handler, CameraAFCallback cb);
+
+        /**
+         * Cancels the auto-focus process.
+         */
+        public void cancelAutoFocus();
+
+        /**
+         * Sets the auto-focus callback
+         *
+         * @param handler The handler in which the callback will be invoked.
+         * @param cb      The callback to be invoked when the preview data is available.
+         */
+        @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
+        public void setAutoFocusMoveCallback(Handler handler, CameraAFMoveCallback cb);
+
+        /**
+         * Instrument the camera to take a picture.
+         *
+         * @param handler   The handler in which the callback will be invoked.
+         * @param shutter   The callback for shutter action, may be null.
+         * @param raw       The callback for uncompressed data, may be null.
+         * @param postview  The callback for postview image data, may be null.
+         * @param jpeg      The callback for jpeg image data, may be null.
+         * @see android.hardware.Camera#takePicture(
+         *         android.hardware.Camera.ShutterCallback,
+         *         android.hardware.Camera.PictureCallback,
+         *         android.hardware.Camera.PictureCallback)
+         */
+        public void takePicture(
+                Handler handler,
+                CameraShutterCallback shutter,
+                CameraPictureCallback raw,
+                CameraPictureCallback postview,
+                CameraPictureCallback jpeg);
+
+        /**
+         * Sets the display orientation for camera to adjust the preview orientation.
+         *
+         * @param degrees The rotation in degrees. Should be 0, 90, 180 or 270.
+         */
+        public void setDisplayOrientation(int degrees);
+
+        /**
+         * Sets the listener for zoom change.
+         *
+         * @param listener The listener.
+         */
+        public void setZoomChangeListener(OnZoomChangeListener listener);
+
+        /**
+         * Sets the face detection listener.
+         *
+         * @param handler  The handler in which the callback will be invoked.
+         * @param callback The callback for face detection results.
+         */
+        public void setFaceDetectionCallback(Handler handler, CameraFaceDetectionCallback callback);
+
+        /**
+         * Starts the face detection.
+         */
+        public void startFaceDetection();
+
+        /**
+         * Stops the face detection.
+         */
+        public void stopFaceDetection();
+
+        /**
+         * Registers an error callback.
+         *
+         * @param handler  The handler on which the callback will be invoked.
+         * @param cb The error callback.
+         * @see android.hardware.Camera#setErrorCallback(android.hardware.Camera.ErrorCallback)
+         */
+        public void setErrorCallback(Handler handler, CameraErrorCallback cb);
+
+        /**
+         * Sets the camera parameters.
+         *
+         * @param params The camera parameters to use.
+         */
+        @Deprecated
+        public void setParameters(Camera.Parameters params);
+
+        /**
+         * Gets the current camera parameters synchronously. This method is
+         * synchronous since the caller has to wait for the camera to return
+         * the parameters. If the parameters are already cached, it returns
+         * immediately.
+         */
+        @Deprecated
+        public Camera.Parameters getParameters();
+
+        /**
+         * Gets the current camera settings synchronously.
+         * <p>This method is synchronous since the caller has to wait for the
+         * camera to return the parameters. If the parameters are already
+         * cached, it returns immediately.</p>
+         */
+        public CameraSettings getSettings();
+
+        /**
+         * Applies the settings to the camera device.
+         *
+         * @param settings The settings to use on the device.
+         * @return Whether the settings can be applied.
+         */
+        public boolean applySettings(CameraSettings settings);
+
+        /**
+         * Forces {@code CameraProxy} to update the cached version of the camera
+         * settings regardless of the dirty bit.
+         */
+        public void refreshSettings();
+
+        /**
+         * Enables/Disables the camera shutter sound.
+         *
+         * @param enable   {@code true} to enable the shutter sound,
+         *                 {@code false} to disable it.
+         */
+        public void enableShutterSound(boolean enable);
+
+        /**
+         * Dumps the current settings of the camera device.
+         *
+         * <p>The content varies based on the underlying camera API settings
+         * implementation.</p>
+         *
+         * @return The content of the device settings represented by a string.
+         */
+        public String dumpDeviceSettings();
+    }
+}
diff --git a/camera2/portability/src/com/android/ex/camera2/portability/CameraAgentFactory.java b/camera2/portability/src/com/android/ex/camera2/portability/CameraAgentFactory.java
new file mode 100644
index 0000000..ce8379d
--- /dev/null
+++ b/camera2/portability/src/com/android/ex/camera2/portability/CameraAgentFactory.java
@@ -0,0 +1,52 @@
+/*
+ * 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.android.ex.camera2.portability;
+
+/**
+ * A factory class for {@link CameraAgent}.
+ */
+public class CameraAgentFactory {
+
+    private static AndroidCameraAgentImpl sAndroidCameraAgent;
+    private static int sAndroidCameraAgentClientCount;
+
+    /**
+     * Returns the android camera implementation of {@link com.android.camera.cameradevice.CameraAgent}.
+     *
+     * @return The {@link CameraAgent} to control the camera device.
+     */
+    public static synchronized CameraAgent getAndroidCameraAgent() {
+        if (sAndroidCameraAgent == null) {
+            sAndroidCameraAgent = new AndroidCameraAgentImpl();
+            sAndroidCameraAgentClientCount = 1;
+        } else {
+            ++sAndroidCameraAgentClientCount;
+        }
+        return sAndroidCameraAgent;
+    }
+
+    /**
+     * Recycles the resources. Always call this method when the activity is
+     * stopped.
+     */
+    public static synchronized void recycle() {
+        if (--sAndroidCameraAgentClientCount == 0 && sAndroidCameraAgent != null) {
+            sAndroidCameraAgent.recycle();
+            sAndroidCameraAgent = null;
+        }
+    }
+}
diff --git a/camera2/portability/src/com/android/ex/camera2/portability/CameraCapabilities.java b/camera2/portability/src/com/android/ex/camera2/portability/CameraCapabilities.java
new file mode 100644
index 0000000..f08301c
--- /dev/null
+++ b/camera2/portability/src/com/android/ex/camera2/portability/CameraCapabilities.java
@@ -0,0 +1,692 @@
+/*
+ * Copyright (C) 2014 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.android.ex.camera2.portability;
+
+import com.android.ex.camera2.portability.debug.Log;
+
+import java.util.ArrayList;
+import java.util.EnumSet;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.TreeSet;
+
+/**
+ * This class holds all the static information of a camera's capabilities.
+ * <p>
+ * The design of this class is thread-safe and can be passed around regardless
+ * of which thread using it.
+ * </p>
+ */
+public class CameraCapabilities {
+
+    private static Log.Tag TAG = new Log.Tag("CamCapabilities");
+
+    /* All internal states are declared final and should be thread-safe. */
+
+    protected final ArrayList<int[]> mSupportedPreviewFpsRange = new ArrayList<int[]>();
+    protected final ArrayList<Size> mSupportedPreviewSizes = new ArrayList<Size>();
+    protected final TreeSet<Integer> mSupportedPreviewFormats = new TreeSet<Integer>();
+    protected final ArrayList<Size> mSupportedVideoSizes = new ArrayList<Size>();
+    protected final ArrayList<Size> mSupportedPhotoSizes = new ArrayList<Size>();
+    protected final TreeSet<Integer> mSupportedPhotoFormats = new TreeSet<Integer>();
+    protected final EnumSet<SceneMode> mSupportedSceneModes = EnumSet.noneOf(SceneMode.class);
+    protected final EnumSet<FlashMode> mSupportedFlashModes = EnumSet.noneOf(FlashMode.class);
+    protected final EnumSet<FocusMode> mSupportedFocusModes = EnumSet.noneOf(FocusMode.class);
+    protected final EnumSet<WhiteBalance> mSupportedWhiteBalances =
+            EnumSet.noneOf(WhiteBalance.class);
+    protected final EnumSet<Feature> mSupportedFeatures = EnumSet.noneOf(Feature.class);
+    protected Size mPreferredPreviewSizeForVideo;
+    protected int mMinExposureCompensation;
+    protected int mMaxExposureCompensation;
+    protected float mExposureCompensationStep;
+    protected int mMaxNumOfFacesSupported;
+    protected int mMaxNumOfFocusAreas;
+    protected int mMaxNumOfMeteringArea;
+    protected int mMaxZoomRatio;
+    protected float mHorizontalViewAngle;
+    protected float mVerticalViewAngle;
+    private final Stringifier mStringifier;
+    protected final ArrayList<Integer> mZoomRatioList = new ArrayList<Integer>();
+    protected int mMaxZoomIndex;
+
+    /**
+     * Focus modes.
+     */
+    public enum FocusMode {
+        /**
+         * Continuous auto focus mode intended for taking pictures.
+         * @see {@link android.hardware.Camera.Parameters#FOCUS_MODE_AUTO}.
+         */
+        AUTO,
+        /**
+         * Continuous auto focus mode intended for taking pictures.
+         * @see {@link android.hardware.Camera.Parameters#FOCUS_MODE_CONTINUOUS_PICTURE}.
+         */
+        CONTINUOUS_PICTURE,
+        /**
+         * Continuous auto focus mode intended for video recording.
+         * @see {@link android.hardware.Camera.Parameters#FOCUS_MODE_CONTINUOUS_VIDEO}.
+         */
+        CONTINUOUS_VIDEO,
+        /**
+         * Extended depth of field (EDOF).
+         * @see {@link android.hardware.Camera.Parameters#FOCUS_MODE_EDOF}.
+         */
+        EXTENDED_DOF,
+        /**
+         * Focus is fixed.
+         * @see {@link android.hardware.Camera.Parameters#FOCUS_MODE_FIXED}.
+         */
+        FIXED,
+        /**
+         * Focus is set at infinity.
+         * @see {@link android.hardware.Camera.Parameters#FOCUS_MODE_INFINITY}.
+         */
+        INFINITY,
+        /**
+         * Macro (close-up) focus mode.
+         * @see {@link android.hardware.Camera.Parameters#FOCUS_MODE_MACRO}.
+         */
+        MACRO,
+    }
+
+    /**
+     * Flash modes.
+     */
+    public enum FlashMode {
+        /**
+         * No flash.
+         */
+        NO_FLASH,
+        /**
+         * Flash will be fired automatically when required.
+         * @see {@link android.hardware.Camera.Parameters#FLASH_MODE_OFF}.
+         */
+        AUTO,
+        /**
+         * Flash will not be fired.
+         * @see {@link android.hardware.Camera.Parameters#FLASH_MODE_OFF}.
+         */
+        OFF,
+        /**
+         * Flash will always be fired during snapshot.
+         * @see {@link android.hardware.Camera.Parameters#FLASH_MODE_ON}.
+         */
+        ON,
+        /**
+         * Constant emission of light during preview, auto-focus and snapshot.
+         * @see {@link android.hardware.Camera.Parameters#FLASH_MODE_TORCH}.
+         */
+        TORCH,
+        /**
+         * Flash will be fired in red-eye reduction mode.
+         * @see {@link android.hardware.Camera.Parameters#FLASH_MODE_RED_EYE}.
+         */
+        RED_EYE,
+    }
+
+    /**
+     * Scene modes.
+     */
+    public enum SceneMode {
+        /**
+         * No supported scene mode.
+         */
+        NO_SCENE_MODE,
+        /**
+         * Scene mode is off.
+         * @see {@link android.hardware.Camera.Parameters#SCENE_MODE_AUTO}.
+         */
+        AUTO,
+        /**
+         * Take photos of fast moving objects.
+         * @see {@link android.hardware.Camera.Parameters#SCENE_MODE_ACTION}.
+         */
+        ACTION,
+        /**
+         * Applications are looking for a barcode.
+         * @see {@link android.hardware.Camera.Parameters#SCENE_MODE_BARCODE}.
+         */
+        BARCODE,
+        /**
+         * Take pictures on the beach.
+         * @see {@link android.hardware.Camera.Parameters#SCENE_MODE_BEACH}.
+         */
+        BEACH,
+        /**
+         * Capture the naturally warm color of scenes lit by candles.
+         * @see {@link android.hardware.Camera.Parameters#SCENE_MODE_CANDLELIGHT}.
+         */
+        CANDLELIGHT,
+        /**
+         * For shooting firework displays.
+         * @see {@link android.hardware.Camera.Parameters#SCENE_MODE_FIREWORKS}.
+         */
+        FIREWORKS,
+        /**
+         * Capture a scene using high dynamic range imaging techniques.
+         * @see {@link android.hardware.Camera.Parameters#SCENE_MODE_HDR}.
+         */
+        HDR,
+        /**
+         * Take pictures on distant objects.
+         * @see {@link android.hardware.Camera.Parameters#SCENE_MODE_LANDSCAPE}.
+         */
+        LANDSCAPE,
+        /**
+         * Take photos at night.
+         * @see {@link android.hardware.Camera.Parameters#SCENE_MODE_NIGHT}.
+         */
+        NIGHT,
+        /**
+         * Take people pictures at night.
+         * @see {@link android.hardware.Camera.Parameters#SCENE_MODE_NIGHT_PORTRAIT}.
+         */
+        NIGHT_PORTRAIT,
+        /**
+         * Take indoor low-light shot.
+         * @see {@link android.hardware.Camera.Parameters#SCENE_MODE_PARTY}.
+         */
+        PARTY,
+        /**
+         * Take people pictures.
+         * @see {@link android.hardware.Camera.Parameters#SCENE_MODE_PORTRAIT}.
+         */
+        PORTRAIT,
+        /**
+         * Take pictures on the snow.
+         * @see {@link android.hardware.Camera.Parameters#SCENE_MODE_SNOW}.
+         */
+        SNOW,
+        /**
+         * Take photos of fast moving objects.
+         * @see {@link android.hardware.Camera.Parameters#SCENE_MODE_SPORTS}.
+         */
+        SPORTS,
+        /**
+         * Avoid blurry pictures (for example, due to hand shake).
+         * @see {@link android.hardware.Camera.Parameters#SCENE_MODE_STEADYPHOTO}.
+         */
+        STEADYPHOTO,
+        /**
+         * Take sunset photos.
+         * @see {@link android.hardware.Camera.Parameters#SCENE_MODE_SUNSET}.
+         */
+        SUNSET,
+        /**
+         * Take photos in a theater.
+         * @see {@link android.hardware.Camera.Parameters#SCENE_MODE_THEATRE}.
+         */
+        THEATRE,
+    }
+
+    /**
+     * White blances.
+     */
+    public enum WhiteBalance {
+        /**
+         * @see {@link android.hardware.Camera.Parameters#WHITE_BALANCE_AUTO}.
+         */
+        AUTO,
+        /**
+         * @see {@link android.hardware.Camera.Parameters#WHITE_BALANCE_CLOUDY_DAYLIGHT}.
+         */
+        CLOUDY_DAYLIGHT,
+        /**
+         * @see {@link android.hardware.Camera.Parameters#WHITE_BALANCE_DAYLIGHT}.
+         */
+        DAYLIGHT,
+        /**
+         * @see {@link android.hardware.Camera.Parameters#WHITE_BALANCE_FLUORESCENT}.
+         */
+        FLUORESCENT,
+        /**
+         * @see {@link android.hardware.Camera.Parameters#WHITE_BALANCE_INCANDESCENT}.
+         */
+        INCANDESCENT,
+        /**
+         * @see {@link android.hardware.Camera.Parameters#WHITE_BALANCE_SHADE}.
+         */
+        SHADE,
+        /**
+         * @see {@link android.hardware.Camera.Parameters#WHITE_BALANCE_TWILIGHT}.
+         */
+        TWILIGHT,
+        /**
+         * @see {@link android.hardware.Camera.Parameters#WHITE_BALANCE_WARM_FLUORESCENT}.
+         */
+        WARM_FLUORESCENT,
+    }
+
+    /**
+     * Features.
+     */
+    public enum Feature {
+        /**
+         * Support zoom-related methods.
+         */
+        ZOOM,
+        /**
+         * Support for photo capturing during video recording.
+         */
+        VIDEO_SNAPSHOT,
+        /**
+         * Support for focus area settings.
+         */
+        FOCUS_AREA,
+        /**
+         * Support for metering area settings.
+         */
+        METERING_AREA,
+        /**
+         * Support for automatic exposure lock.
+         */
+        AUTO_EXPOSURE_LOCK,
+        /**
+         * Support for automatic white balance lock.
+         */
+        AUTO_WHITE_BALANCE_LOCK,
+        /**
+         * Support for video stabilization.
+         */
+        VIDEO_STABILIZATION,
+    }
+
+    /**
+     * A interface stringifier to convert abstract representations to API
+     * related string representation.
+     */
+    public interface Stringifier {
+        /**
+         * Converts the focus mode to API-related string representation.
+         *
+         * @param focus The focus mode to convert.
+         * @return The string used by the camera framework API to represent the
+         * focus mode.
+         */
+        String stringify(FocusMode focus);
+
+        /**
+         * Converts the API-related string representation of the focus mode to the
+         * abstract representation.
+         *
+         * @param val The string representation.
+         * @return The focus mode represented by the input string.
+         */
+        FocusMode focusModeFromString(String val);
+
+        /**
+         * Converts the flash mode to API-related string representation.
+         *
+         * @param flash The focus mode to convert.
+         * @return The string used by the camera framework API to represent the
+         * flash mode.
+         */
+        String stringify(FlashMode flash);
+
+        /**
+         * Converts the API-related string representation of the flash mode to the
+         * abstract representation.
+         *
+         * @param val The string representation.
+         * @return The flash mode represented by the input string. Can be
+         *         {@code null}.
+         */
+        FlashMode flashModeFromString(String val);
+
+        /**
+         * Converts the scene mode to API-related string representation.
+         *
+         * @param scene The focus mode to convert.
+         * @return The string used by the camera framework API to represent the
+         * scene mode.
+         */
+        String stringify(SceneMode scene);
+
+        /**
+         * Converts the API-related string representation of the scene mode to the
+         * abstract representation.
+         *
+         * @param val The string representation.
+         * @return The scene mode represented by the input string.
+         */
+        SceneMode sceneModeFromString(String val);
+
+        /**
+         * Converts the white balance to API-related string representation.
+         *
+         * @param wb The focus mode to convert.
+         * @return The string used by the camera framework API to represent the
+         * white balance.
+         */
+        String stringify(WhiteBalance wb);
+
+        /**
+         * Converts the API-related string representation of the white balance to
+         * the abstract representation.
+         *
+         * @param val The string representation.
+         * @return The white balance represented by the input string.
+         */
+        WhiteBalance whiteBalanceFromString(String val);
+    }
+
+    /**
+     * Constructor.
+     * @param stringifier The API-specific stringifier for this instance.
+     */
+    CameraCapabilities(Stringifier stringifier) {
+        mStringifier = stringifier;
+    }
+
+    /**
+     * Copy constructor.
+     * @param src The source instance.
+     */
+    public CameraCapabilities(CameraCapabilities src) {
+        mSupportedPreviewFpsRange.addAll(src.mSupportedPreviewFpsRange);
+        mSupportedPreviewSizes.addAll(src.mSupportedPreviewSizes);
+        mSupportedPreviewFormats.addAll(src.mSupportedPreviewFormats);
+        mSupportedVideoSizes.addAll(src.mSupportedVideoSizes);
+        mSupportedPhotoSizes.addAll(src.mSupportedPhotoSizes);
+        mSupportedPhotoFormats.addAll(src.mSupportedPhotoFormats);
+        mSupportedSceneModes.addAll(src.mSupportedSceneModes);
+        mSupportedFlashModes.addAll(src.mSupportedFlashModes);
+        mSupportedFocusModes.addAll(src.mSupportedFocusModes);
+        mSupportedWhiteBalances.addAll(src.mSupportedWhiteBalances);
+        mSupportedFeatures.addAll(src.mSupportedFeatures);
+        mPreferredPreviewSizeForVideo = src.mPreferredPreviewSizeForVideo;
+        mMaxExposureCompensation = src.mMaxExposureCompensation;
+        mMinExposureCompensation = src.mMinExposureCompensation;
+        mExposureCompensationStep = src.mExposureCompensationStep;
+        mMaxNumOfFacesSupported = src.mMaxNumOfFacesSupported;
+        mMaxNumOfFocusAreas = src.mMaxNumOfFocusAreas;
+        mMaxNumOfMeteringArea = src.mMaxNumOfMeteringArea;
+        mMaxZoomRatio = src.mMaxZoomRatio;
+        mHorizontalViewAngle = src.mHorizontalViewAngle;
+        mVerticalViewAngle = src.mVerticalViewAngle;
+        mStringifier = src.mStringifier;
+    }
+
+    public float getHorizontalViewAngle() {
+        return mHorizontalViewAngle;
+    }
+
+    public float getVerticalViewAngle() {
+        return mVerticalViewAngle;
+    }
+
+    /**
+     * @return the supported picture formats. See {@link android.graphics.ImageFormat}.
+     */
+    public Set<Integer> getSupportedPhotoFormats() {
+        return new TreeSet<Integer>(mSupportedPhotoFormats);
+    }
+
+    /**
+     * Gets the supported preview formats.
+     * @return The supported preview {@link android.graphics.ImageFormat}s.
+     */
+    public Set<Integer> getSupportedPreviewFormats() {
+        return new TreeSet<Integer>(mSupportedPreviewFormats);
+    }
+
+    /**
+     * Gets the supported picture sizes.
+     */
+    public List<Size> getSupportedPhotoSizes() {
+        return new ArrayList<Size>(mSupportedPhotoSizes);
+    }
+
+    /**
+     * @return The supported preview fps (frame-per-second) ranges. The returned
+     * list is sorted by maximum fps then minimum fps in a descending order.
+     * The values are multiplied by 1000.
+     */
+    public final List<int[]> getSupportedPreviewFpsRange() {
+        return new ArrayList<int[]>(mSupportedPreviewFpsRange);
+    }
+
+    /**
+     * @return The supported preview sizes. The list is sorted by width then
+     * height in a descending order.
+     */
+    public final List<Size> getSupportedPreviewSizes() {
+        return new ArrayList<Size>(mSupportedPreviewSizes);
+    }
+
+    public final Size getPreferredPreviewSizeForVideo() {
+        return new Size(mPreferredPreviewSizeForVideo);
+    }
+
+    /**
+     * @return The supported video frame sizes that can be used by MediaRecorder.
+     *         The list is sorted by width then height in a descending order.
+     */
+    public final List<Size> getSupportedVideoSizes() {
+        return new ArrayList<Size>(mSupportedVideoSizes);
+    }
+
+    /**
+     * @return The supported scene modes.
+     */
+    public final Set<SceneMode> getSupportedSceneModes() {
+        return new HashSet<SceneMode>(mSupportedSceneModes);
+    }
+
+    /**
+     * @return Whether the scene mode is supported.
+     */
+    public final boolean supports(SceneMode scene) {
+        return (scene != null && mSupportedSceneModes.contains(scene));
+    }
+
+    public boolean supports(final CameraSettings settings) {
+        if (zoomCheck(settings) && exposureCheck(settings) && focusCheck(settings) &&
+                flashCheck(settings) && photoSizeCheck(settings) && previewSizeCheck(settings) &&
+                videoStabilizationCheck(settings)) {
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * @return The supported flash modes.
+     */
+    public final Set<FlashMode> getSupportedFlashModes() {
+        return new HashSet<FlashMode>(mSupportedFlashModes);
+    }
+
+    /**
+     * @return Whether the flash mode is supported.
+     */
+    public final boolean supports(FlashMode flash) {
+        return (flash != null && mSupportedFlashModes.contains(flash));
+    }
+
+    /**
+     * @return The supported focus modes.
+     */
+    public final Set<FocusMode> getSupportedFocusModes() {
+        return new HashSet<FocusMode>(mSupportedFocusModes);
+    }
+
+    /**
+     * @return Whether the focus mode is supported.
+     */
+    public final boolean supports(FocusMode focus) {
+        return (focus != null && mSupportedFocusModes.contains(focus));
+    }
+
+    /**
+     * @return The supported white balanceas.
+     */
+    public final Set<WhiteBalance> getSupportedWhiteBalance() {
+        return new HashSet<WhiteBalance>(mSupportedWhiteBalances);
+    }
+
+    /**
+     * @return Whether the white balance is supported.
+     */
+    public boolean supports(WhiteBalance wb) {
+        return (wb != null && mSupportedWhiteBalances.contains(wb));
+    }
+
+    public final Set<Feature> getSupportedFeature() {
+        return new HashSet<Feature>(mSupportedFeatures);
+    }
+
+    public boolean supports(Feature ft) {
+        return (ft != null && mSupportedFeatures.contains(ft));
+    }
+
+    /**
+     * @return The maximal supported zoom ratio.
+     */
+    public float getMaxZoomRatio() {
+        return mMaxZoomRatio;
+    }
+
+    // We'll replace these old style methods with new ones.
+    @Deprecated
+    public int getMaxZoomIndex() {
+        return mMaxZoomIndex;
+    }
+
+    @Deprecated
+    public List<Integer> getZoomRatioList() {
+        return new ArrayList<Integer>(mZoomRatioList);
+    }
+
+    /**
+     * @return The min exposure compensation index. The EV is the compensation
+     * index multiplied by the step value. If unsupported, both this method and
+     * {@link #getMaxExposureCompensation()} return 0.
+     */
+    public final int getMinExposureCompensation() {
+        return mMinExposureCompensation;
+    }
+
+    /**
+     * @return The max exposure compensation index. The EV is the compensation
+     * index multiplied by the step value. If unsupported, both this method and
+     * {@link #getMinExposureCompensation()} return 0.
+     */
+    public final int getMaxExposureCompensation() {
+        return mMaxExposureCompensation;
+    }
+
+    /**
+     * @return The exposure compensation step. The EV is the compensation index
+     * multiplied by the step value.
+     */
+    public final float getExposureCompensationStep() {
+        return mExposureCompensationStep;
+    }
+
+    /**
+     * @return The max number of faces supported by the face detection. 0 if
+     * unsupported.
+     */
+    public final int getMaxNumOfFacesSupported() {
+        return mMaxNumOfFacesSupported;
+    }
+
+    /**
+     * @return The stringifier used by this instance.
+     */
+    public Stringifier getStringifier() {
+        return mStringifier;
+    }
+
+    private boolean zoomCheck(final CameraSettings settings) {
+        final float ratio = settings.getCurrentZoomRatio();
+        final int index = settings.getCurrentZoomIndex();
+        if (!supports(Feature.ZOOM)) {
+            if (ratio != 1.0f || index != 0) {
+                Log.v(TAG, "Zoom is not supported");
+                return false;
+            }
+        } else {
+            if (settings.getCurrentZoomRatio() > getMaxZoomRatio() ||
+                    index > getMaxZoomIndex()) {
+                Log.v(TAG, "Zoom ratio is not supported: ratio = " +
+                        settings.getCurrentZoomRatio() + ", index = " + index);
+                return false;
+            }
+        }
+        return true;
+    }
+
+    private boolean exposureCheck(final CameraSettings settings) {
+        final int index = settings.getExposureCompensationIndex();
+        if (index > getMaxExposureCompensation() || index < getMinExposureCompensation()) {
+            Log.v(TAG, "Exposure compensation index is not supported. Min = " +
+                    getMinExposureCompensation() + ", max = " + getMaxExposureCompensation() + "," +
+                    " setting = " + index);
+            return false;
+        }
+        return true;
+    }
+
+    private boolean focusCheck(final CameraSettings settings) {
+        FocusMode focusMode = settings.getCurrentFocusMode();
+        if (!supports(focusMode)) {
+            Log.v(TAG,
+                    "Focus mode not supported:" + (focusMode != null ? focusMode.name() : "null"));
+            return false;
+        }
+        return true;
+    }
+
+    private boolean flashCheck(final CameraSettings settings) {
+        FlashMode flashMode = settings.getCurrentFlashMode();
+        if (!supports(flashMode)) {
+            Log.v(TAG,
+                    "Flash mode not supported:" + (flashMode != null ? flashMode.name() : "null"));
+            return false;
+        }
+        return true;
+    }
+
+    private boolean photoSizeCheck(final CameraSettings settings) {
+        Size photoSize = settings.getCurrentPhotoSize();
+        if (mSupportedPhotoSizes.contains(photoSize)) {
+            return true;
+        }
+        Log.v(TAG, "Unsupported photo size:" + photoSize);
+        return false;
+    }
+
+    private boolean previewSizeCheck(final CameraSettings settings) {
+        final Size previewSize = settings.getCurrentPreviewSize();
+        if (mSupportedPreviewSizes.contains(previewSize)) {
+            return true;
+        }
+        Log.v(TAG, "Unsupported preview size:" + previewSize);
+        return false;
+    }
+
+    private boolean videoStabilizationCheck(final CameraSettings settings) {
+        if (!settings.isVideoStabilizationEnabled() || supports(Feature.VIDEO_STABILIZATION)) {
+            return true;
+        }
+        Log.v(TAG, "Video stabilization is not supported");
+        return false;
+    }
+}
diff --git a/camera2/portability/src/com/android/ex/camera2/portability/CameraCapabilitiesFactory.java b/camera2/portability/src/com/android/ex/camera2/portability/CameraCapabilitiesFactory.java
new file mode 100644
index 0000000..80765d3
--- /dev/null
+++ b/camera2/portability/src/com/android/ex/camera2/portability/CameraCapabilitiesFactory.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2014 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.android.ex.camera2.portability;
+
+import android.hardware.Camera;
+
+import com.android.ex.camera2.portability.debug.Log;
+
+public class CameraCapabilitiesFactory {
+
+    private static Log.Tag TAG = new Log.Tag("CapabilitiesFactory");
+
+    public static CameraCapabilities createFrom(Camera.Parameters p) {
+        if (p == null) {
+            Log.w(TAG, "Null parameter passed in.");
+            return null;
+        }
+        return new AndroidCameraCapabilities(p);
+    }
+}
diff --git a/camera2/portability/src/com/android/ex/camera2/portability/CameraDeviceInfo.java b/camera2/portability/src/com/android/ex/camera2/portability/CameraDeviceInfo.java
new file mode 100644
index 0000000..60ad8ed
--- /dev/null
+++ b/camera2/portability/src/com/android/ex/camera2/portability/CameraDeviceInfo.java
@@ -0,0 +1,35 @@
+package com.android.ex.camera2.portability;
+
+import android.hardware.Camera;
+
+/**
+ * The camera device info.
+ */
+public interface CameraDeviceInfo {
+
+    static final int NO_DEVICE = -1;
+
+    /**
+     * @return The camera info.
+     * // TODO: Remove the dependency on API 1.
+     */
+    @Deprecated
+    Camera.CameraInfo[] getCameraInfos();
+
+    /**
+     * @return The total number of the available camera devices.
+     */
+    int getNumberOfCameras();
+
+    /**
+     * @return The first (lowest) ID of the back cameras or {@code NO_DEVICE}
+     *         if not available.
+     */
+    int getFirstBackCameraId();
+
+    /**
+     * @return The first (lowest) ID of the front cameras or {@code NO_DEVICE}
+     *         if not available.
+     */
+    int getFirstFrontCameraId();
+}
diff --git a/camera2/portability/src/com/android/ex/camera2/portability/CameraSettings.java b/camera2/portability/src/com/android/ex/camera2/portability/CameraSettings.java
new file mode 100644
index 0000000..33ceb5c
--- /dev/null
+++ b/camera2/portability/src/com/android/ex/camera2/portability/CameraSettings.java
@@ -0,0 +1,447 @@
+package com.android.ex.camera2.portability;
+
+import android.hardware.Camera;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.TreeMap;
+
+/**
+ * A class which stores the camera settings.
+ */
+public class CameraSettings {
+
+    protected final Map<String, String> mGeneralSetting = new TreeMap<>();
+    protected final List<Camera.Area> mMeteringAreas = new ArrayList<>();
+    protected final List<Camera.Area> mFocusAreas = new ArrayList<>();
+    protected int mPreviewFpsRangeMin;
+    protected int mPreviewFpsRangeMax;
+    protected int mPreviewFrameRate;
+    protected Size mCurrentPreviewSize;
+    private int mCurrentPreviewFormat;
+    protected Size mCurrentPhotoSize;
+    protected int mJpegCompressQuality;
+    protected int mCurrentPhotoFormat;
+    protected float mCurrentZoomRatio;
+    protected int mCurrentZoomIndex;
+    protected float mPhotoRotationDegrees;
+    protected int mExposureCompensationIndex;
+    protected CameraCapabilities.FlashMode mCurrentFlashMode;
+    protected CameraCapabilities.FocusMode mCurrentFocusMode;
+    protected CameraCapabilities.SceneMode mCurrentSceneMode;
+    protected CameraCapabilities.WhiteBalance mWhiteBalance;
+    protected boolean mVideoStabilizationEnabled;
+    protected boolean mAutoExposureLocked;
+    protected boolean mAutoWhiteBalanceLocked;
+    protected boolean mRecordingHintEnabled;
+    protected GpsData mGpsData;
+    protected Size mExifThumbnailSize = new Size(0,0);
+
+    /**
+     * An immutable class storing GPS related information.
+     * <p>It's a hack since we always use GPS time stamp but does not use other
+     * fields sometimes. Setting processing method to null means the other
+     * fields should not be used.</p>
+     */
+    public static class GpsData {
+        public final double latitude;
+        public final double longitude;
+        public final double altitude;
+        public final long timeStamp;
+        public final String processingMethod;
+
+        /** Constructor. */
+        public GpsData(double latitude, double longitude, double altitude, long timeStamp,
+                String processingMethod) {
+            this.latitude = latitude;
+            this.longitude = longitude;
+            this.altitude = altitude;
+            this.timeStamp = timeStamp;
+            this.processingMethod = processingMethod;
+        }
+
+        /** Copy constructor. */
+        public GpsData(GpsData src) {
+            this.latitude = src.latitude;
+            this.longitude = src.longitude;
+            this.altitude = src.altitude;
+            this.timeStamp = src.timeStamp;
+            this.processingMethod = src.processingMethod;
+        }
+    }
+
+    protected CameraSettings() {
+    }
+
+    /**
+     * Copy constructor.
+     *
+     * @param src The source settings.
+     * @return The copy of the source.
+     */
+    public CameraSettings(CameraSettings src) {
+        mGeneralSetting.putAll(src.mGeneralSetting);
+        mMeteringAreas.addAll(src.mMeteringAreas);
+        mFocusAreas.addAll(src.mFocusAreas);
+        mPreviewFpsRangeMin = src.mPreviewFpsRangeMin;
+        mPreviewFpsRangeMax = src.mPreviewFpsRangeMax;
+        mPreviewFrameRate = src.mPreviewFrameRate;
+        mCurrentPreviewSize =
+                (src.mCurrentPreviewSize == null ? null : new Size(src.mCurrentPreviewSize));
+        mCurrentPreviewFormat = src.mCurrentPreviewFormat;
+        mCurrentPhotoSize =
+                (src.mCurrentPhotoSize == null ? null : new Size(src.mCurrentPhotoSize));
+        mJpegCompressQuality = src.mJpegCompressQuality;
+        mCurrentPhotoFormat = src.mCurrentPhotoFormat;
+        mCurrentZoomRatio = src.mCurrentZoomRatio;
+        mCurrentZoomIndex = src.mCurrentZoomIndex;
+        mPhotoRotationDegrees = src.mPhotoRotationDegrees;
+        mExposureCompensationIndex = src.mExposureCompensationIndex;
+        mCurrentFlashMode = src.mCurrentFlashMode;
+        mCurrentFocusMode = src.mCurrentFocusMode;
+        mCurrentSceneMode = src.mCurrentSceneMode;
+        mWhiteBalance = src.mWhiteBalance;
+        mVideoStabilizationEnabled = src.mVideoStabilizationEnabled;
+        mAutoExposureLocked = src.mAutoExposureLocked;
+        mAutoWhiteBalanceLocked = src.mAutoWhiteBalanceLocked;
+        mRecordingHintEnabled = src.mRecordingHintEnabled;
+        mGpsData = src.mGpsData;
+        mExifThumbnailSize = src.mExifThumbnailSize;
+    }
+
+    /** General setting **/
+    @Deprecated
+    public void setSetting(String key, String value) {
+        mGeneralSetting.put(key, value);
+    }
+
+    /**  Preview **/
+
+    /**
+     * Sets the preview FPS range. This call will invalidate prior calls to
+     * {@link #setPreviewFrameRate(int)}.
+     *
+     * @param min The min FPS.
+     * @param max The max FPS.
+     */
+    public void setPreviewFpsRange(int min, int max) {
+        if (min > max) {
+            int temp = max;
+            max = min;
+            min = temp;
+        }
+        mPreviewFpsRangeMax = max;
+        mPreviewFpsRangeMin = min;
+        mPreviewFrameRate = -1;
+    }
+
+    /**
+     * @return The min of the preview FPS range.
+     */
+    public int getPreviewFpsRangeMin() {
+        return mPreviewFpsRangeMin;
+    }
+
+    /**
+     * @return The max of the preview FPS range.
+     */
+    public int getPreviewFpsRangeMax() {
+        return mPreviewFpsRangeMax;
+    }
+
+    /**
+     * Sets the preview FPS. This call will invalidate prior calls to
+     * {@link #setPreviewFpsRange(int, int)}.
+     *
+     * @param frameRate The target frame rate.
+     */
+    public void setPreviewFrameRate(int frameRate) {
+        if (frameRate > 0) {
+            mPreviewFrameRate = frameRate;
+            mPreviewFpsRangeMax = frameRate;
+            mPreviewFpsRangeMin = frameRate;
+        }
+    }
+
+    public int getPreviewFrameRate() {
+        return mPreviewFrameRate;
+    }
+
+    /**
+     * @return The current preview size.
+     */
+    public Size getCurrentPreviewSize() {
+        return new Size(mCurrentPreviewSize);
+    }
+
+    /**
+     * @param previewSize The size to use for preview.
+     */
+    public void setPreviewSize(Size previewSize) {
+        mCurrentPreviewSize = new Size(previewSize);
+    }
+
+    /**
+     * Sets the preview format.
+     *
+     * @param format
+     * @see {@link android.graphics.ImageFormat}.
+     */
+    public void setPreviewFormat(int format) {
+        mCurrentPreviewFormat = format;
+    }
+
+    /**
+     * @return The preview format.
+     * @see {@link android.graphics.ImageFormat}.
+     */
+    public int getCurrentPreviewFormat() {
+        return mCurrentPreviewFormat;
+    }
+
+    /** Picture **/
+
+    /**
+     * @return The current photo size.
+     */
+    public Size getCurrentPhotoSize() {
+        return new Size(mCurrentPhotoSize);
+    }
+
+    /**
+     * Sets the size for the photo.
+     *
+     * @param photoSize The photo size.
+     */
+    public void setPhotoSize(Size photoSize) {
+        mCurrentPhotoSize = new Size(photoSize);
+    }
+
+    /**
+     * Sets the format for the photo.
+     *
+     * @param format The format for the photos taken.
+     * @see {@link android.graphics.ImageFormat}.
+     */
+    public void setPhotoFormat(int format) {
+        mCurrentPhotoFormat = format;
+    }
+
+    /**
+     * @return The format for the photos taken.
+     * @see {@link android.graphics.ImageFormat}.
+     */
+    public int getCurrentPhotoFormat() {
+        return mCurrentPhotoFormat;
+    }
+
+    /**
+     * Sets the JPEG compression quality.
+     *
+     * @param quality The quality for JPEG.
+     */
+    public void setPhotoJpegCompressionQuality(int quality) {
+        mJpegCompressQuality = quality;
+    }
+
+    public int getPhotoJpegCompressionQuality() {
+        return mJpegCompressQuality;
+    }
+
+    /** Zoom **/
+
+    /**
+     * @return The current zoom ratio. The min is 1.0f.
+     */
+    public float getCurrentZoomRatio() {
+        return mCurrentZoomRatio;
+    }
+
+    /**
+     * Sets the zoom ratio.
+     * @param ratio The new zoom ratio. Should be in the range between 1.0 to
+     *              the value returned from {@link
+     *              com.android.camera.cameradevice.CameraCapabilities#getMaxZoomRatio()}.
+     * @throws java.lang.UnsupportedOperationException if the ratio is not
+     *         supported.
+     */
+    public void setZoomRatio(float ratio) {
+        mCurrentZoomRatio = ratio;
+    }
+
+    @Deprecated
+    public int getCurrentZoomIndex() {
+        return mCurrentZoomIndex;
+    }
+
+    @Deprecated
+    public void setZoomIndex(int index) {
+        mCurrentZoomIndex = index;
+    }
+
+    /** Transformation **/
+
+    public void setPhotoRotationDegrees(float photoRotationDegrees) {
+        mPhotoRotationDegrees = photoRotationDegrees;
+    }
+
+    public float getCurrentPhotoRotationDegrees() {
+        return mPhotoRotationDegrees;
+    }
+
+    /** Exposure **/
+
+    public void setExposureCompensationIndex(int index) {
+        mExposureCompensationIndex = index;
+    }
+
+    public int getExposureCompensationIndex() {
+        return mExposureCompensationIndex;
+    }
+
+    public void setAutoExposureLock(boolean locked) {
+        mAutoExposureLocked = locked;
+    }
+
+    public boolean isAutoExposureLocked() {
+        return mAutoExposureLocked;
+    }
+
+    public void setMeteringAreas(List<Camera.Area> areas) {
+        mMeteringAreas.clear();
+        if (areas != null) {
+            mMeteringAreas.addAll(areas);
+        }
+    }
+
+    public List<Camera.Area> getMeteringAreas() {
+        return new ArrayList<Camera.Area>(mMeteringAreas);
+    }
+
+    /** Flash **/
+
+    public CameraCapabilities.FlashMode getCurrentFlashMode() {
+        return mCurrentFlashMode;
+    }
+
+    public void setFlashMode(CameraCapabilities.FlashMode flashMode) {
+        mCurrentFlashMode = flashMode;
+    }
+
+    /** Focus **/
+
+    /**
+     * Sets the focus mode.
+     * @param focusMode The focus mode to use.
+     */
+    public void setFocusMode(CameraCapabilities.FocusMode focusMode) {
+        mCurrentFocusMode = focusMode;
+    }
+
+    /**
+     * @return The current focus mode.
+     */
+    public CameraCapabilities.FocusMode getCurrentFocusMode() {
+        return mCurrentFocusMode;
+    }
+
+    /**
+     * @param areas The areas to focus.
+     */
+    public void setFocusAreas(List<Camera.Area> areas) {
+        mFocusAreas.clear();
+        if (areas != null) {
+            mFocusAreas.addAll(areas);
+        }
+    }
+
+    public List<Camera.Area> getFocusAreas() {
+        return new ArrayList<Camera.Area>(mFocusAreas);
+    }
+
+    /** White balance **/
+
+    public void setWhiteBalance(CameraCapabilities.WhiteBalance whiteBalance) {
+        mWhiteBalance = whiteBalance;
+    }
+
+    public CameraCapabilities.WhiteBalance getWhiteBalance() {
+        return mWhiteBalance;
+    }
+
+    public void setAutoWhiteBalanceLock(boolean locked) {
+        mAutoWhiteBalanceLocked = locked;
+    }
+
+    public boolean isAutoWhiteBalanceLocked() {
+        return mAutoWhiteBalanceLocked;
+    }
+
+    /** Scene mode **/
+
+    /**
+     * @return The current scene mode.
+     */
+    public CameraCapabilities.SceneMode getCurrentSceneMode() {
+        return mCurrentSceneMode;
+    }
+
+    /**
+     * Sets the scene mode for capturing.
+     *
+     * @param sceneMode The scene mode to use.
+     * @throws java.lang.UnsupportedOperationException if it's not supported.
+     */
+    public void setSceneMode(CameraCapabilities.SceneMode sceneMode) {
+        mCurrentSceneMode = sceneMode;
+    }
+
+    /** Other Features **/
+
+    public void setVideoStabilization(boolean enabled) {
+        mVideoStabilizationEnabled = enabled;
+    }
+
+    public boolean isVideoStabilizationEnabled() {
+        return mVideoStabilizationEnabled;
+    }
+
+    public void setRecordingHintEnabled(boolean hintEnabled) {
+        mRecordingHintEnabled = hintEnabled;
+    }
+
+    public boolean isRecordingHintEnabled() {
+        return mRecordingHintEnabled;
+    }
+
+    public void setGpsData(GpsData data) {
+        mGpsData = new GpsData(data);
+    }
+
+    public GpsData getGpsData() {
+        return (mGpsData == null ? null : new GpsData(mGpsData));
+    }
+
+    public void clearGpsData() {
+        mGpsData = null;
+    }
+
+    /**
+     * Sets the size of the thumbnail in EXIF header.
+     *
+     * @param s The size for the thumbnail. {@code null} will clear the size to
+     *          (0,0).
+     */
+    public void setExifThumbnailSize(Size s) {
+        if (s != null) {
+            mExifThumbnailSize = s;
+        } else {
+            mExifThumbnailSize = new Size(0,0);
+        }
+    }
+
+    public Size getExifThumbnailSize() {
+        return new Size(mExifThumbnailSize);
+    }
+}
diff --git a/camera2/portability/src/com/android/ex/camera2/portability/CameraStateHolder.java b/camera2/portability/src/com/android/ex/camera2/portability/CameraStateHolder.java
new file mode 100644
index 0000000..02d6c48
--- /dev/null
+++ b/camera2/portability/src/com/android/ex/camera2/portability/CameraStateHolder.java
@@ -0,0 +1,123 @@
+/*
+ * Copyright (C) 2014 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.android.ex.camera2.portability;
+
+import android.os.SystemClock;
+
+import com.android.ex.camera2.portability.debug.Log;
+
+class CameraStateHolder {
+    private static final Log.Tag TAG = new Log.Tag("CameraStateHolder");
+
+    /** Camera states **/
+    // These states are defined bitwise so we can easily to specify a set of
+    // states together.
+    public static final int CAMERA_UNOPENED = 1;
+    public static final int CAMERA_IDLE = 1 << 1;
+    public static final int CAMERA_UNLOCKED = 1 << 2;
+    public static final int CAMERA_CAPTURING = 1 << 3;
+    public static final int CAMERA_FOCUSING = 1 << 4;
+
+    private int mState;
+
+    public CameraStateHolder() {
+        setState(CAMERA_UNOPENED);
+    }
+
+    public CameraStateHolder(int state) {
+        setState(state);
+    }
+
+    public synchronized void setState(int state) {
+        mState = state;
+        this.notifyAll();
+    }
+
+    public synchronized int getState() {
+        return mState;
+    }
+
+    private interface ConditionChecker {
+        /**
+         * @return Whether the condition holds.
+         */
+        boolean success();
+    }
+
+    /**
+     * A helper method used by {@link #waitToAvoidStates(int)} and
+     * {@link #waitForStates(int)}. This method will wait until the
+     * condition is successful.
+     *
+     * @param stateChecker The state checker to be used.
+     * @param timeoutMs The timeout limit in milliseconds.
+     * @return {@code false} if the wait is interrupted or timeout limit is
+     *         reached.
+     */
+    private boolean waitForCondition(ConditionChecker stateChecker,
+            long timeoutMs) {
+        long timeBound = SystemClock.uptimeMillis() + timeoutMs;
+        synchronized (this) {
+            while (!stateChecker.success()) {
+                try {
+                    this.wait(timeoutMs);
+                } catch (InterruptedException ex) {
+                    if (SystemClock.uptimeMillis() > timeBound) {
+                        // Timeout.
+                        Log.w(TAG, "Timeout waiting.");
+                    }
+                    return false;
+                }
+            }
+        }
+        return true;
+    }
+
+    /**
+     * Block the current thread until the state becomes one of the
+     * specified.
+     *
+     * @param states Expected states.
+     * @return {@code false} if the wait is interrupted or timeout limit is
+     *         reached.
+     */
+    public boolean waitForStates(final int states) {
+        return waitForCondition(new ConditionChecker() {
+            @Override
+            public boolean success() {
+                return (states | mState) == states;
+            }
+        }, CameraAgent.CAMERA_OPERATION_TIMEOUT_MS);
+    }
+
+    /**
+     * Block the current thread until the state becomes NOT one of the
+     * specified.
+     *
+     * @param states States to avoid.
+     * @return {@code false} if the wait is interrupted or timeout limit is
+     *         reached.
+     */
+    public boolean waitToAvoidStates(final int states) {
+        return waitForCondition(new ConditionChecker() {
+            @Override
+            public boolean success() {
+                return (states & mState) == 0;
+            }
+        }, CameraAgent.CAMERA_OPERATION_TIMEOUT_MS);
+    }
+}
diff --git a/camera2/portability/src/com/android/ex/camera2/portability/DispatchThread.java b/camera2/portability/src/com/android/ex/camera2/portability/DispatchThread.java
new file mode 100644
index 0000000..67713c9
--- /dev/null
+++ b/camera2/portability/src/com/android/ex/camera2/portability/DispatchThread.java
@@ -0,0 +1,155 @@
+/*
+ * Copyright (C) 2014 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.android.ex.camera2.portability;
+
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.SystemClock;
+
+import com.android.ex.camera2.portability.debug.Log;
+
+import java.util.LinkedList;
+import java.util.Queue;
+
+class DispatchThread extends Thread {
+    private static final Log.Tag TAG = new Log.Tag("DispatchThread");
+    private static final long MAX_MESSAGE_QUEUE_LENGTH = 256;
+
+    private final Queue<Runnable> mJobQueue;
+    private Boolean mIsEnded;
+    private Handler mCameraHandler;
+    private HandlerThread mCameraHandlerThread;
+
+    public DispatchThread(Handler cameraHandler, HandlerThread cameraHandlerThread) {
+        super("Camera Job Dispatch Thread");
+        mJobQueue = new LinkedList<Runnable>();
+        mIsEnded = new Boolean(false);
+        mCameraHandler = cameraHandler;
+        mCameraHandlerThread = cameraHandlerThread;
+    }
+
+    /**
+     * Queues up the job.
+     *
+     * @param job The job to run.
+     */
+    public void runJob(Runnable job) {
+        if (isEnded()) {
+            throw new IllegalStateException(
+                    "Trying to run job on interrupted dispatcher thread");
+        }
+        synchronized (mJobQueue) {
+            if (mJobQueue.size() == MAX_MESSAGE_QUEUE_LENGTH) {
+                throw new RuntimeException("Camera master thread job queue full");
+            }
+
+            mJobQueue.add(job);
+            mJobQueue.notifyAll();
+        }
+    }
+
+    /**
+     * Queues up the job and wait for it to be done.
+     *
+     * @param job The job to run.
+     * @param timeoutMs Timeout limit in milliseconds.
+     * @param jobMsg The message to log when the job runs timeout.
+     * @return Whether the job finishes before timeout.
+     */
+    public void runJobSync(final Runnable job, Object waitLock, long timeoutMs, String jobMsg) {
+        String timeoutMsg = "Timeout waiting " + timeoutMs + "ms for " + jobMsg;
+        synchronized (waitLock) {
+            long timeoutBound = SystemClock.uptimeMillis() + timeoutMs;
+            try {
+                runJob(job);
+                waitLock.wait(timeoutMs);
+                if (SystemClock.uptimeMillis() > timeoutBound) {
+                    throw new IllegalStateException(timeoutMsg);
+                }
+            } catch (InterruptedException ex) {
+                if (SystemClock.uptimeMillis() > timeoutBound) {
+                    throw new IllegalStateException(timeoutMsg);
+                }
+            }
+        }
+    }
+
+    /**
+     * Gracefully ends this thread. Will stop after all jobs are processed.
+     */
+    public void end() {
+        synchronized (mIsEnded) {
+            mIsEnded = true;
+        }
+        synchronized(mJobQueue) {
+            mJobQueue.notifyAll();
+        }
+    }
+
+    private boolean isEnded() {
+        synchronized (mIsEnded) {
+            return mIsEnded;
+        }
+    }
+
+    @Override
+    public void run() {
+        while(true) {
+            Runnable job = null;
+            synchronized (mJobQueue) {
+                while (mJobQueue.size() == 0 && !isEnded()) {
+                    try {
+                        mJobQueue.wait();
+                    } catch (InterruptedException ex) {
+                        Log.w(TAG, "Dispatcher thread wait() interrupted, exiting");
+                        break;
+                    }
+                }
+
+                job = mJobQueue.poll();
+            }
+
+            if (job == null) {
+                // mJobQueue.poll() returning null means wait() is
+                // interrupted and the queue is empty.
+                if (isEnded()) {
+                    break;
+                }
+                continue;
+            }
+
+            job.run();
+
+            synchronized (DispatchThread.this) {
+                mCameraHandler.post(new Runnable() {
+                    @Override
+                    public void run() {
+                        synchronized (DispatchThread.this) {
+                            DispatchThread.this.notifyAll();
+                        }
+                    }
+                });
+                try {
+                    DispatchThread.this.wait();
+                } catch (InterruptedException ex) {
+                    // TODO: do something here.
+                }
+            }
+        }
+        mCameraHandlerThread.quitSafely();
+    }
+}
diff --git a/camera2/portability/src/com/android/ex/camera2/portability/HistoryHandler.java b/camera2/portability/src/com/android/ex/camera2/portability/HistoryHandler.java
new file mode 100644
index 0000000..ec2a555
--- /dev/null
+++ b/camera2/portability/src/com/android/ex/camera2/portability/HistoryHandler.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2014 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.android.ex.camera2.portability;
+
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+
+import java.util.LinkedList;
+
+class HistoryHandler extends Handler {
+    private static final int MAX_HISTORY_SIZE = 400;
+
+    final LinkedList<Integer> mMsgHistory;
+
+    HistoryHandler(Looper looper) {
+        super(looper);
+        mMsgHistory = new LinkedList<Integer>();
+        // We add a -1 at the beginning to mark the very beginning of the
+        // history.
+        mMsgHistory.offerLast(-1);
+    }
+
+    String generateHistoryString(int cameraId) {
+        String info = new String("HIST");
+        info += "_ID" + cameraId;
+        for (Integer msg : mMsgHistory) {
+            info = info + '_' + msg.toString();
+        }
+        info += "_HEND";
+        return info;
+    }
+
+    /**
+     * Subclasses' implementations should call this one before doing their work.
+     */
+    @Override
+    public void handleMessage(Message msg) {
+        mMsgHistory.offerLast(msg.what);
+        while (mMsgHistory.size() > MAX_HISTORY_SIZE) {
+            mMsgHistory.pollFirst();
+        }
+    }
+}
diff --git a/camera2/portability/src/com/android/ex/camera2/portability/Size.java b/camera2/portability/src/com/android/ex/camera2/portability/Size.java
new file mode 100644
index 0000000..9ae21aa
--- /dev/null
+++ b/camera2/portability/src/com/android/ex/camera2/portability/Size.java
@@ -0,0 +1,151 @@
+/*
+ * Copyright (C) 2014 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.android.ex.camera2.portability;
+
+import android.graphics.Point;
+import android.hardware.Camera;
+import android.text.TextUtils;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * An immutable simple size container.
+ */
+public class Size {
+    public static final String DELIMITER = ",";
+
+    /**
+     * An helper method to build a list of this class from a list of
+     * {@link android.hardware.Camera.Size}.
+     *
+     * @param cameraSizes Source.
+     * @return The built list.
+     */
+    public static List<Size> buildListFromCameraSizes(List<Camera.Size> cameraSizes) {
+        ArrayList<Size> list = new ArrayList<Size>(cameraSizes.size());
+        for (Camera.Size cameraSize : cameraSizes) {
+            list.add(new Size(cameraSize));
+        }
+        return list;
+    }
+
+    /**
+     * Encode List of this class as comma-separated list of integers.
+     *
+     * @param sizes List of this class to encode.
+     * @return encoded string.
+     */
+    public static String listToString(List<Size> sizes) {
+        ArrayList<Integer> flatSizes = new ArrayList<>();
+        for (Size s : sizes) {
+            flatSizes.add(s.width());
+            flatSizes.add(s.height());
+        }
+        return TextUtils.join(DELIMITER, flatSizes);
+    }
+
+    /**
+     * Decode comma-separated even-length list of integers into a List of this class.
+     *
+     * @param encodedSizes encoded string.
+     * @return List of this class.
+     */
+    public static List<Size> stringToList(String encodedSizes) {
+        String[] flatSizes = TextUtils.split(encodedSizes, DELIMITER);
+        ArrayList<Size> list = new ArrayList<>();
+        for (int i = 0; i < flatSizes.length; i += 2) {
+            int width = Integer.parseInt(flatSizes[i]);
+            int height = Integer.parseInt(flatSizes[i + 1]);
+            list.add(new Size(width,height));
+        }
+        return list;
+    }
+
+    private final Point val;
+
+    /**
+     * Constructor.
+     */
+    public Size(int width, int height) {
+        val = new Point(width, height);
+    }
+
+    /**
+     * Copy constructor.
+     */
+    public Size(Size other) {
+        if (other == null) {
+            val = new Point(0, 0);
+        } else {
+            val = new Point(other.width(), other.height());
+        }
+    }
+
+    /**
+     * Constructor from a source {@link android.hardware.Camera.Size}.
+     *
+     * @param other The source size.
+     */
+    public Size(Camera.Size other) {
+        if (other == null) {
+            val = new Point(0, 0);
+        } else {
+            val = new Point(other.width, other.height);
+        }
+    }
+
+    /**
+     * Constructor from a source {@link android.graphics.Point}.
+     *
+     * @param p The source size.
+     */
+    public Size(Point p) {
+        if (p == null) {
+            val = new Point(0, 0);
+        } else {
+            val = new Point(p);
+        }
+    }
+
+    public int width() {
+        return val.x;
+    }
+
+    public int height() {
+        return val.y;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (o instanceof Size) {
+            Size other = (Size) o;
+            return val.equals(other.val);
+        }
+        return false;
+    }
+
+    @Override
+    public int hashCode() {
+        return val.hashCode();
+    }
+
+    @Override
+    public String toString() {
+        return "Size: (" + this.width() + " x " + this.height() + ")";
+    }
+}
diff --git a/camera2/portability/src/com/android/ex/camera2/portability/debug/Log.java b/camera2/portability/src/com/android/ex/camera2/portability/debug/Log.java
new file mode 100644
index 0000000..07e758e
--- /dev/null
+++ b/camera2/portability/src/com/android/ex/camera2/portability/debug/Log.java
@@ -0,0 +1,135 @@
+/*
+ * Copyright (C) 2014 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.android.ex.camera2.portability.debug;
+
+public class Log {
+    /**
+     * All Camera logging using this class will use this tag prefix.
+     * Additionally, the prefix itself is checked in isLoggable and
+     * serves as an override. So, to toggle all logs allowed by the
+     * current {@link Configuration}, you can set properties:
+     *
+     * adb shell setprop log.tag.CAM2PORT_ VERBOSE
+     * adb shell setprop log.tag.CAM2PORT_ ""
+     */
+    public static final String CAMERA_LOGTAG_PREFIX = "CAM2PORT_";
+    private static final Log.Tag TAG = new Log.Tag("Log");
+
+    /**
+     * This class restricts the length of the log tag to be less than the
+     * framework limit and also prepends the common tag prefix defined by
+     * {@code CAMERA_LOGTAG_PREFIX}.
+     */
+    public static final class Tag {
+
+        // The length limit from Android framework is 23.
+        private static final int MAX_TAG_LEN = 23 - CAMERA_LOGTAG_PREFIX.length();
+
+        final String mValue;
+
+        public Tag(String tag) {
+            final int lenDiff = tag.length() - MAX_TAG_LEN;
+            if (lenDiff > 0) {
+                w(TAG, "Tag " + tag + " is " + lenDiff + " chars longer than limit.");
+            }
+            mValue = CAMERA_LOGTAG_PREFIX + (lenDiff > 0 ? tag.substring(0, MAX_TAG_LEN) : tag);
+        }
+
+        @Override
+        public String toString() {
+            return mValue;
+        }
+    }
+
+    public static void d(Tag tag, String msg) {
+        if (isLoggable(tag, android.util.Log.DEBUG)) {
+            android.util.Log.d(tag.toString(), msg);
+        }
+    }
+
+    public static void d(Tag tag, String msg, Throwable tr) {
+        if (isLoggable(tag, android.util.Log.DEBUG)) {
+            android.util.Log.d(tag.toString(), msg, tr);
+        }
+    }
+
+    public static void e(Tag tag, String msg) {
+        if (isLoggable(tag, android.util.Log.ERROR)) {
+            android.util.Log.e(tag.toString(), msg);
+        }
+    }
+
+    public static void e(Tag tag, String msg, Throwable tr) {
+        if (isLoggable(tag, android.util.Log.ERROR)) {
+            android.util.Log.e(tag.toString(), msg, tr);
+        }
+    }
+
+    public static void i(Tag tag, String msg) {
+        if (isLoggable(tag, android.util.Log.INFO)) {
+            android.util.Log.i(tag.toString(), msg);
+        }
+    }
+
+    public static void i(Tag tag, String msg, Throwable tr) {
+        if (isLoggable(tag, android.util.Log.INFO)) {
+            android.util.Log.i(tag.toString(), msg, tr);
+        }
+    }
+
+    public static void v(Tag tag, String msg) {
+        if (isLoggable(tag, android.util.Log.VERBOSE)) {
+            android.util.Log.v(tag.toString(), msg);
+        }
+    }
+
+    public static void v(Tag tag, String msg, Throwable tr) {
+        if (isLoggable(tag, android.util.Log.VERBOSE)) {
+            android.util.Log.v(tag.toString(), msg, tr);
+        }
+    }
+
+    public static void w(Tag tag, String msg) {
+        if (isLoggable(tag, android.util.Log.WARN)) {
+            android.util.Log.w(tag.toString(), msg);
+        }
+    }
+
+    public static void w(Tag tag, String msg, Throwable tr) {
+        if (isLoggable(tag, android.util.Log.WARN)) {
+            android.util.Log.w(tag.toString(), msg, tr);
+        }
+    }
+
+    private static boolean isLoggable(Tag tag, int level) {
+        try {
+            if (LogHelper.getOverrideLevel() != 0) {
+                // Override system log level and output. VERBOSE is smaller than
+                // ERROR, so the comparison checks that the override value is smaller
+                // than the desired output level. This applies to all tags.
+                return LogHelper.getOverrideLevel() <= level;
+            } else {
+                // The prefix can be used as an override tag to see all camera logs
+                return android.util.Log.isLoggable(CAMERA_LOGTAG_PREFIX, level)
+                        || android.util.Log.isLoggable(tag.toString(), level);
+            }
+        } catch (IllegalArgumentException ex) {
+            e(TAG, "Tag too long:" + tag);
+            return false;
+        }
+    }
+}
diff --git a/camera2/portability/src/com/android/ex/camera2/portability/debug/LogHelper.java b/camera2/portability/src/com/android/ex/camera2/portability/debug/LogHelper.java
new file mode 100644
index 0000000..3ec0260
--- /dev/null
+++ b/camera2/portability/src/com/android/ex/camera2/portability/debug/LogHelper.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2014 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.android.ex.camera2.portability.debug;
+
+import android.content.Context;
+
+public class LogHelper {
+    public static void initialize(Context context) {
+        // Do nothing.
+    }
+
+    /**
+     * Return a valid log level from {@link android.util.Log} to override
+     * the system log level. Return 0 to instead defer to system log level.
+     */
+    public static int getOverrideLevel() {
+        return 0;
+    }
+}
\ No newline at end of file
diff --git a/camera2/public/src/com/android/ex/camera2/blocking/BlockingSessionListener.java b/camera2/public/src/com/android/ex/camera2/blocking/BlockingSessionListener.java
new file mode 100644
index 0000000..725b368
--- /dev/null
+++ b/camera2/public/src/com/android/ex/camera2/blocking/BlockingSessionListener.java
@@ -0,0 +1,228 @@
+/*
+ * Copyright 2014 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.android.ex.camera2.blocking;
+
+import android.hardware.camera2.CameraCaptureSession;
+import android.os.ConditionVariable;
+import android.util.Log;
+
+import com.android.ex.camera2.exceptions.TimeoutRuntimeException;
+import com.android.ex.camera2.utils.StateChangeListener;
+import com.android.ex.camera2.utils.StateWaiter;
+
+import java.util.ArrayList;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+
+
+/**
+ * A camera session listener that implements blocking operations on session state changes.
+ *
+ * <p>Provides a waiter that can be used to block until the next unobserved state of the
+ * requested type arrives.</p>
+ *
+ * <p>Pass-through all StateListener changes to the proxy.</p>
+ *
+ * @see #getStateWaiter
+ */
+public class BlockingSessionListener extends CameraCaptureSession.StateListener {
+    /**
+     * Session is configured, ready for captures
+     */
+    public static final int SESSION_CONFIGURED = 0;
+
+    /**
+     * Session has failed to configure, can't do any captures
+     */
+    public static final int SESSION_CONFIGURE_FAILED = 1;
+
+    /**
+     * Session is ready
+     */
+    public static final int SESSION_READY = 2;
+
+    /**
+     * Session is active (transitory)
+     */
+    public static final int SESSION_ACTIVE = 3;
+
+    /**
+     * Session is closed
+     */
+    public static final int SESSION_CLOSED = 4;
+
+    private final int NUM_STATES = 5;
+
+    /*
+     * Private fields
+     */
+    private static final String TAG = "BlockingSessionListener";
+    private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
+
+    private final CameraCaptureSession.StateListener mProxy;
+    private final SessionFuture mSessionFuture = new SessionFuture();
+
+    private final StateWaiter mStateWaiter = new StateWaiter(mStateNames);
+    private final StateChangeListener mStateChangeListener = mStateWaiter.getListener();
+
+    private static final String[] mStateNames = {
+        "SESSION_CONFIGURED",
+        "SESSION_CONFIGURE_FAILED",
+        "SESSION_READY",
+        "SESSION_ACTIVE",
+        "SESSION_CLOSED"
+    };
+
+    /**
+     * Create a blocking session listener without forwarding the session listener invocations
+     * to another session listener.
+     */
+    public BlockingSessionListener() {
+        mProxy = null;
+    }
+
+    /**
+     * Create a blocking session listener; forward original listener invocations
+     * into {@code listener}.
+     *
+     * @param listener a non-{@code null} listener to forward invocations into
+     *
+     * @throws NullPointerException if {@code listener} was {@code null}
+     */
+    public BlockingSessionListener(CameraCaptureSession.StateListener listener) {
+        if (listener == null) {
+            throw new NullPointerException("listener must not be null");
+        }
+        mProxy = listener;
+    }
+
+    /**
+     * Acquire the state waiter; can be used to block until a set of state transitions have
+     * been reached.
+     *
+     * <p>Only one thread should wait at a time.</p>
+     */
+    public StateWaiter getStateWaiter() {
+        return mStateWaiter;
+    }
+
+    /**
+     * Return session if already have it; otherwise wait until any of the session listener
+     * invocations fire and the session is available.
+     *
+     * <p>Does not consume any of the states from the state waiter.</p>
+     *
+     * @param timeoutMs how many milliseconds to wait for
+     * @return a non-{@code null} {@link CameraCaptureSession} instance
+     *
+     * @throws TimeoutRuntimeException if waiting for more than {@long timeoutMs}
+     */
+    public CameraCaptureSession waitAndGetSession(long timeoutMs) {
+        try {
+            return mSessionFuture.get(timeoutMs, TimeUnit.MILLISECONDS);
+        } catch (TimeoutException e) {
+            throw new TimeoutRuntimeException(
+                    String.format("Failed to get session after %s milliseconds", timeoutMs), e);
+        }
+    }
+
+    /*
+     * CameraCaptureSession.StateListener implementation
+     */
+
+    @Override
+    public void onActive(CameraCaptureSession session) {
+        mSessionFuture.setSession(session);
+        if (mProxy != null) mProxy.onActive(session);
+        mStateChangeListener.onStateChanged(SESSION_ACTIVE);
+    }
+
+    @Override
+    public void onClosed(CameraCaptureSession session) {
+        mSessionFuture.setSession(session);
+        if (mProxy != null) mProxy.onClosed(session);
+        mStateChangeListener.onStateChanged(SESSION_CLOSED);
+    }
+
+    @Override
+    public void onConfigured(CameraCaptureSession session) {
+        mSessionFuture.setSession(session);
+        if (mProxy != null) mProxy.onConfigured(session);
+        mStateChangeListener.onStateChanged(SESSION_CONFIGURED);
+    }
+
+    @Override
+    public void onConfigureFailed(CameraCaptureSession session) {
+        mSessionFuture.setSession(session);
+        if (mProxy != null) mProxy.onConfigureFailed(session);
+        mStateChangeListener.onStateChanged(SESSION_CONFIGURE_FAILED);
+    }
+
+    @Override
+    public void onReady(CameraCaptureSession session) {
+        mSessionFuture.setSession(session);
+        if (mProxy != null) mProxy.onReady(session);
+        mStateChangeListener.onStateChanged(SESSION_READY);
+    }
+
+    private static class SessionFuture implements Future<CameraCaptureSession> {
+        private volatile CameraCaptureSession mSession;
+        ConditionVariable mCondVar = new ConditionVariable(/*opened*/false);
+
+        public void setSession(CameraCaptureSession session) {
+            mSession = session;
+            mCondVar.open();
+        }
+
+        @Override
+        public boolean cancel(boolean mayInterruptIfRunning) {
+            return false; // don't allow canceling this task
+        }
+
+        @Override
+        public boolean isCancelled() {
+            return false; // can never cancel this task
+        }
+
+        @Override
+        public boolean isDone() {
+            return mSession != null;
+        }
+
+        @Override
+        public CameraCaptureSession get() {
+            mCondVar.block();
+            return mSession;
+        }
+
+        @Override
+        public CameraCaptureSession get(long timeout, TimeUnit unit) throws TimeoutException {
+            long timeoutMs = unit.convert(timeout, TimeUnit.MILLISECONDS);
+            if (!mCondVar.block(timeoutMs)) {
+                throw new TimeoutException(
+                        "Failed to receive session after " + timeout + " " + unit);
+            }
+
+            if (mSession == null) {
+                throw new AssertionError();
+            }
+            return mSession;
+        }
+
+    }
+}
diff --git a/camera2/public/src/com/android/ex/camera2/blocking/BlockingStateListener.java b/camera2/public/src/com/android/ex/camera2/blocking/BlockingStateListener.java
index 619ba0a..ef3b293 100644
--- a/camera2/public/src/com/android/ex/camera2/blocking/BlockingStateListener.java
+++ b/camera2/public/src/com/android/ex/camera2/blocking/BlockingStateListener.java
@@ -86,21 +86,25 @@
     /**
      * Device is unconfigured
      */
+    @Deprecated
     public static final int STATE_UNCONFIGURED = 1;
 
     /**
      * Device is idle
      */
+    @Deprecated
     public static final int STATE_IDLE = 2;
 
     /**
      * Device is active (transitory)
      */
+    @Deprecated
     public static final int STATE_ACTIVE = 3;
 
     /**
      * Device is busy (transitory)
      */
+    @Deprecated
     public static final int STATE_BUSY = 4;
 
     /**
@@ -133,50 +137,54 @@
 
     @Override
     public void onOpened(CameraDevice camera) {
-        setCurrentState(STATE_OPENED);
         if (mProxy != null) mProxy.onOpened(camera);
+        setCurrentState(STATE_OPENED);
     }
 
     @Override
     public void onDisconnected(CameraDevice camera) {
-        setCurrentState(STATE_DISCONNECTED);
         if (mProxy != null) mProxy.onDisconnected(camera);
+        setCurrentState(STATE_DISCONNECTED);
     }
 
     @Override
     public void onError(CameraDevice camera, int error) {
-        setCurrentState(STATE_ERROR);
         if (mProxy != null) mProxy.onError(camera, error);
+        setCurrentState(STATE_ERROR);
     }
 
     @Override
+    @Deprecated
     public void onUnconfigured(CameraDevice camera) {
-        setCurrentState(STATE_UNCONFIGURED);
         if (mProxy != null) mProxy.onUnconfigured(camera);
+        setCurrentState(STATE_UNCONFIGURED);
     }
 
     @Override
+    @Deprecated
     public void onIdle(CameraDevice camera) {
-        setCurrentState(STATE_IDLE);
         if (mProxy != null) mProxy.onIdle(camera);
+        setCurrentState(STATE_IDLE);
     }
 
     @Override
+    @Deprecated
     public void onActive(CameraDevice camera) {
-        setCurrentState(STATE_ACTIVE);
         if (mProxy != null) mProxy.onActive(camera);
+        setCurrentState(STATE_ACTIVE);
     }
 
     @Override
+    @Deprecated
     public void onBusy(CameraDevice camera) {
-        setCurrentState(STATE_BUSY);
         if (mProxy != null) mProxy.onBusy(camera);
+        setCurrentState(STATE_BUSY);
     }
 
     @Override
     public void onClosed(CameraDevice camera) {
-        setCurrentState(STATE_CLOSED);
         if (mProxy != null) mProxy.onClosed(camera);
+        setCurrentState(STATE_CLOSED);
     }
 
     /**
diff --git a/camera2/public/src/com/android/ex/camera2/pos/AutoFocusStateMachine.java b/camera2/public/src/com/android/ex/camera2/pos/AutoFocusStateMachine.java
index 500fb12..9fa2af2 100644
--- a/camera2/public/src/com/android/ex/camera2/pos/AutoFocusStateMachine.java
+++ b/camera2/public/src/com/android/ex/camera2/pos/AutoFocusStateMachine.java
@@ -16,7 +16,7 @@
 package com.android.ex.camera2.pos;
 
 import android.hardware.camera2.CameraDevice;
-import android.hardware.camera2.CameraMetadata.Key;
+import android.hardware.camera2.CaptureResult.Key;
 import android.hardware.camera2.CaptureRequest;
 import android.hardware.camera2.CaptureResult;
 import android.util.Log;
diff --git a/chips/src/com/android/ex/chips/AccountSpecifier.java b/camera2/public/src/com/android/ex/camera2/utils/StateChangeListener.java
similarity index 61%
rename from chips/src/com/android/ex/chips/AccountSpecifier.java
rename to camera2/public/src/com/android/ex/camera2/utils/StateChangeListener.java
index 5eb8314..48cca17 100644
--- a/chips/src/com/android/ex/chips/AccountSpecifier.java
+++ b/camera2/public/src/com/android/ex/camera2/utils/StateChangeListener.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2011 The Android Open Source Project
+ * Copyright 2014 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.
@@ -14,15 +14,8 @@
  * limitations under the License.
  */
 
-package com.android.ex.chips;
+package com.android.ex.camera2.utils;
 
-import android.accounts.Account;
-
-/**
- * The AccountSpecificAdapter interface describes an Adapter
- * that can take an account to retrieve information tied to
- * a specific account.
- */
-public interface AccountSpecifier {
-    public void setAccount(Account account);
+public interface StateChangeListener {
+    public void onStateChanged(int state);
 }
diff --git a/camera2/public/src/com/android/ex/camera2/utils/StateWaiter.java b/camera2/public/src/com/android/ex/camera2/utils/StateWaiter.java
new file mode 100644
index 0000000..ec57829
--- /dev/null
+++ b/camera2/public/src/com/android/ex/camera2/utils/StateWaiter.java
@@ -0,0 +1,220 @@
+/*
+ * Copyright 2014 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.android.ex.camera2.utils;
+
+import android.os.SystemClock;
+import android.util.Log;
+
+import com.android.ex.camera2.exceptions.TimeoutRuntimeException;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+/**
+ * Block until a specific state change occurs.
+ *
+ * <p>Provides wait calls that block until the next unobserved state of the
+ * requested type arrives. Unobserved states are states that have occurred since
+ * the last wait, or that will be received from the camera device in the
+ * future.</p>
+ *
+ * <p>Thread interruptions are not supported; interrupting a thread that is either
+ * waiting with {@link #waitForState} / {@link #waitForAnyOfStates} or is currently in
+ * {@link StateChangeListener#onStateChanged} (provided by {@link #getListener}) will result in an
+ * {@link UnsupportedOperationException} being raised on that thread.</p>
+ */
+public final class StateWaiter {
+
+    private static final String TAG = "StateWaiter";
+    private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
+
+    private final String[] mStateNames;
+    private final int mStateCount;
+    private final StateChangeListener mListener;
+
+    /** Guard waitForState, waitForAnyState to only have one waiter */
+    private final AtomicBoolean mWaiting = new AtomicBoolean(false);
+
+    private final LinkedBlockingQueue<Integer> mQueuedStates = new LinkedBlockingQueue<>();
+
+    /**
+     * Create a new state waiter.
+     *
+     * <p>All {@code state}/{@code states} arguments used in other methods must be
+     * in the range of {@code [0, stateNames.length - 1]}.</p>
+     *
+     * @param stateNames an array of string names, used to mark the range of the valid states
+     */
+    public StateWaiter(String[] stateNames) {
+        mStateCount = stateNames.length;
+        mStateNames = new String[mStateCount];
+        System.arraycopy(stateNames, /*srcPos*/0, mStateNames, /*dstPos*/0, mStateCount);
+
+        mListener = new StateChangeListener() {
+            @Override
+            public void onStateChanged(int state) {
+                queueStateTransition(checkStateInRange(state));
+            }
+        };
+    }
+
+    public StateChangeListener getListener() {
+        return mListener;
+    }
+
+    /**
+     * Wait until the desired state is observed, checking all state
+     * transitions since the last time a state was waited on.
+     *
+     * <p>Any intermediate state transitions that is not {@code state} are ignored.</p>
+     *
+     * <p>Note: Only one waiter allowed at a time!</p>
+     *
+     * @param desired state to observe a transition to
+     * @param timeoutMs how long to wait in milliseconds
+     *
+     * @throws IllegalArgumentException if {@code state} was out of range
+     * @throws TimeoutRuntimeException if the desired state is not observed before timeout.
+     * @throws IllegalStateException if another thread is already waiting for a state transition
+     */
+    public void waitForState(int state, long timeoutMs) {
+        Integer[] stateArray = { checkStateInRange(state) };
+
+        waitForAnyOfStates(Arrays.asList(stateArray), timeoutMs);
+    }
+
+    /**
+     * Wait until the one of the desired {@code states} is observed, checking all
+     * state transitions since the last time a state was waited on.
+     *
+     * <p>Any intermediate state transitions that are not in {@code states} are ignored.</p>
+     *
+     * <p>Note: Only one waiter allowed at a time!</p>
+     *
+     * @param states Set of desired states to observe a transition to.
+     * @param timeoutMs how long to wait in milliseconds
+     *
+     * @return the state reached
+     *
+     * @throws IllegalArgumentException if {@code state} was out of range
+     * @throws TimeoutRuntimeException if none of the states is observed before timeout.
+     * @throws IllegalStateException if another thread is already waiting for a state transition
+     */
+    public int waitForAnyOfStates(Collection<Integer> states, final long timeoutMs) {
+        checkStateCollectionInRange(states);
+
+        // Acquire exclusive waiting privileges
+        if (mWaiting.getAndSet(true)) {
+            throw new IllegalStateException("Only one waiter allowed at a time");
+        }
+
+        Integer nextState = null;
+        try {
+            if (VERBOSE) {
+                StringBuilder s = new StringBuilder("Waiting for state(s) ");
+                appendStateNames(s, states);
+                Log.v(TAG, s.toString());
+            }
+
+            long timeoutLeft = timeoutMs;
+            long startMs = SystemClock.elapsedRealtime();
+            while ((nextState = mQueuedStates.poll(timeoutLeft, TimeUnit.MILLISECONDS)) != null) {
+                if (VERBOSE) {
+                    Log.v(TAG, "  Saw transition to " + getStateName(nextState));
+                }
+
+                if (states.contains(nextState)) {
+                    break;
+                }
+
+                long endMs = SystemClock.elapsedRealtime();
+                timeoutLeft -= (endMs - startMs);
+                startMs = endMs;
+            }
+        } catch (InterruptedException e) {
+            throw new UnsupportedOperationException("Does not support interrupts on waits", e);
+        } finally {
+            // Release exclusive waiting privileges
+            mWaiting.set(false);
+        }
+
+        if (!states.contains(nextState)) {
+            StringBuilder s = new StringBuilder("Timed out after ");
+            s.append(timeoutMs);
+            s.append(" ms waiting for state(s) ");
+            appendStateNames(s, states);
+
+            throw new TimeoutRuntimeException(s.toString());
+        }
+
+        return nextState;
+    }
+
+    /**
+     * Convert state integer to a String
+     */
+    public String getStateName(int state) {
+        return mStateNames[checkStateInRange(state)];
+    }
+
+    /**
+     * Append all states to string
+     */
+    public void appendStateNames(StringBuilder s, Collection<Integer> states) {
+        checkStateCollectionInRange(states);
+
+        boolean start = true;
+        for (Integer state : states) {
+            if (!start) {
+                s.append(" ");
+            }
+
+            s.append(getStateName(state));
+            start = false;
+        }
+    }
+
+    private void queueStateTransition(int state) {
+        if (VERBOSE) Log.v(TAG, "setCurrentState - state now " + getStateName(state));
+
+        try {
+            mQueuedStates.put(state);
+        } catch(InterruptedException e) {
+            throw new UnsupportedOperationException("Unable to set current state", e);
+        }
+    }
+
+    private int checkStateInRange(int state) {
+        if (state < 0 || state >= mStateCount) {
+            throw new IllegalArgumentException("State out of range " + state);
+        }
+
+        return state;
+    }
+
+    private Collection<Integer> checkStateCollectionInRange(Collection<Integer> states) {
+        for (int state : states) {
+            checkStateInRange(state);
+        }
+
+        return states;
+    }
+
+}
diff --git a/carousel/test/res/values-am/strings.xml b/carousel/test/res/values-am/strings.xml
index 5f4b961..f1e8da2 100644
--- a/carousel/test/res/values-am/strings.xml
+++ b/carousel/test/res/values-am/strings.xml
@@ -21,7 +21,7 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="music_demo_activity_label" msgid="4382090808250495841">"የሙዚቃ Carousel"</string>
     <string name="carousel_test_activity_label" msgid="6014624482213318747">"የCarousel ፍተሻ"</string>
-    <string name="carousel_test_activity_description" msgid="1632693812604375483">"የCarouselን ጥቅም የሚያሳይ ትግበራ"</string>
+    <string name="carousel_test_activity_description" msgid="1632693812604375483">"የCarouselን ጥቅም የሚያሳይ መተግበሪያ"</string>
     <string name="task_switcher_activity_label" msgid="714620143340933546">"ክንውን ቀያያሪ"</string>
     <string name="recent_tasks_title" msgid="1030287226205477117">"የቅርብ ጊዜ ትግበራዎች"</string>
     <string name="no_recent_tasks" msgid="6884096266670555780">"ምንም የቅርብ ጊዜ ክንውን የለም"</string>
diff --git a/carousel/test/res/values-az-rAZ/strings.xml b/carousel/test/res/values-az-rAZ/strings.xml
new file mode 100644
index 0000000..634f926
--- /dev/null
+++ b/carousel/test/res/values-az-rAZ/strings.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/*
+* 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="music_demo_activity_label" msgid="4382090808250495841">"MusicCarousel"</string>
+    <string name="carousel_test_activity_label" msgid="6014624482213318747">"CarouselTest"</string>
+    <string name="carousel_test_activity_description" msgid="1632693812604375483">"Carousel istifadəsini göstərmək üçün tətbiq"</string>
+    <string name="task_switcher_activity_label" msgid="714620143340933546">"TaskSwitcher"</string>
+    <string name="recent_tasks_title" msgid="1030287226205477117">"Son Tətbiqlər"</string>
+    <string name="no_recent_tasks" msgid="6884096266670555780">"Son tapşırıqlar yoxdur"</string>
+</resources>
diff --git a/carousel/test/res/values-az/strings.xml b/carousel/test/res/values-az/strings.xml
new file mode 100644
index 0000000..634f926
--- /dev/null
+++ b/carousel/test/res/values-az/strings.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/*
+* 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="music_demo_activity_label" msgid="4382090808250495841">"MusicCarousel"</string>
+    <string name="carousel_test_activity_label" msgid="6014624482213318747">"CarouselTest"</string>
+    <string name="carousel_test_activity_description" msgid="1632693812604375483">"Carousel istifadəsini göstərmək üçün tətbiq"</string>
+    <string name="task_switcher_activity_label" msgid="714620143340933546">"TaskSwitcher"</string>
+    <string name="recent_tasks_title" msgid="1030287226205477117">"Son Tətbiqlər"</string>
+    <string name="no_recent_tasks" msgid="6884096266670555780">"Son tapşırıqlar yoxdur"</string>
+</resources>
diff --git a/carousel/test/res/values-ca/strings.xml b/carousel/test/res/values-ca/strings.xml
index 15d758c..9f85f5e 100644
--- a/carousel/test/res/values-ca/strings.xml
+++ b/carousel/test/res/values-ca/strings.xml
@@ -21,7 +21,7 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="music_demo_activity_label" msgid="4382090808250495841">"MusicCarousel"</string>
     <string name="carousel_test_activity_label" msgid="6014624482213318747">"CarouselTest"</string>
-    <string name="carousel_test_activity_description" msgid="1632693812604375483">"Una aplicació per mostrar l\'ús de Carousel"</string>
+    <string name="carousel_test_activity_description" msgid="1632693812604375483">"Una aplicació per mostrar com es fa servir l\'expositor giratori"</string>
     <string name="task_switcher_activity_label" msgid="714620143340933546">"TaskSwitcher"</string>
     <string name="recent_tasks_title" msgid="1030287226205477117">"Aplicacions usades recentment"</string>
     <string name="no_recent_tasks" msgid="6884096266670555780">"No hi ha tasques recents"</string>
diff --git a/carousel/test/res/values-en-rIN/strings.xml b/carousel/test/res/values-en-rIN/strings.xml
new file mode 100644
index 0000000..86ea2cb
--- /dev/null
+++ b/carousel/test/res/values-en-rIN/strings.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/*
+* 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="music_demo_activity_label" msgid="4382090808250495841">"Music Carousel"</string>
+    <string name="carousel_test_activity_label" msgid="6014624482213318747">"Carousel Test"</string>
+    <string name="carousel_test_activity_description" msgid="1632693812604375483">"An application to show the use of Carousel"</string>
+    <string name="task_switcher_activity_label" msgid="714620143340933546">"Task Switcher"</string>
+    <string name="recent_tasks_title" msgid="1030287226205477117">"Recent Applications"</string>
+    <string name="no_recent_tasks" msgid="6884096266670555780">"No recent tasks"</string>
+</resources>
diff --git a/carousel/test/res/values-et-rEE/strings.xml b/carousel/test/res/values-et-rEE/strings.xml
new file mode 100644
index 0000000..59f0b5c
--- /dev/null
+++ b/carousel/test/res/values-et-rEE/strings.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/*
+* 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="music_demo_activity_label" msgid="4382090808250495841">"MusicCarousel"</string>
+    <string name="carousel_test_activity_label" msgid="6014624482213318747">"CarouselTest"</string>
+    <string name="carousel_test_activity_description" msgid="1632693812604375483">"Rakendus, mis näitab karusselli kasutust"</string>
+    <string name="task_switcher_activity_label" msgid="714620143340933546">"TaskSwitcher"</string>
+    <string name="recent_tasks_title" msgid="1030287226205477117">"Hiljutised rakendused"</string>
+    <string name="no_recent_tasks" msgid="6884096266670555780">"Hiljutised ülesanded puuduvad"</string>
+</resources>
diff --git a/carousel/test/res/values-fr-rCA/strings.xml b/carousel/test/res/values-fr-rCA/strings.xml
new file mode 100644
index 0000000..87aeb34
--- /dev/null
+++ b/carousel/test/res/values-fr-rCA/strings.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/*
+* 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="music_demo_activity_label" msgid="4382090808250495841">"MusicCarousel"</string>
+    <string name="carousel_test_activity_label" msgid="6014624482213318747">"CarouselTest"</string>
+    <string name="carousel_test_activity_description" msgid="1632693812604375483">"Une application expliquant l\'utilisation de Carousel"</string>
+    <string name="task_switcher_activity_label" msgid="714620143340933546">"TaskSwitcher"</string>
+    <string name="recent_tasks_title" msgid="1030287226205477117">"Applications récentes"</string>
+    <string name="no_recent_tasks" msgid="6884096266670555780">"Aucune tâche récente"</string>
+</resources>
diff --git a/carousel/test/res/values-hy-rAM/strings.xml b/carousel/test/res/values-hy-rAM/strings.xml
new file mode 100644
index 0000000..39bf205
--- /dev/null
+++ b/carousel/test/res/values-hy-rAM/strings.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/*
+* 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="music_demo_activity_label" msgid="4382090808250495841">"MusicCarousel"</string>
+    <string name="carousel_test_activity_label" msgid="6014624482213318747">"CarouselTest"</string>
+    <string name="carousel_test_activity_description" msgid="1632693812604375483">"Հավելված` Carousel-ի օգտագործումը ցույց տալու համար"</string>
+    <string name="task_switcher_activity_label" msgid="714620143340933546">"TaskSwitcher"</string>
+    <string name="recent_tasks_title" msgid="1030287226205477117">"Վերջին հավելվածները"</string>
+    <string name="no_recent_tasks" msgid="6884096266670555780">"Վերջին առաջադրանքներ չկան"</string>
+</resources>
diff --git a/carousel/test/res/values-iw/strings.xml b/carousel/test/res/values-iw/strings.xml
index 53fa5d2..a4163c5 100644
--- a/carousel/test/res/values-iw/strings.xml
+++ b/carousel/test/res/values-iw/strings.xml
@@ -21,8 +21,8 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="music_demo_activity_label" msgid="4382090808250495841">"MusicCarousel"</string>
     <string name="carousel_test_activity_label" msgid="6014624482213318747">"CarouselTest"</string>
-    <string name="carousel_test_activity_description" msgid="1632693812604375483">"יישום להצגת השימוש בקרוסלה"</string>
+    <string name="carousel_test_activity_description" msgid="1632693812604375483">"אפליקציה להצגת השימוש בקרוסלה"</string>
     <string name="task_switcher_activity_label" msgid="714620143340933546">"TaskSwitcher"</string>
-    <string name="recent_tasks_title" msgid="1030287226205477117">"יישומים אחרונים"</string>
+    <string name="recent_tasks_title" msgid="1030287226205477117">"אפליקציות אחרונות"</string>
     <string name="no_recent_tasks" msgid="6884096266670555780">"אין משימות אחרונות"</string>
 </resources>
diff --git a/carousel/test/res/values-ka-rGE/strings.xml b/carousel/test/res/values-ka-rGE/strings.xml
new file mode 100644
index 0000000..0764f3f
--- /dev/null
+++ b/carousel/test/res/values-ka-rGE/strings.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/*
+* 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="music_demo_activity_label" msgid="4382090808250495841">"MusicCarousel"</string>
+    <string name="carousel_test_activity_label" msgid="6014624482213318747">"CarouselTest"</string>
+    <string name="carousel_test_activity_description" msgid="1632693812604375483">"Carousel-ის გამოყენების მაჩვენებელი აპლიკაცია"</string>
+    <string name="task_switcher_activity_label" msgid="714620143340933546">"TaskSwitcher"</string>
+    <string name="recent_tasks_title" msgid="1030287226205477117">"ბოლოდროინდელი აპლიკაციები"</string>
+    <string name="no_recent_tasks" msgid="6884096266670555780">"ბოლოდროინდელი ამოცანები არ არის."</string>
+</resources>
diff --git a/carousel/test/res/values-km-rKH/strings.xml b/carousel/test/res/values-km-rKH/strings.xml
new file mode 100644
index 0000000..5bb198d
--- /dev/null
+++ b/carousel/test/res/values-km-rKH/strings.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/*
+* 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="music_demo_activity_label" msgid="4382090808250495841">"MusicCarousel"</string>
+    <string name="carousel_test_activity_label" msgid="6014624482213318747">"CarouselTest"</string>
+    <string name="carousel_test_activity_description" msgid="1632693812604375483">"កម្មវិធី​ត្រូវ​បង្ហាញ​ការ​ប្រើ Carousel"</string>
+    <string name="task_switcher_activity_label" msgid="714620143340933546">"TaskSwitcher"</string>
+    <string name="recent_tasks_title" msgid="1030287226205477117">"កម្មវិធី​ថ្មីៗ"</string>
+    <string name="no_recent_tasks" msgid="6884096266670555780">"គ្មាន​ភារកិច្ច​ថ្មីៗ"</string>
+</resources>
diff --git a/carousel/test/res/values-lo-rLA/strings.xml b/carousel/test/res/values-lo-rLA/strings.xml
new file mode 100644
index 0000000..83a4cbd
--- /dev/null
+++ b/carousel/test/res/values-lo-rLA/strings.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/*
+* 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="music_demo_activity_label" msgid="4382090808250495841">"MusicCarousel"</string>
+    <string name="carousel_test_activity_label" msgid="6014624482213318747">"CarouselTest"</string>
+    <string name="carousel_test_activity_description" msgid="1632693812604375483">"ແອັບພລິເຄຊັນທີ່ໃຊ້ສະແດງປະໂຫຍດຂອງ Carousel"</string>
+    <string name="task_switcher_activity_label" msgid="714620143340933546">"TaskSwitcher"</string>
+    <string name="recent_tasks_title" msgid="1030287226205477117">"ແອັບຯພລິເຄຊັນທີ່ຫາກໍໃຊ້"</string>
+    <string name="no_recent_tasks" msgid="6884096266670555780">"ບໍ່ມີວຽກເມື່ອໄວໆນີ້ເທື່ອ"</string>
+</resources>
diff --git a/carousel/test/res/values-mn-rMN/strings.xml b/carousel/test/res/values-mn-rMN/strings.xml
new file mode 100644
index 0000000..20d9f8d
--- /dev/null
+++ b/carousel/test/res/values-mn-rMN/strings.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/*
+* 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="music_demo_activity_label" msgid="4382090808250495841">"Хөгжмийн тойруулга"</string>
+    <string name="carousel_test_activity_label" msgid="6014624482213318747">"Тойруулга тест"</string>
+    <string name="carousel_test_activity_description" msgid="1632693812604375483">"Тойруулга ашиглалтыг харуулах аппликешн"</string>
+    <string name="task_switcher_activity_label" msgid="714620143340933546">"Даалгавар солигч"</string>
+    <string name="recent_tasks_title" msgid="1030287226205477117">"Сүүлийн аппликешн"</string>
+    <string name="no_recent_tasks" msgid="6884096266670555780">"Сүүлийн даалгавар хоосон"</string>
+</resources>
diff --git a/carousel/test/res/values-ms-rMY/strings.xml b/carousel/test/res/values-ms-rMY/strings.xml
new file mode 100644
index 0000000..ab9381a
--- /dev/null
+++ b/carousel/test/res/values-ms-rMY/strings.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/*
+* 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="music_demo_activity_label" msgid="4382090808250495841">"MusicCarousel"</string>
+    <string name="carousel_test_activity_label" msgid="6014624482213318747">"CarouselTest"</string>
+    <string name="carousel_test_activity_description" msgid="1632693812604375483">"Satu aplikasi untuk menunjukkan penggunaan Carousel"</string>
+    <string name="task_switcher_activity_label" msgid="714620143340933546">"TaskSwitcher"</string>
+    <string name="recent_tasks_title" msgid="1030287226205477117">"Aplikasi Terbaru"</string>
+    <string name="no_recent_tasks" msgid="6884096266670555780">"Tiada tugasan terbaru"</string>
+</resources>
diff --git a/carousel/test/res/values-nb/strings.xml b/carousel/test/res/values-nb/strings.xml
index 8cddca0..751baf9 100644
--- a/carousel/test/res/values-nb/strings.xml
+++ b/carousel/test/res/values-nb/strings.xml
@@ -21,8 +21,8 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="music_demo_activity_label" msgid="4382090808250495841">"MusicCarousel"</string>
     <string name="carousel_test_activity_label" msgid="6014624482213318747">"CarouselTest"</string>
-    <string name="carousel_test_activity_description" msgid="1632693812604375483">"En applikasjon som viser bruken av Carousel"</string>
+    <string name="carousel_test_activity_description" msgid="1632693812604375483">"En app som viser bruken av Carousel"</string>
     <string name="task_switcher_activity_label" msgid="714620143340933546">"TaskSwitcher"</string>
-    <string name="recent_tasks_title" msgid="1030287226205477117">"Nylige applikasjoner"</string>
+    <string name="recent_tasks_title" msgid="1030287226205477117">"Nylige apper"</string>
     <string name="no_recent_tasks" msgid="6884096266670555780">"Ingen nylige oppgaver"</string>
 </resources>
diff --git a/carousel/test/res/values-ne-rNP/strings.xml b/carousel/test/res/values-ne-rNP/strings.xml
new file mode 100644
index 0000000..838502f
--- /dev/null
+++ b/carousel/test/res/values-ne-rNP/strings.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/*
+* 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="music_demo_activity_label" msgid="4382090808250495841">"संगीत करउसेल"</string>
+    <string name="carousel_test_activity_label" msgid="6014624482213318747">"करउसेल परीक्षण"</string>
+    <string name="carousel_test_activity_description" msgid="1632693812604375483">"कारउसेलको प्रयोग देखाउन एउटा अनुप्रयोग"</string>
+    <string name="task_switcher_activity_label" msgid="714620143340933546">"कार्य स्विच गर्ने"</string>
+    <string name="recent_tasks_title" msgid="1030287226205477117">"भर्खरैका अनुप्रयोगहरू"</string>
+    <string name="no_recent_tasks" msgid="6884096266670555780">"कुनै भरखरका कार्यहरू छैनन्।"</string>
+</resources>
diff --git a/carousel/test/res/values-ne/strings.xml b/carousel/test/res/values-ne/strings.xml
new file mode 100644
index 0000000..838502f
--- /dev/null
+++ b/carousel/test/res/values-ne/strings.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/*
+* 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="music_demo_activity_label" msgid="4382090808250495841">"संगीत करउसेल"</string>
+    <string name="carousel_test_activity_label" msgid="6014624482213318747">"करउसेल परीक्षण"</string>
+    <string name="carousel_test_activity_description" msgid="1632693812604375483">"कारउसेलको प्रयोग देखाउन एउटा अनुप्रयोग"</string>
+    <string name="task_switcher_activity_label" msgid="714620143340933546">"कार्य स्विच गर्ने"</string>
+    <string name="recent_tasks_title" msgid="1030287226205477117">"भर्खरैका अनुप्रयोगहरू"</string>
+    <string name="no_recent_tasks" msgid="6884096266670555780">"कुनै भरखरका कार्यहरू छैनन्।"</string>
+</resources>
diff --git a/carousel/test/res/values-si-rLK/strings.xml b/carousel/test/res/values-si-rLK/strings.xml
new file mode 100644
index 0000000..5a1aa48
--- /dev/null
+++ b/carousel/test/res/values-si-rLK/strings.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/*
+* 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="music_demo_activity_label" msgid="4382090808250495841">"සංගීත Carousel"</string>
+    <string name="carousel_test_activity_label" msgid="6014624482213318747">"Carousel පරීක්ෂණය"</string>
+    <string name="carousel_test_activity_description" msgid="1632693812604375483">"Carousel භාවිතය පෙන්වීමට යෙදුමකි"</string>
+    <string name="task_switcher_activity_label" msgid="714620143340933546">"කාර්ය ස්විචය"</string>
+    <string name="recent_tasks_title" msgid="1030287226205477117">"මෑත කාලීන යෙදුම්"</string>
+    <string name="no_recent_tasks" msgid="6884096266670555780">"මෑත කාර්යයන් නැත"</string>
+</resources>
diff --git a/carousel/test/res/values-si/strings.xml b/carousel/test/res/values-si/strings.xml
new file mode 100644
index 0000000..5a1aa48
--- /dev/null
+++ b/carousel/test/res/values-si/strings.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/*
+* 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="music_demo_activity_label" msgid="4382090808250495841">"සංගීත Carousel"</string>
+    <string name="carousel_test_activity_label" msgid="6014624482213318747">"Carousel පරීක්ෂණය"</string>
+    <string name="carousel_test_activity_description" msgid="1632693812604375483">"Carousel භාවිතය පෙන්වීමට යෙදුමකි"</string>
+    <string name="task_switcher_activity_label" msgid="714620143340933546">"කාර්ය ස්විචය"</string>
+    <string name="recent_tasks_title" msgid="1030287226205477117">"මෑත කාලීන යෙදුම්"</string>
+    <string name="no_recent_tasks" msgid="6884096266670555780">"මෑත කාර්යයන් නැත"</string>
+</resources>
diff --git a/carousel/test/res/values-zh-rHK/strings.xml b/carousel/test/res/values-zh-rHK/strings.xml
new file mode 100644
index 0000000..193cb4a
--- /dev/null
+++ b/carousel/test/res/values-zh-rHK/strings.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+/*
+* 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="music_demo_activity_label" msgid="4382090808250495841">"MusicCarousel"</string>
+    <string name="carousel_test_activity_label" msgid="6014624482213318747">"CarouselTest"</string>
+    <string name="carousel_test_activity_description" msgid="1632693812604375483">"展示輪轉使用方法的應用程式"</string>
+    <string name="task_switcher_activity_label" msgid="714620143340933546">"TaskSwitcher"</string>
+    <string name="recent_tasks_title" msgid="1030287226205477117">"最近使用的應用程式"</string>
+    <string name="no_recent_tasks" msgid="6884096266670555780">"最近沒有任務"</string>
+</resources>
diff --git a/carousel/test/src/com/android/carouseltest/TaskSwitcherActivity.java b/carousel/test/src/com/android/carouseltest/TaskSwitcherActivity.java
index d6c4829..0a76ecc 100644
--- a/carousel/test/src/com/android/carouseltest/TaskSwitcherActivity.java
+++ b/carousel/test/src/com/android/carouseltest/TaskSwitcherActivity.java
@@ -164,37 +164,6 @@
         }
     };
 
-    private final IThumbnailReceiver mThumbnailReceiver = new IThumbnailReceiver.Stub() {
-
-        public void finished() throws RemoteException {
-
-        }
-
-        public void newThumbnail(final int id, final Bitmap bitmap, CharSequence description)
-                throws RemoteException {
-            int w = bitmap.getWidth();
-            int h = bitmap.getHeight();
-            Log.v(TAG, "New thumbnail for id=" + id + ", dimensions=" + w + "x" + h
-                    + " description '" + description + "'");
-            ActivityDescription info = findActivityDescription(id);
-            if (info != null) {
-                info.thumbnail = bitmap;
-                final int thumbWidth = bitmap.getWidth();
-                final int thumbHeight = bitmap.getHeight();
-                if ((mPortraitMode && thumbWidth > thumbHeight)
-                        || (!mPortraitMode && thumbWidth < thumbHeight)) {
-                    Matrix matrix = new Matrix();
-                    matrix.setRotate(90.0f, (float) thumbWidth / 2, (float) thumbHeight / 2);
-                    info.matrix = matrix;
-                } else {
-                    info.matrix = null;
-                }
-            } else {
-                Log.v(TAG, "Can't find view for id " + id);
-            }
-        }
-    };
-
     @Override
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
@@ -243,7 +212,7 @@
     }
 
     void updateRunningTasks() {
-        mRunningTaskList = mActivityManager.getRunningTasks(MAX_TASKS + 2, 0, mThumbnailReceiver);
+        mRunningTaskList = mActivityManager.getRunningTasks(MAX_TASKS + 2);
         Log.v(TAG, "Portrait: " + mPortraitMode);
         for (RunningTaskInfo r : mRunningTaskList) {
             if (r.thumbnail != null) {
diff --git a/chips/Android.mk b/chips/Android.mk
deleted file mode 100644
index 2bca597..0000000
--- a/chips/Android.mk
+++ /dev/null
@@ -1,30 +0,0 @@
-# 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.
-
-LOCAL_PATH := $(call my-dir)
-
-include $(CLEAR_VARS)
-LOCAL_MODULE := android-common-chips
-LOCAL_STATIC_JAVA_LIBRARIES += android-support-v4
-LOCAL_SDK_VERSION := 19
-LOCAL_SRC_FILES := \
-     $(call all-java-files-under, src) \
-     $(call all-logtags-files-under, src)
-LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
-include $(BUILD_STATIC_JAVA_LIBRARY)
-
-##################################################
-# Build all sub-directories
-
-include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/chips/AndroidManifest.xml b/chips/AndroidManifest.xml
deleted file mode 100644
index 02ea564..0000000
--- a/chips/AndroidManifest.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
-          package="com.android.ex.chips"
-          android:versionCode="1">
-
-    <uses-sdk
-        android:minSdkVersion="11"
-        android:targetSdkVersion="19" />
-
-</manifest>
\ No newline at end of file
diff --git a/chips/project.properties b/chips/project.properties
deleted file mode 100644
index 91d2b02..0000000
--- a/chips/project.properties
+++ /dev/null
@@ -1,15 +0,0 @@
-# This file is automatically generated by Android Tools.
-# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
-#
-# This file must be checked in Version Control Systems.
-#
-# To customize properties used by the Ant build system edit
-# "ant.properties", and override values to adapt the script to your
-# project structure.
-#
-# To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home):
-#proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt
-
-# Project target.
-target=android-19
-android.library=true
diff --git a/chips/res/drawable-hdpi/chip_background.9.png b/chips/res/drawable-hdpi/chip_background.9.png
deleted file mode 100644
index 3988da5..0000000
--- a/chips/res/drawable-hdpi/chip_background.9.png
+++ /dev/null
Binary files differ
diff --git a/chips/res/drawable-hdpi/chip_background_invalid.9.png b/chips/res/drawable-hdpi/chip_background_invalid.9.png
deleted file mode 100644
index 01a3d95..0000000
--- a/chips/res/drawable-hdpi/chip_background_invalid.9.png
+++ /dev/null
Binary files differ
diff --git a/chips/res/drawable-hdpi/chip_background_selected.9.png b/chips/res/drawable-hdpi/chip_background_selected.9.png
deleted file mode 100644
index e05657f..0000000
--- a/chips/res/drawable-hdpi/chip_background_selected.9.png
+++ /dev/null
Binary files differ
diff --git a/chips/res/drawable-hdpi/chip_delete.png b/chips/res/drawable-hdpi/chip_delete.png
deleted file mode 100644
index 75cde4a..0000000
--- a/chips/res/drawable-hdpi/chip_delete.png
+++ /dev/null
Binary files differ
diff --git a/chips/res/drawable-hdpi/ic_contact_picture.png b/chips/res/drawable-hdpi/ic_contact_picture.png
deleted file mode 100644
index 4c0e35e..0000000
--- a/chips/res/drawable-hdpi/ic_contact_picture.png
+++ /dev/null
Binary files differ
diff --git a/chips/res/drawable-mdpi/chip_background.9.png b/chips/res/drawable-mdpi/chip_background.9.png
deleted file mode 100644
index 99fd037..0000000
--- a/chips/res/drawable-mdpi/chip_background.9.png
+++ /dev/null
Binary files differ
diff --git a/chips/res/drawable-mdpi/chip_background_invalid.9.png b/chips/res/drawable-mdpi/chip_background_invalid.9.png
deleted file mode 100644
index f90bec5..0000000
--- a/chips/res/drawable-mdpi/chip_background_invalid.9.png
+++ /dev/null
Binary files differ
diff --git a/chips/res/drawable-mdpi/chip_background_selected.9.png b/chips/res/drawable-mdpi/chip_background_selected.9.png
deleted file mode 100644
index 308fa03..0000000
--- a/chips/res/drawable-mdpi/chip_background_selected.9.png
+++ /dev/null
Binary files differ
diff --git a/chips/res/drawable-mdpi/chip_delete.png b/chips/res/drawable-mdpi/chip_delete.png
deleted file mode 100644
index 75cde4a..0000000
--- a/chips/res/drawable-mdpi/chip_delete.png
+++ /dev/null
Binary files differ
diff --git a/chips/res/drawable-mdpi/ic_contact_picture.png b/chips/res/drawable-mdpi/ic_contact_picture.png
deleted file mode 100644
index ead9718..0000000
--- a/chips/res/drawable-mdpi/ic_contact_picture.png
+++ /dev/null
Binary files differ
diff --git a/chips/res/drawable-xhdpi/chip_background.9.png b/chips/res/drawable-xhdpi/chip_background.9.png
deleted file mode 100644
index 72b0f22..0000000
--- a/chips/res/drawable-xhdpi/chip_background.9.png
+++ /dev/null
Binary files differ
diff --git a/chips/res/drawable-xhdpi/chip_background_invalid.9.png b/chips/res/drawable-xhdpi/chip_background_invalid.9.png
deleted file mode 100644
index a9195ea..0000000
--- a/chips/res/drawable-xhdpi/chip_background_invalid.9.png
+++ /dev/null
Binary files differ
diff --git a/chips/res/drawable-xhdpi/chip_background_selected.9.png b/chips/res/drawable-xhdpi/chip_background_selected.9.png
deleted file mode 100644
index abed86a..0000000
--- a/chips/res/drawable-xhdpi/chip_background_selected.9.png
+++ /dev/null
Binary files differ
diff --git a/chips/res/drawable-xhdpi/ic_contact_picture.png b/chips/res/drawable-xhdpi/ic_contact_picture.png
deleted file mode 100644
index 05a65f6..0000000
--- a/chips/res/drawable-xhdpi/ic_contact_picture.png
+++ /dev/null
Binary files differ
diff --git a/chips/res/drawable/list_item_font_primary.xml b/chips/res/drawable/list_item_font_primary.xml
deleted file mode 100644
index 4351905..0000000
--- a/chips/res/drawable/list_item_font_primary.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
-
- <selector xmlns:android="http://schemas.android.com/apk/res/android">
-   <item android:state_activated="true" android:color="@android:color/white" />
-   <item android:state_checked="true" android:color="@android:color/white" />
-   <item android:state_selected="true" android:color="@android:color/white" />
-   <item android:color="#333333"/>
- </selector>
\ No newline at end of file
diff --git a/chips/res/drawable/list_item_font_secondary.xml b/chips/res/drawable/list_item_font_secondary.xml
deleted file mode 100644
index 78c4066..0000000
--- a/chips/res/drawable/list_item_font_secondary.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
-
- <selector xmlns:android="http://schemas.android.com/apk/res/android">
-   <item android:state_activated="true" android:color="@android:color/white" />
-   <item android:state_checked="true" android:color="@android:color/white" />
-   <item android:state_selected="true" android:color="@android:color/white" />
-   <item android:color="#777777"/>
- </selector>
\ No newline at end of file
diff --git a/chips/res/layout/chips_alternate_item.xml b/chips/res/layout/chips_alternate_item.xml
deleted file mode 100644
index 9bc7b6d..0000000
--- a/chips/res/layout/chips_alternate_item.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
-
-<FrameLayout  xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_width="match_parent"
-    android:layout_height="wrap_content">
-
-    <include layout="@layout/chips_recipient_dropdown_item"/>
-
-</FrameLayout>
diff --git a/chips/res/layout/chips_recipient_dropdown_item.xml b/chips/res/layout/chips_recipient_dropdown_item.xml
deleted file mode 100644
index b02b197..0000000
--- a/chips/res/layout/chips_recipient_dropdown_item.xml
+++ /dev/null
@@ -1,57 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
-
-<LinearLayout
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_width="match_parent"
-    android:layout_height="wrap_content"
-    android:minHeight="48dip"
-    android:orientation="horizontal"
-    android:gravity="center_vertical"
-    android:background="?android:attr/activatedBackgroundIndicator">
-    <LinearLayout
-        android:layout_width="0dip"
-        android:layout_height="wrap_content"
-        android:gravity="center_vertical"
-        android:orientation="vertical"
-        android:layout_weight="1">
-        <TextView android:id="@android:id/title"
-                  android:textColor="@drawable/list_item_font_primary"
-                  android:textSize="18sp"
-                  android:layout_width="wrap_content"
-                  android:layout_height="wrap_content"
-                  android:singleLine="true"
-                  android:ellipsize="end"
-                  style="@style/ChipTitleStyle" />
-        <TextView android:id="@android:id/text1"
-                  android:textColor="@drawable/list_item_font_secondary"
-                  android:textSize="14sp"
-                  android:layout_width="wrap_content"
-                  android:layout_height="wrap_content"
-                  android:singleLine="true"
-                  android:ellipsize="end"
-                  android:layout_marginTop="-4dip"
-                  style="@style/ChipSubtitleStyle" />
-    </LinearLayout>
-    <ImageView
-        android:id="@android:id/icon"
-        android:layout_width="48dip"
-        android:layout_height="48dip"
-        android:src="@drawable/ic_contact_picture"
-        android:cropToPadding="true"
-        android:scaleType="centerCrop"
-        style="@style/ChipIconStyle" />
-</LinearLayout>
diff --git a/chips/res/layout/copy_chip_dialog_layout.xml b/chips/res/layout/copy_chip_dialog_layout.xml
deleted file mode 100644
index f131626..0000000
--- a/chips/res/layout/copy_chip_dialog_layout.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
-
-<Button xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_width="match_parent"
-    android:layout_height="wrap_content"
-    android:text="@string/copy_email"
-    android:id="@+android:id/button1"
-    android:background="@null"
-    android:layout_gravity="left"/>
diff --git a/chips/res/layout/more_item.xml b/chips/res/layout/more_item.xml
deleted file mode 100644
index 20693d6..0000000
--- a/chips/res/layout/more_item.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
-<TextView  xmlns:android="http://schemas.android.com/apk/res/android"
-    android:id="@+id/text1"
-    android:textColor="#aaaaaa"
-    android:textSize="18sp"
-    android:layout_width="wrap_content"
-    android:layout_height="wrap_content"
-    android:text="@string/more_string"
-    android:paddingLeft="8dip"
-    android:paddingRight="8dip"
-    />
\ No newline at end of file
diff --git a/chips/res/values-af/strings.xml b/chips/res/values-af/strings.xml
deleted file mode 100644
index 3ccbcba..0000000
--- a/chips/res/values-af/strings.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--  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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="more_string" msgid="8495478259330621990">"+<xliff:g id="COUNT">%1$s</xliff:g>"</string>
-    <string name="copy_email" msgid="7869435992461603532">"Kopieer e-posadres"</string>
-    <string name="copy_number" msgid="530057841276106843">"Kopieer foonnommer"</string>
-    <string name="done" msgid="2356320650733788862">"Terugkeer"</string>
-</resources>
diff --git a/chips/res/values-am/strings.xml b/chips/res/values-am/strings.xml
deleted file mode 100644
index 0537868..0000000
--- a/chips/res/values-am/strings.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--  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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="more_string" msgid="8495478259330621990">"+<xliff:g id="COUNT">%1$s</xliff:g>"</string>
-    <string name="copy_email" msgid="7869435992461603532">"የኢሜይል አድራሻ ቅዳ"</string>
-    <string name="copy_number" msgid="530057841276106843">"የስልክ ቁጥር ቅዳ"</string>
-    <string name="done" msgid="2356320650733788862">"መልስ"</string>
-</resources>
diff --git a/chips/res/values-ar/strings.xml b/chips/res/values-ar/strings.xml
deleted file mode 100644
index 3398641..0000000
--- a/chips/res/values-ar/strings.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--  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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="more_string" msgid="8495478259330621990">"<xliff:g id="COUNT">%1$s</xliff:g>+"</string>
-    <string name="copy_email" msgid="7869435992461603532">"نسخ عنوان البريد الإلكتروني"</string>
-    <string name="copy_number" msgid="530057841276106843">"نسخ رقم الهاتف"</string>
-    <string name="done" msgid="2356320650733788862">"رجوع"</string>
-</resources>
diff --git a/chips/res/values-bg/strings.xml b/chips/res/values-bg/strings.xml
deleted file mode 100644
index 06ed49e..0000000
--- a/chips/res/values-bg/strings.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--  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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="more_string" msgid="8495478259330621990">"+ <xliff:g id="COUNT">%1$s</xliff:g>"</string>
-    <string name="copy_email" msgid="7869435992461603532">"Копиране на имейл адреса"</string>
-    <string name="copy_number" msgid="530057841276106843">"Копиране на телефонния номер"</string>
-    <string name="done" msgid="2356320650733788862">"Enter"</string>
-</resources>
diff --git a/chips/res/values-ca/strings.xml b/chips/res/values-ca/strings.xml
deleted file mode 100644
index 77d2196..0000000
--- a/chips/res/values-ca/strings.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--  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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="more_string" msgid="8495478259330621990">"+<xliff:g id="COUNT">%1$s</xliff:g>"</string>
-    <string name="copy_email" msgid="7869435992461603532">"Copia l\'adreça electrònica"</string>
-    <string name="copy_number" msgid="530057841276106843">"Copia el número de telèfon"</string>
-    <string name="done" msgid="2356320650733788862">"Retorn"</string>
-</resources>
diff --git a/chips/res/values-cs/strings.xml b/chips/res/values-cs/strings.xml
deleted file mode 100644
index 212d734..0000000
--- a/chips/res/values-cs/strings.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--  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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="more_string" msgid="8495478259330621990">"+<xliff:g id="COUNT">%1$s</xliff:g>"</string>
-    <string name="copy_email" msgid="7869435992461603532">"Kopírovat e-mailovou adresu"</string>
-    <string name="copy_number" msgid="530057841276106843">"Kopírovat telefonní číslo"</string>
-    <string name="done" msgid="2356320650733788862">"Enter"</string>
-</resources>
diff --git a/chips/res/values-da/strings.xml b/chips/res/values-da/strings.xml
deleted file mode 100644
index 8196fee..0000000
--- a/chips/res/values-da/strings.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--  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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="more_string" msgid="8495478259330621990">"+<xliff:g id="COUNT">%1$s</xliff:g>"</string>
-    <string name="copy_email" msgid="7869435992461603532">"Kopiér e-mailadressen"</string>
-    <string name="copy_number" msgid="530057841276106843">"Kopiér telefonnummeret"</string>
-    <string name="done" msgid="2356320650733788862">"Tilbage"</string>
-</resources>
diff --git a/chips/res/values-de/strings.xml b/chips/res/values-de/strings.xml
deleted file mode 100644
index d4c2fe4..0000000
--- a/chips/res/values-de/strings.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--  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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="more_string" msgid="8495478259330621990">"+<xliff:g id="COUNT">%1$s</xliff:g>"</string>
-    <string name="copy_email" msgid="7869435992461603532">"E-Mail-Adresse kopieren"</string>
-    <string name="copy_number" msgid="530057841276106843">"Telefonnummer kopieren"</string>
-    <string name="done" msgid="2356320650733788862">"Eingabe"</string>
-</resources>
diff --git a/chips/res/values-el/strings.xml b/chips/res/values-el/strings.xml
deleted file mode 100644
index 51b5ac3..0000000
--- a/chips/res/values-el/strings.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--  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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="more_string" msgid="8495478259330621990">"+<xliff:g id="COUNT">%1$s</xliff:g>"</string>
-    <string name="copy_email" msgid="7869435992461603532">"Αντιγραφή διεύθυνσης ηλεκτρονικού ταχυδρομείου"</string>
-    <string name="copy_number" msgid="530057841276106843">"Αντιγραφή αριθμού τηλεφώνου"</string>
-    <string name="done" msgid="2356320650733788862">"Πλήκτρο Return"</string>
-</resources>
diff --git a/chips/res/values-en-rGB/strings.xml b/chips/res/values-en-rGB/strings.xml
deleted file mode 100644
index 1ae784b..0000000
--- a/chips/res/values-en-rGB/strings.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--  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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="more_string" msgid="8495478259330621990">"+<xliff:g id="COUNT">%1$s</xliff:g>"</string>
-    <string name="copy_email" msgid="7869435992461603532">"Copy email address"</string>
-    <string name="copy_number" msgid="530057841276106843">"Copy phone number"</string>
-    <string name="done" msgid="2356320650733788862">"Return"</string>
-</resources>
diff --git a/chips/res/values-es-rUS/strings.xml b/chips/res/values-es-rUS/strings.xml
deleted file mode 100644
index c63e6cb..0000000
--- a/chips/res/values-es-rUS/strings.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--  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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="more_string" msgid="8495478259330621990">"+<xliff:g id="COUNT">%1$s</xliff:g>"</string>
-    <string name="copy_email" msgid="7869435992461603532">"Copiar la dirección de correo"</string>
-    <string name="copy_number" msgid="530057841276106843">"Copiar el número de teléfono"</string>
-    <string name="done" msgid="2356320650733788862">"Volver"</string>
-</resources>
diff --git a/chips/res/values-es/strings.xml b/chips/res/values-es/strings.xml
deleted file mode 100644
index 74478a8..0000000
--- a/chips/res/values-es/strings.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--  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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="more_string" msgid="8495478259330621990">"+<xliff:g id="COUNT">%1$s</xliff:g>"</string>
-    <string name="copy_email" msgid="7869435992461603532">"Copiar dirección de correo electrónico"</string>
-    <string name="copy_number" msgid="530057841276106843">"Copiar número de teléfono"</string>
-    <string name="done" msgid="2356320650733788862">"Intro"</string>
-</resources>
diff --git a/chips/res/values-fa/strings.xml b/chips/res/values-fa/strings.xml
deleted file mode 100644
index e5b32ba..0000000
--- a/chips/res/values-fa/strings.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--  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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="more_string" msgid="8495478259330621990">"+<xliff:g id="COUNT">%1$s</xliff:g>"</string>
-    <string name="copy_email" msgid="7869435992461603532">"کپی آدرس ایمیل"</string>
-    <string name="copy_number" msgid="530057841276106843">"کپی شماره تلفن"</string>
-    <string name="done" msgid="2356320650733788862">"بازگشت"</string>
-</resources>
diff --git a/chips/res/values-fi/strings.xml b/chips/res/values-fi/strings.xml
deleted file mode 100644
index 9893923..0000000
--- a/chips/res/values-fi/strings.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--  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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="more_string" msgid="8495478259330621990">"yli <xliff:g id="COUNT">%1$s</xliff:g>"</string>
-    <string name="copy_email" msgid="7869435992461603532">"Kopioi sähköpostiosoite"</string>
-    <string name="copy_number" msgid="530057841276106843">"Kopioi puhelinnumero"</string>
-    <string name="done" msgid="2356320650733788862">"Enter"</string>
-</resources>
diff --git a/chips/res/values-fr/strings.xml b/chips/res/values-fr/strings.xml
deleted file mode 100644
index 3d13b5f..0000000
--- a/chips/res/values-fr/strings.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--  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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="more_string" msgid="8495478259330621990">"+ <xliff:g id="COUNT">%1$s</xliff:g>"</string>
-    <string name="copy_email" msgid="7869435992461603532">"Copier l\'adresse e-mail"</string>
-    <string name="copy_number" msgid="530057841276106843">"Copier le numéro de téléphone"</string>
-    <string name="done" msgid="2356320650733788862">"Entrée"</string>
-</resources>
diff --git a/chips/res/values-hi/strings.xml b/chips/res/values-hi/strings.xml
deleted file mode 100644
index 8368c0d..0000000
--- a/chips/res/values-hi/strings.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--  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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="more_string" msgid="8495478259330621990">"+<xliff:g id="COUNT">%1$s</xliff:g>"</string>
-    <string name="copy_email" msgid="7869435992461603532">"ईमेल पते की प्रतिलिपि बनाएं"</string>
-    <string name="copy_number" msgid="530057841276106843">"फोन नंबर की प्रतिलिपि बनाएं"</string>
-    <string name="done" msgid="2356320650733788862">"वापस लौटें"</string>
-</resources>
diff --git a/chips/res/values-hr/strings.xml b/chips/res/values-hr/strings.xml
deleted file mode 100644
index 65f2c12..0000000
--- a/chips/res/values-hr/strings.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--  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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="more_string" msgid="8495478259330621990">"+<xliff:g id="COUNT">%1$s</xliff:g>"</string>
-    <string name="copy_email" msgid="7869435992461603532">"Kopiranje e-adrese"</string>
-    <string name="copy_number" msgid="530057841276106843">"Kopiranje telefonskog broja"</string>
-    <string name="done" msgid="2356320650733788862">"Vrati"</string>
-</resources>
diff --git a/chips/res/values-hu/strings.xml b/chips/res/values-hu/strings.xml
deleted file mode 100644
index a18f811..0000000
--- a/chips/res/values-hu/strings.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--  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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="more_string" msgid="8495478259330621990">"+<xliff:g id="COUNT">%1$s</xliff:g>"</string>
-    <string name="copy_email" msgid="7869435992461603532">"E-mail cím másolása"</string>
-    <string name="copy_number" msgid="530057841276106843">"Telefonszám másolása"</string>
-    <string name="done" msgid="2356320650733788862">"Enter"</string>
-</resources>
diff --git a/chips/res/values-in/strings.xml b/chips/res/values-in/strings.xml
deleted file mode 100644
index 9c1cbe6..0000000
--- a/chips/res/values-in/strings.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--  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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="more_string" msgid="8495478259330621990">"+<xliff:g id="COUNT">%1$s</xliff:g>"</string>
-    <string name="copy_email" msgid="7869435992461603532">"Salin alamat email"</string>
-    <string name="copy_number" msgid="530057841276106843">"Salin nomor telepon"</string>
-    <string name="done" msgid="2356320650733788862">"Kembali"</string>
-</resources>
diff --git a/chips/res/values-it/strings.xml b/chips/res/values-it/strings.xml
deleted file mode 100644
index 3f87ec0..0000000
--- a/chips/res/values-it/strings.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--  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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="more_string" msgid="8495478259330621990">"+<xliff:g id="COUNT">%1$s</xliff:g>"</string>
-    <string name="copy_email" msgid="7869435992461603532">"Copia indirizzo email"</string>
-    <string name="copy_number" msgid="530057841276106843">"Copia numero di telefono"</string>
-    <string name="done" msgid="2356320650733788862">"Invio"</string>
-</resources>
diff --git a/chips/res/values-iw/strings.xml b/chips/res/values-iw/strings.xml
deleted file mode 100644
index d7be75a..0000000
--- a/chips/res/values-iw/strings.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--  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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="more_string" msgid="8495478259330621990">"+<xliff:g id="COUNT">%1$s</xliff:g>"</string>
-    <string name="copy_email" msgid="7869435992461603532">"העתק כתובת דוא\"ל"</string>
-    <string name="copy_number" msgid="530057841276106843">"העתק מספר טלפון"</string>
-    <string name="done" msgid="2356320650733788862">"חזור"</string>
-</resources>
diff --git a/chips/res/values-ja/strings.xml b/chips/res/values-ja/strings.xml
deleted file mode 100644
index 543eb8a..0000000
--- a/chips/res/values-ja/strings.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--  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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="more_string" msgid="8495478259330621990">"+<xliff:g id="COUNT">%1$s</xliff:g>"</string>
-    <string name="copy_email" msgid="7869435992461603532">"メールアドレスをコピー"</string>
-    <string name="copy_number" msgid="530057841276106843">"電話番号をコピー"</string>
-    <string name="done" msgid="2356320650733788862">"戻る"</string>
-</resources>
diff --git a/chips/res/values-ko/strings.xml b/chips/res/values-ko/strings.xml
deleted file mode 100644
index f7884bd..0000000
--- a/chips/res/values-ko/strings.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--  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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="more_string" msgid="8495478259330621990">"<xliff:g id="COUNT">%1$s</xliff:g>명 이상"</string>
-    <string name="copy_email" msgid="7869435992461603532">"이메일 주소 복사"</string>
-    <string name="copy_number" msgid="530057841276106843">"전화번호 복사"</string>
-    <string name="done" msgid="2356320650733788862">"입력"</string>
-</resources>
diff --git a/chips/res/values-land/dimen.xml b/chips/res/values-land/dimen.xml
deleted file mode 100644
index 1ee6608..0000000
--- a/chips/res/values-land/dimen.xml
+++ /dev/null
@@ -1,19 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
-
-<resources>
-    <dimen name="chip_height">26dip</dimen>
-</resources>
\ No newline at end of file
diff --git a/chips/res/values-lt/strings.xml b/chips/res/values-lt/strings.xml
deleted file mode 100644
index e85eba3..0000000
--- a/chips/res/values-lt/strings.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--  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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="more_string" msgid="8495478259330621990">"+ <xliff:g id="COUNT">%1$s</xliff:g>"</string>
-    <string name="copy_email" msgid="7869435992461603532">"Kopijuoti el. pašto adresą"</string>
-    <string name="copy_number" msgid="530057841276106843">"Kopijuoti telefono numerį"</string>
-    <string name="done" msgid="2356320650733788862">"Grįžti"</string>
-</resources>
diff --git a/chips/res/values-lv/strings.xml b/chips/res/values-lv/strings.xml
deleted file mode 100644
index f06e4fc..0000000
--- a/chips/res/values-lv/strings.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--  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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="more_string" msgid="8495478259330621990">"Vairāk nekā <xliff:g id="COUNT">%1$s</xliff:g>"</string>
-    <string name="copy_email" msgid="7869435992461603532">"Kopēt e-pasta adresi"</string>
-    <string name="copy_number" msgid="530057841276106843">"Kopēt tālruņa numuru"</string>
-    <string name="done" msgid="2356320650733788862">"Iev. taust."</string>
-</resources>
diff --git a/chips/res/values-nb/strings.xml b/chips/res/values-nb/strings.xml
deleted file mode 100644
index a71348e..0000000
--- a/chips/res/values-nb/strings.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--  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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="more_string" msgid="8495478259330621990">"+<xliff:g id="COUNT">%1$s</xliff:g>"</string>
-    <string name="copy_email" msgid="7869435992461603532">"Kopiér e-postadressen"</string>
-    <string name="copy_number" msgid="530057841276106843">"Kopiér telefonnummeret"</string>
-    <string name="done" msgid="2356320650733788862">"Gå tilbake"</string>
-</resources>
diff --git a/chips/res/values-nl/strings.xml b/chips/res/values-nl/strings.xml
deleted file mode 100644
index c4289c6..0000000
--- a/chips/res/values-nl/strings.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--  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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="more_string" msgid="8495478259330621990">"+<xliff:g id="COUNT">%1$s</xliff:g>"</string>
-    <string name="copy_email" msgid="7869435992461603532">"E-mailadres kopiëren"</string>
-    <string name="copy_number" msgid="530057841276106843">"Telefoonnummer kopiëren"</string>
-    <string name="done" msgid="2356320650733788862">"Return"</string>
-</resources>
diff --git a/chips/res/values-pl/strings.xml b/chips/res/values-pl/strings.xml
deleted file mode 100644
index 8746e48..0000000
--- a/chips/res/values-pl/strings.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--  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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="more_string" msgid="8495478259330621990">"+<xliff:g id="COUNT">%1$s</xliff:g>"</string>
-    <string name="copy_email" msgid="7869435992461603532">"Kopiuj adres e-mail"</string>
-    <string name="copy_number" msgid="530057841276106843">"Kopiuj numer telefonu"</string>
-    <string name="done" msgid="2356320650733788862">"Enter"</string>
-</resources>
diff --git a/chips/res/values-pt-rPT/strings.xml b/chips/res/values-pt-rPT/strings.xml
deleted file mode 100644
index fc991b1..0000000
--- a/chips/res/values-pt-rPT/strings.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--  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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="more_string" msgid="8495478259330621990">"+<xliff:g id="COUNT">%1$s</xliff:g>"</string>
-    <string name="copy_email" msgid="7869435992461603532">"Copiar endereço de email"</string>
-    <string name="copy_number" msgid="530057841276106843">"Copiar número de telefone"</string>
-    <string name="done" msgid="2356320650733788862">"Voltar"</string>
-</resources>
diff --git a/chips/res/values-pt/strings.xml b/chips/res/values-pt/strings.xml
deleted file mode 100644
index 58a23e3..0000000
--- a/chips/res/values-pt/strings.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--  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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="more_string" msgid="8495478259330621990">"+<xliff:g id="COUNT">%1$s</xliff:g>"</string>
-    <string name="copy_email" msgid="7869435992461603532">"Copiar endereço de e-mail"</string>
-    <string name="copy_number" msgid="530057841276106843">"Copiar número de telefone"</string>
-    <string name="done" msgid="2356320650733788862">"Enter"</string>
-</resources>
diff --git a/chips/res/values-ro/strings.xml b/chips/res/values-ro/strings.xml
deleted file mode 100644
index 6bd8a36..0000000
--- a/chips/res/values-ro/strings.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--  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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="more_string" msgid="8495478259330621990">"+<xliff:g id="COUNT">%1$s</xliff:g>"</string>
-    <string name="copy_email" msgid="7869435992461603532">"Copiaţi adresa de e-mail"</string>
-    <string name="copy_number" msgid="530057841276106843">"Copiaţi numărul de telefon"</string>
-    <string name="done" msgid="2356320650733788862">"Enter"</string>
-</resources>
diff --git a/chips/res/values-ru/strings.xml b/chips/res/values-ru/strings.xml
deleted file mode 100644
index 0d6a2d7..0000000
--- a/chips/res/values-ru/strings.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--  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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="more_string" msgid="8495478259330621990">"ещё <xliff:g id="COUNT">%1$s</xliff:g>"</string>
-    <string name="copy_email" msgid="7869435992461603532">"Скопировать адрес эл. почты"</string>
-    <string name="copy_number" msgid="530057841276106843">"Скопировать номер телефона"</string>
-    <string name="done" msgid="2356320650733788862">"Назад"</string>
-</resources>
diff --git a/chips/res/values-sk/strings.xml b/chips/res/values-sk/strings.xml
deleted file mode 100644
index 155da99..0000000
--- a/chips/res/values-sk/strings.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--  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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="more_string" msgid="8495478259330621990">"+<xliff:g id="COUNT">%1$s</xliff:g>"</string>
-    <string name="copy_email" msgid="7869435992461603532">"Kopírovať e-mailovú adresu"</string>
-    <string name="copy_number" msgid="530057841276106843">"Kopírovať telefónne číslo"</string>
-    <string name="done" msgid="2356320650733788862">"Enter"</string>
-</resources>
diff --git a/chips/res/values-sl/strings.xml b/chips/res/values-sl/strings.xml
deleted file mode 100644
index e9877dd..0000000
--- a/chips/res/values-sl/strings.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--  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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="more_string" msgid="8495478259330621990">"+<xliff:g id="COUNT">%1$s</xliff:g>"</string>
-    <string name="copy_email" msgid="7869435992461603532">"Kopiranje e-poštnega naslova"</string>
-    <string name="copy_number" msgid="530057841276106843">"Kopiranje telefonske številke"</string>
-    <string name="done" msgid="2356320650733788862">"Vračalka"</string>
-</resources>
diff --git a/chips/res/values-sr/strings.xml b/chips/res/values-sr/strings.xml
deleted file mode 100644
index 578ca42..0000000
--- a/chips/res/values-sr/strings.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--  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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="more_string" msgid="8495478259330621990">"+<xliff:g id="COUNT">%1$s</xliff:g>"</string>
-    <string name="copy_email" msgid="7869435992461603532">"Копирај адресу е-поште"</string>
-    <string name="copy_number" msgid="530057841276106843">"Копирај број телефона"</string>
-    <string name="done" msgid="2356320650733788862">"Врати"</string>
-</resources>
diff --git a/chips/res/values-sv/strings.xml b/chips/res/values-sv/strings.xml
deleted file mode 100644
index a2a9f40..0000000
--- a/chips/res/values-sv/strings.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--  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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="more_string" msgid="8495478259330621990">"+<xliff:g id="COUNT">%1$s</xliff:g>"</string>
-    <string name="copy_email" msgid="7869435992461603532">"Kopiera e-postadress"</string>
-    <string name="copy_number" msgid="530057841276106843">"Kopiera telefonnummer"</string>
-    <string name="done" msgid="2356320650733788862">"Retur"</string>
-</resources>
diff --git a/chips/res/values-sw/strings.xml b/chips/res/values-sw/strings.xml
deleted file mode 100644
index edea133..0000000
--- a/chips/res/values-sw/strings.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--  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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="more_string" msgid="8495478259330621990">"+<xliff:g id="COUNT">%1$s</xliff:g>"</string>
-    <string name="copy_email" msgid="7869435992461603532">"Nakili anwani ya barua pepe"</string>
-    <string name="copy_number" msgid="530057841276106843">"Nakili namba ya simu"</string>
-    <string name="done" msgid="2356320650733788862">"Inayofuata"</string>
-</resources>
diff --git a/chips/res/values-sw600dp-land/dimen.xml b/chips/res/values-sw600dp-land/dimen.xml
deleted file mode 100644
index dc3aadf..0000000
--- a/chips/res/values-sw600dp-land/dimen.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
-
-<resources>
-    <dimen name="chip_padding">8dip</dimen>
-    <dimen name="chip_height">32dip</dimen>
-    <dimen name="chip_text_size">14sp</dimen>
-</resources>
\ No newline at end of file
diff --git a/chips/res/values-sw600dp/styles.xml b/chips/res/values-sw600dp/styles.xml
deleted file mode 100644
index 00988a9..0000000
--- a/chips/res/values-sw600dp/styles.xml
+++ /dev/null
@@ -1,32 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
-<resources xmlns:tools="http://schemas.android.com/tools"
-           xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <style name="RecipientEditTextView" parent="@android:attr/autoCompleteTextViewStyle">
-        <item name="android:paddingLeft">8dip</item>
-        <item name="android:paddingRight">4dip</item>
-        <item name="android:inputType">textEmailAddress|textMultiLine</item>
-        <item name="android:imeOptions">actionNext</item>
-        <item name="android:textAppearance">?android:attr/textAppearanceMedium</item>
-        <item name="android:background">@null</item>
-        <item name="android:layout_height">wrap_content</item>
-        <item name="android:layout_width">match_parent</item>
-        <item name="android:dropDownVerticalOffset">0dip</item>
-        <item name="android:dropDownHorizontalOffset">-4dip</item>
-        <item name="android:textAlignment" tools:ignore="NewApi">viewStart</item>
-        <item name="android:textDirection" tools:ignore="NewApi">locale</item>
-    </style>
-</resources>
diff --git a/chips/res/values-th/strings.xml b/chips/res/values-th/strings.xml
deleted file mode 100644
index fffafd0..0000000
--- a/chips/res/values-th/strings.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--  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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="more_string" msgid="8495478259330621990">"+<xliff:g id="COUNT">%1$s</xliff:g>"</string>
-    <string name="copy_email" msgid="7869435992461603532">"คัดลอกที่อยู่อีเมล"</string>
-    <string name="copy_number" msgid="530057841276106843">"คัดลอกหมายเลขโทรศัพท์"</string>
-    <string name="done" msgid="2356320650733788862">"ส่งคืน"</string>
-</resources>
diff --git a/chips/res/values-tl/strings.xml b/chips/res/values-tl/strings.xml
deleted file mode 100644
index db846ca..0000000
--- a/chips/res/values-tl/strings.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--  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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="more_string" msgid="8495478259330621990">"+<xliff:g id="COUNT">%1$s</xliff:g>"</string>
-    <string name="copy_email" msgid="7869435992461603532">"Kopyahin ang email address"</string>
-    <string name="copy_number" msgid="530057841276106843">"Kopyahin ang numero ng telepono"</string>
-    <string name="done" msgid="2356320650733788862">"Bumalik"</string>
-</resources>
diff --git a/chips/res/values-tr/strings.xml b/chips/res/values-tr/strings.xml
deleted file mode 100644
index 1e099a4..0000000
--- a/chips/res/values-tr/strings.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--  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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="more_string" msgid="8495478259330621990">"+<xliff:g id="COUNT">%1$s</xliff:g>"</string>
-    <string name="copy_email" msgid="7869435992461603532">"E-posta adresini kopyala"</string>
-    <string name="copy_number" msgid="530057841276106843">"Telefon numarasını kopyala"</string>
-    <string name="done" msgid="2356320650733788862">"Enter"</string>
-</resources>
diff --git a/chips/res/values-uk/strings.xml b/chips/res/values-uk/strings.xml
deleted file mode 100644
index 820183e..0000000
--- a/chips/res/values-uk/strings.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--  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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="more_string" msgid="8495478259330621990">"+<xliff:g id="COUNT">%1$s</xliff:g>"</string>
-    <string name="copy_email" msgid="7869435992461603532">"Копіювати електронну адресу"</string>
-    <string name="copy_number" msgid="530057841276106843">"Копіювати номер телефону"</string>
-    <string name="done" msgid="2356320650733788862">"Return"</string>
-</resources>
diff --git a/chips/res/values-v17/styles-v17.xml b/chips/res/values-v17/styles-v17.xml
deleted file mode 100644
index d151a75..0000000
--- a/chips/res/values-v17/styles-v17.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2014 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>
-    <style name="ChipTitleStyle">
-        <item name="android:paddingStart">@dimen/chip_title_padding_start</item>
-    </style>
-
-    <style name="ChipSubtitleStyle">
-        <item name="android:paddingStart">@dimen/chip_subtitle_padding_start</item>
-    </style>
-
-    <style name="ChipIconStyle">
-        <item name="android:layout_marginStart">@dimen/chip_icon_margin_start</item>
-    </style>
-</resources>
diff --git a/chips/res/values-vi/strings.xml b/chips/res/values-vi/strings.xml
deleted file mode 100644
index f42d837..0000000
--- a/chips/res/values-vi/strings.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--  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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="more_string" msgid="8495478259330621990">"+<xliff:g id="COUNT">%1$s</xliff:g>"</string>
-    <string name="copy_email" msgid="7869435992461603532">"Sao chép địa chỉ email"</string>
-    <string name="copy_number" msgid="530057841276106843">"Sao chép số điện thoại"</string>
-    <string name="done" msgid="2356320650733788862">"Quay lại"</string>
-</resources>
diff --git a/chips/res/values-zh-rCN/strings.xml b/chips/res/values-zh-rCN/strings.xml
deleted file mode 100644
index 2283f75..0000000
--- a/chips/res/values-zh-rCN/strings.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--  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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="more_string" msgid="8495478259330621990">"+<xliff:g id="COUNT">%1$s</xliff:g>"</string>
-    <string name="copy_email" msgid="7869435992461603532">"复制电子邮件地址"</string>
-    <string name="copy_number" msgid="530057841276106843">"复制电话号码"</string>
-    <string name="done" msgid="2356320650733788862">"上一步"</string>
-</resources>
diff --git a/chips/res/values-zh-rTW/strings.xml b/chips/res/values-zh-rTW/strings.xml
deleted file mode 100644
index 62d71cf..0000000
--- a/chips/res/values-zh-rTW/strings.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--  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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="more_string" msgid="8495478259330621990">"+<xliff:g id="COUNT">%1$s</xliff:g> 人"</string>
-    <string name="copy_email" msgid="7869435992461603532">"複製電子郵件地址"</string>
-    <string name="copy_number" msgid="530057841276106843">"複製電話號碼"</string>
-    <string name="done" msgid="2356320650733788862">"返回"</string>
-</resources>
diff --git a/chips/res/values-zu/strings.xml b/chips/res/values-zu/strings.xml
deleted file mode 100644
index 9ae03ed..0000000
--- a/chips/res/values-zu/strings.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--  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.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="more_string" msgid="8495478259330621990">"+<xliff:g id="COUNT">%1$s</xliff:g>"</string>
-    <string name="copy_email" msgid="7869435992461603532">"Kopisha ikheli le-imeyli"</string>
-    <string name="copy_number" msgid="530057841276106843">"Kopisha inombolo yefoni"</string>
-    <string name="done" msgid="2356320650733788862">"Buyela"</string>
-</resources>
diff --git a/chips/res/values/attrs.xml b/chips/res/values/attrs.xml
deleted file mode 100644
index d3500aa..0000000
--- a/chips/res/values/attrs.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
-<resources>
-    <declare-styleable name="RecipientEditTextView">
-        <attr name="avatarPosition">
-            <enum name="end" value="0" />
-            <enum name="start" value="1" />
-        </attr>
-        <attr name="chipBackground" format="reference" />
-        <attr name="chipBackgroundPressed" format="reference" />
-        <attr name="chipDelete" format="reference" />
-        <attr name="chipFontSize" format="reference" />
-        <attr name="chipHeight" format="reference" />
-        <attr name="chipPadding" format="reference" />
-        <attr name="disableDelete" format="boolean" />
-        <attr name="invalidChipBackground" format="reference" />
-        <attr name="imageSpanAlignment">
-            <enum name="bottom" value = "0"/>
-            <enum name="baseline" value = "1"/>
-        </attr>
-    </declare-styleable>
-</resources>
\ No newline at end of file
diff --git a/chips/res/values/dimen.xml b/chips/res/values/dimen.xml
deleted file mode 100644
index f989c86..0000000
--- a/chips/res/values/dimen.xml
+++ /dev/null
@@ -1,27 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
-
-<resources>
-    <dimen name="chip_padding">8dip</dimen>
-    <dimen name="chip_height">32dip</dimen>
-    <dimen name="chip_text_size">14sp</dimen>
-    <dimen name="line_spacing_extra">4dip</dimen>
-    <integer name="chips_max_lines">-1</integer>
-
-    <dimen name="chip_title_padding_start">8dip</dimen>
-    <dimen name="chip_subtitle_padding_start">16dip</dimen>
-    <dimen name="chip_icon_margin_start">12dip</dimen>
-</resources>
diff --git a/chips/res/values/strings.xml b/chips/res/values/strings.xml
deleted file mode 100644
index 3588ec3..0000000
--- a/chips/res/values/strings.xml
+++ /dev/null
@@ -1,29 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <!-- Text displayed when the recipientedittextview is not focused. Displays the total number of recipients since the field is shrunk to just display a portion -->
-    <string name="more_string">\u002B<xliff:g id="count">%1$s</xliff:g></string>
-
-    <!-- Text displayed when the user long presses on a chip to copy the recipients email address.
-         [CHAR LIMIT=200] -->
-    <string name="copy_email">Copy email address</string>
-    <!-- Text displayed when the user long presses on a chip to copy the recipient's phone number.
-         [CHAR LIMIT=200] -->
-    <string name="copy_number">Copy phone number</string>
-    <!-- Text displayed in the enter key slot when the recipientedittextview has focus.
-         [CHAR LIMIT=12] -->
-    <string name="done">Return</string>
-</resources>
diff --git a/chips/res/values/styles.xml b/chips/res/values/styles.xml
deleted file mode 100644
index 9b60cde..0000000
--- a/chips/res/values/styles.xml
+++ /dev/null
@@ -1,44 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
-<resources xmlns:tools="http://schemas.android.com/tools"
-           xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <style name="RecipientEditTextView" parent="@android:attr/autoCompleteTextViewStyle">
-        <item name="android:inputType">textEmailAddress|textMultiLine</item>
-        <item name="android:imeOptions">actionNext|flagNoFullscreen</item>
-        <item name="android:textAppearance">?android:attr/textAppearanceMedium</item>
-        <item name="android:background">@null</item>
-        <item name="android:layout_height">wrap_content</item>
-        <item name="android:layout_width">match_parent</item>
-        <item name="android:dropDownVerticalOffset">-6dip</item>
-        <item name="android:dropDownHorizontalOffset">-16dip</item>
-        <item name="android:minHeight">48dip</item>
-        <item name="android:lineSpacingExtra">@dimen/line_spacing_extra</item>
-        <item name="android:textAlignment" tools:ignore="NewApi">viewStart</item>
-        <item name="android:textDirection" tools:ignore="NewApi">locale</item>
-    </style>
-
-    <style name="ChipTitleStyle">
-        <item name="android:paddingLeft">@dimen/chip_title_padding_start</item>
-    </style>
-
-    <style name="ChipSubtitleStyle">
-        <item name="android:paddingLeft">@dimen/chip_subtitle_padding_start</item>
-    </style>
-
-    <style name="ChipIconStyle">
-        <item name="android:layout_marginLeft">@dimen/chip_icon_margin_start</item>
-    </style>
-</resources>
diff --git a/chips/sample/Android.mk b/chips/sample/Android.mk
deleted file mode 100644
index 978aa7f..0000000
--- a/chips/sample/Android.mk
+++ /dev/null
@@ -1,44 +0,0 @@
-# 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.
-
-LOCAL_PATH:= $(call my-dir)
-
-# Include res dir from chips
-chips_dir := ../res
-res_dirs := res $(chips_dir)
-
-##################################################
-# Build APK
-include $(CLEAR_VARS)
-
-src_dirs := src
-LOCAL_PACKAGE_NAME := ChipsSample
-
-LOCAL_STATIC_JAVA_LIBRARIES += android-common-chips
-
-LOCAL_SDK_VERSION := 18
-
-LOCAL_SRC_FILES := $(call all-java-files-under, src) \
-        $(call all-logtags-files-under, $(src_dirs))
-LOCAL_RESOURCE_DIR := $(addprefix $(LOCAL_PATH)/, $(res_dirs))
-LOCAL_AAPT_FLAGS := --auto-add-overlay
-LOCAL_AAPT_FLAGS += --extra-packages com.android.ex.chips
-
-include $(BUILD_PACKAGE)
-
-
-##################################################
-# Build all sub-directories
-
-include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/chips/sample/AndroidManifest.xml b/chips/sample/AndroidManifest.xml
deleted file mode 100644
index a490e29..0000000
--- a/chips/sample/AndroidManifest.xml
+++ /dev/null
@@ -1,29 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
-    package="com.android.ex.chips.sample"
-    android:versionCode="1"
-    android:versionName="1.0" >
-
-    <uses-sdk
-        android:minSdkVersion="11"
-        android:targetSdkVersion="18" />
-
-    <uses-permission android:name="android.permission.READ_CONTACTS" />
-
-    <application
-        android:allowBackup="true"
-        android:icon="@drawable/ic_launcher"
-        android:label="@string/app_name"
-        android:theme="@android:style/Theme.Holo.Light" >
-        <activity
-            android:name="com.android.ex.chips.sample.MainActivity"
-            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>
\ No newline at end of file
diff --git a/chips/sample/res/drawable-hdpi/ic_launcher.png b/chips/sample/res/drawable-hdpi/ic_launcher.png
deleted file mode 100644
index 96a442e..0000000
--- a/chips/sample/res/drawable-hdpi/ic_launcher.png
+++ /dev/null
Binary files differ
diff --git a/chips/sample/res/drawable-mdpi/ic_launcher.png b/chips/sample/res/drawable-mdpi/ic_launcher.png
deleted file mode 100644
index 359047d..0000000
--- a/chips/sample/res/drawable-mdpi/ic_launcher.png
+++ /dev/null
Binary files differ
diff --git a/chips/sample/res/drawable-xhdpi/ic_launcher.png b/chips/sample/res/drawable-xhdpi/ic_launcher.png
deleted file mode 100644
index 71c6d76..0000000
--- a/chips/sample/res/drawable-xhdpi/ic_launcher.png
+++ /dev/null
Binary files differ
diff --git a/chips/sample/res/layout/activity_main.xml b/chips/sample/res/layout/activity_main.xml
deleted file mode 100644
index 01a9ff3..0000000
--- a/chips/sample/res/layout/activity_main.xml
+++ /dev/null
@@ -1,36 +0,0 @@
-<!-- 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.
--->
-<LinearLayout 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:orientation="vertical"
-    tools:context=".MainActivity" >
-
-    <com.android.ex.chips.RecipientEditTextView
-        android:id="@+id/email_retv"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:hint="@string/email_addresses"
-        android:minHeight="58dp" />
-
-    <com.android.ex.chips.RecipientEditTextView
-        android:id="@+id/phone_retv"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:hint="@string/phone_numbers"
-        android:minHeight="58dp" />
-
-</LinearLayout>
\ No newline at end of file
diff --git a/chips/sample/res/values-af/strings.xml b/chips/sample/res/values-af/strings.xml
deleted file mode 100644
index e4c4945..0000000
--- a/chips/sample/res/values-af/strings.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--  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 xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="app_name" msgid="4076638519189386225">"Chips-voorbeeld"</string>
-    <string name="email_addresses" msgid="5320415175940315400">"E-posadresse"</string>
-    <string name="phone_numbers" msgid="7836326833170390688">"Foonnommers"</string>
-</resources>
diff --git a/chips/sample/res/values-am/strings.xml b/chips/sample/res/values-am/strings.xml
deleted file mode 100644
index d19c4e8..0000000
--- a/chips/sample/res/values-am/strings.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--  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 xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="app_name" msgid="4076638519189386225">"የቺፕስ ናሙና"</string>
-    <string name="email_addresses" msgid="5320415175940315400">"የኢሜይል አድራሻዎች"</string>
-    <string name="phone_numbers" msgid="7836326833170390688">"ስልክ ቁጥሮች"</string>
-</resources>
diff --git a/chips/sample/res/values-ar/strings.xml b/chips/sample/res/values-ar/strings.xml
deleted file mode 100644
index 4492ec7..0000000
--- a/chips/sample/res/values-ar/strings.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--  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 xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="app_name" msgid="4076638519189386225">"عينة شرائح"</string>
-    <string name="email_addresses" msgid="5320415175940315400">"عناوين البريد الإلكتروني"</string>
-    <string name="phone_numbers" msgid="7836326833170390688">"أرقام الهواتف"</string>
-</resources>
diff --git a/chips/sample/res/values-bg/strings.xml b/chips/sample/res/values-bg/strings.xml
deleted file mode 100644
index 4c118c1..0000000
--- a/chips/sample/res/values-bg/strings.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--  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 xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="app_name" msgid="4076638519189386225">"Chips Sample"</string>
-    <string name="email_addresses" msgid="5320415175940315400">"Имейл адреси"</string>
-    <string name="phone_numbers" msgid="7836326833170390688">"Телефонни номера"</string>
-</resources>
diff --git a/chips/sample/res/values-ca/strings.xml b/chips/sample/res/values-ca/strings.xml
deleted file mode 100644
index 847cc6f..0000000
--- a/chips/sample/res/values-ca/strings.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--  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 xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="app_name" msgid="4076638519189386225">"Mostra de xips"</string>
-    <string name="email_addresses" msgid="5320415175940315400">"Adreces electròniques"</string>
-    <string name="phone_numbers" msgid="7836326833170390688">"Números de telèfon"</string>
-</resources>
diff --git a/chips/sample/res/values-cs/strings.xml b/chips/sample/res/values-cs/strings.xml
deleted file mode 100644
index 3e0a928..0000000
--- a/chips/sample/res/values-cs/strings.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--  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 xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="app_name" msgid="4076638519189386225">"Vzorové čipy"</string>
-    <string name="email_addresses" msgid="5320415175940315400">"E-mailové adresy"</string>
-    <string name="phone_numbers" msgid="7836326833170390688">"Telefonní čísla"</string>
-</resources>
diff --git a/chips/sample/res/values-da/strings.xml b/chips/sample/res/values-da/strings.xml
deleted file mode 100644
index e55fcc6..0000000
--- a/chips/sample/res/values-da/strings.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--  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 xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="app_name" msgid="4076638519189386225">"Eksempel på chips"</string>
-    <string name="email_addresses" msgid="5320415175940315400">"E-mailadresser"</string>
-    <string name="phone_numbers" msgid="7836326833170390688">"Telefonnumre"</string>
-</resources>
diff --git a/chips/sample/res/values-de/strings.xml b/chips/sample/res/values-de/strings.xml
deleted file mode 100644
index 614081c..0000000
--- a/chips/sample/res/values-de/strings.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--  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 xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="app_name" msgid="4076638519189386225">"Chips Sample"</string>
-    <string name="email_addresses" msgid="5320415175940315400">"E-Mail-Adressen"</string>
-    <string name="phone_numbers" msgid="7836326833170390688">"Telefonnummern"</string>
-</resources>
diff --git a/chips/sample/res/values-el/strings.xml b/chips/sample/res/values-el/strings.xml
deleted file mode 100644
index a90018a..0000000
--- a/chips/sample/res/values-el/strings.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--  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 xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="app_name" msgid="4076638519189386225">"Δείγμα τσιπ"</string>
-    <string name="email_addresses" msgid="5320415175940315400">"Διευθύνσεις ηλεκτρονικού ταχυδρομείου"</string>
-    <string name="phone_numbers" msgid="7836326833170390688">"Αριθμοί τηλεφώνου"</string>
-</resources>
diff --git a/chips/sample/res/values-en-rGB/strings.xml b/chips/sample/res/values-en-rGB/strings.xml
deleted file mode 100644
index aaccb10..0000000
--- a/chips/sample/res/values-en-rGB/strings.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--  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 xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="app_name" msgid="4076638519189386225">"Chips Sample"</string>
-    <string name="email_addresses" msgid="5320415175940315400">"Email Addresses"</string>
-    <string name="phone_numbers" msgid="7836326833170390688">"Phone Numbers"</string>
-</resources>
diff --git a/chips/sample/res/values-en-rIN/strings.xml b/chips/sample/res/values-en-rIN/strings.xml
deleted file mode 100644
index aaccb10..0000000
--- a/chips/sample/res/values-en-rIN/strings.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--  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 xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="app_name" msgid="4076638519189386225">"Chips Sample"</string>
-    <string name="email_addresses" msgid="5320415175940315400">"Email Addresses"</string>
-    <string name="phone_numbers" msgid="7836326833170390688">"Phone Numbers"</string>
-</resources>
diff --git a/chips/sample/res/values-es-rUS/strings.xml b/chips/sample/res/values-es-rUS/strings.xml
deleted file mode 100644
index e314778..0000000
--- a/chips/sample/res/values-es-rUS/strings.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--  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 xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="app_name" msgid="4076638519189386225">"Muestra de chips"</string>
-    <string name="email_addresses" msgid="5320415175940315400">"Direcciones de correo electrónico"</string>
-    <string name="phone_numbers" msgid="7836326833170390688">"Números de teléfono"</string>
-</resources>
diff --git a/chips/sample/res/values-es/strings.xml b/chips/sample/res/values-es/strings.xml
deleted file mode 100644
index dd64514..0000000
--- a/chips/sample/res/values-es/strings.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--  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 xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="app_name" msgid="4076638519189386225">"Muestra de Chips"</string>
-    <string name="email_addresses" msgid="5320415175940315400">"Direcciones de correo electrónico"</string>
-    <string name="phone_numbers" msgid="7836326833170390688">"Números de teléfono"</string>
-</resources>
diff --git a/chips/sample/res/values-et-rEE/strings.xml b/chips/sample/res/values-et-rEE/strings.xml
deleted file mode 100644
index 5c7d6e5..0000000
--- a/chips/sample/res/values-et-rEE/strings.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--  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 xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="app_name" msgid="4076638519189386225">"Chips Sample"</string>
-    <string name="email_addresses" msgid="5320415175940315400">"E-posti aadressid"</string>
-    <string name="phone_numbers" msgid="7836326833170390688">"Telefoninumbrid"</string>
-</resources>
diff --git a/chips/sample/res/values-fa/strings.xml b/chips/sample/res/values-fa/strings.xml
deleted file mode 100644
index 8ee4162..0000000
--- a/chips/sample/res/values-fa/strings.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--  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 xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="app_name" msgid="4076638519189386225">"نمونه تراشه‌ها"</string>
-    <string name="email_addresses" msgid="5320415175940315400">"آدرس‌های ایمیل"</string>
-    <string name="phone_numbers" msgid="7836326833170390688">"شماره‌ تلفن‌ها"</string>
-</resources>
diff --git a/chips/sample/res/values-fi/strings.xml b/chips/sample/res/values-fi/strings.xml
deleted file mode 100644
index c72df4d..0000000
--- a/chips/sample/res/values-fi/strings.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--  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 xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="app_name" msgid="4076638519189386225">"Chips Sample"</string>
-    <string name="email_addresses" msgid="5320415175940315400">"Sähköpostiosoitteet"</string>
-    <string name="phone_numbers" msgid="7836326833170390688">"Puhelinnumerot"</string>
-</resources>
diff --git a/chips/sample/res/values-fr-rCA/strings.xml b/chips/sample/res/values-fr-rCA/strings.xml
deleted file mode 100644
index e88de2d..0000000
--- a/chips/sample/res/values-fr-rCA/strings.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--  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 xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="app_name" msgid="4076638519189386225">"Échantillon Chips"</string>
-    <string name="email_addresses" msgid="5320415175940315400">"Adresses de courriel"</string>
-    <string name="phone_numbers" msgid="7836326833170390688">"Numéros de téléphone"</string>
-</resources>
diff --git a/chips/sample/res/values-fr/strings.xml b/chips/sample/res/values-fr/strings.xml
deleted file mode 100644
index 2b1c18e..0000000
--- a/chips/sample/res/values-fr/strings.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--  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 xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="app_name" msgid="4076638519189386225">"Échantillon Chips"</string>
-    <string name="email_addresses" msgid="5320415175940315400">"Adresses e-mail"</string>
-    <string name="phone_numbers" msgid="7836326833170390688">"Numéros de téléphone"</string>
-</resources>
diff --git a/chips/sample/res/values-hi/strings.xml b/chips/sample/res/values-hi/strings.xml
deleted file mode 100644
index bae6585..0000000
--- a/chips/sample/res/values-hi/strings.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--  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 xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="app_name" msgid="4076638519189386225">"चिप्‍स नमूने"</string>
-    <string name="email_addresses" msgid="5320415175940315400">"ईमेल पते"</string>
-    <string name="phone_numbers" msgid="7836326833170390688">"फ़ोन नंबर"</string>
-</resources>
diff --git a/chips/sample/res/values-hr/strings.xml b/chips/sample/res/values-hr/strings.xml
deleted file mode 100644
index 6eb8a8e..0000000
--- a/chips/sample/res/values-hr/strings.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--  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 xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="app_name" msgid="4076638519189386225">"Chips Sample"</string>
-    <string name="email_addresses" msgid="5320415175940315400">"E-adrese"</string>
-    <string name="phone_numbers" msgid="7836326833170390688">"Telefonski brojevi"</string>
-</resources>
diff --git a/chips/sample/res/values-hu/strings.xml b/chips/sample/res/values-hu/strings.xml
deleted file mode 100644
index 1d00752..0000000
--- a/chips/sample/res/values-hu/strings.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--  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 xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="app_name" msgid="4076638519189386225">"„Chips” minta"</string>
-    <string name="email_addresses" msgid="5320415175940315400">"E-mail címek"</string>
-    <string name="phone_numbers" msgid="7836326833170390688">"Telefonszámok"</string>
-</resources>
diff --git a/chips/sample/res/values-hy-rAM/strings.xml b/chips/sample/res/values-hy-rAM/strings.xml
deleted file mode 100644
index fbdcb21..0000000
--- a/chips/sample/res/values-hy-rAM/strings.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--  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 xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="app_name" msgid="4076638519189386225">"Չիպերի նմուշ"</string>
-    <string name="email_addresses" msgid="5320415175940315400">"Էլփոստի հասցեներ"</string>
-    <string name="phone_numbers" msgid="7836326833170390688">"Հեռախոսահամարներ"</string>
-</resources>
diff --git a/chips/sample/res/values-in/strings.xml b/chips/sample/res/values-in/strings.xml
deleted file mode 100644
index 1ebd148..0000000
--- a/chips/sample/res/values-in/strings.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--  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 xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="app_name" msgid="4076638519189386225">"Contoh Chip"</string>
-    <string name="email_addresses" msgid="5320415175940315400">"Alamat Email"</string>
-    <string name="phone_numbers" msgid="7836326833170390688">"Nomor Telepon"</string>
-</resources>
diff --git a/chips/sample/res/values-it/strings.xml b/chips/sample/res/values-it/strings.xml
deleted file mode 100644
index aefbd01..0000000
--- a/chips/sample/res/values-it/strings.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--  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 xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="app_name" msgid="4076638519189386225">"Chips Sample"</string>
-    <string name="email_addresses" msgid="5320415175940315400">"Indirizzi email"</string>
-    <string name="phone_numbers" msgid="7836326833170390688">"Numeri di telefono"</string>
-</resources>
diff --git a/chips/sample/res/values-iw/strings.xml b/chips/sample/res/values-iw/strings.xml
deleted file mode 100644
index 24c7e69..0000000
--- a/chips/sample/res/values-iw/strings.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--  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 xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="app_name" msgid="4076638519189386225">"דוגמאות שבבים"</string>
-    <string name="email_addresses" msgid="5320415175940315400">"כתובות דוא\"ל"</string>
-    <string name="phone_numbers" msgid="7836326833170390688">"מספרי טלפון"</string>
-</resources>
diff --git a/chips/sample/res/values-ja/strings.xml b/chips/sample/res/values-ja/strings.xml
deleted file mode 100644
index c75120a..0000000
--- a/chips/sample/res/values-ja/strings.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--  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 xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="app_name" msgid="4076638519189386225">"チップサンプル"</string>
-    <string name="email_addresses" msgid="5320415175940315400">"メールアドレス"</string>
-    <string name="phone_numbers" msgid="7836326833170390688">"電話番号"</string>
-</resources>
diff --git a/chips/sample/res/values-ka-rGE/strings.xml b/chips/sample/res/values-ka-rGE/strings.xml
deleted file mode 100644
index a21dab5..0000000
--- a/chips/sample/res/values-ka-rGE/strings.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--  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 xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="app_name" msgid="4076638519189386225">"ჩიპების ნიმუში"</string>
-    <string name="email_addresses" msgid="5320415175940315400">"ელფოსტის მისამართები"</string>
-    <string name="phone_numbers" msgid="7836326833170390688">"ტელეფონის ნომრები"</string>
-</resources>
diff --git a/chips/sample/res/values-km-rKH/strings.xml b/chips/sample/res/values-km-rKH/strings.xml
deleted file mode 100644
index 3730a7d..0000000
--- a/chips/sample/res/values-km-rKH/strings.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--  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 xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="app_name" msgid="4076638519189386225">"គំរូ​បន្ទះ​សៀគ្វី"</string>
-    <string name="email_addresses" msgid="5320415175940315400">"អាសយដ្ឋាន​អ៊ីមែល"</string>
-    <string name="phone_numbers" msgid="7836326833170390688">"លេខទូរស័ព្ទ"</string>
-</resources>
diff --git a/chips/sample/res/values-ko/strings.xml b/chips/sample/res/values-ko/strings.xml
deleted file mode 100644
index 24d2793..0000000
--- a/chips/sample/res/values-ko/strings.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--  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 xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="app_name" msgid="4076638519189386225">"칩 샘플"</string>
-    <string name="email_addresses" msgid="5320415175940315400">"이메일 주소"</string>
-    <string name="phone_numbers" msgid="7836326833170390688">"전화번호"</string>
-</resources>
diff --git a/chips/sample/res/values-lo-rLA/strings.xml b/chips/sample/res/values-lo-rLA/strings.xml
deleted file mode 100644
index 6357807..0000000
--- a/chips/sample/res/values-lo-rLA/strings.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--  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 xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="app_name" msgid="4076638519189386225">"Chips Sample"</string>
-    <string name="email_addresses" msgid="5320415175940315400">"ທີ່ຢູ່ອີເມວ"</string>
-    <string name="phone_numbers" msgid="7836326833170390688">"ເບີໂທລະສັບ:"</string>
-</resources>
diff --git a/chips/sample/res/values-lt/strings.xml b/chips/sample/res/values-lt/strings.xml
deleted file mode 100644
index b966062..0000000
--- a/chips/sample/res/values-lt/strings.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--  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 xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="app_name" msgid="4076638519189386225">"Lustų pavyzdžiai"</string>
-    <string name="email_addresses" msgid="5320415175940315400">"El. pašto adresai"</string>
-    <string name="phone_numbers" msgid="7836326833170390688">"Telefonų numeriai"</string>
-</resources>
diff --git a/chips/sample/res/values-lv/strings.xml b/chips/sample/res/values-lv/strings.xml
deleted file mode 100644
index fec05b5..0000000
--- a/chips/sample/res/values-lv/strings.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--  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 xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="app_name" msgid="4076638519189386225">"Chips Sample"</string>
-    <string name="email_addresses" msgid="5320415175940315400">"E-pasta adreses"</string>
-    <string name="phone_numbers" msgid="7836326833170390688">"Tālruņa numuri"</string>
-</resources>
diff --git a/chips/sample/res/values-mn-rMN/strings.xml b/chips/sample/res/values-mn-rMN/strings.xml
deleted file mode 100644
index 5289e5c..0000000
--- a/chips/sample/res/values-mn-rMN/strings.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--  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 xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="app_name" msgid="4076638519189386225">"Чипний дээж"</string>
-    <string name="email_addresses" msgid="5320415175940315400">"Имэйл хаягууд"</string>
-    <string name="phone_numbers" msgid="7836326833170390688">"Утасны дугаарууд"</string>
-</resources>
diff --git a/chips/sample/res/values-ms-rMY/strings.xml b/chips/sample/res/values-ms-rMY/strings.xml
deleted file mode 100644
index 12ab807..0000000
--- a/chips/sample/res/values-ms-rMY/strings.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--  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 xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="app_name" msgid="4076638519189386225">"Sampel Cip"</string>
-    <string name="email_addresses" msgid="5320415175940315400">"Alamat E-mel"</string>
-    <string name="phone_numbers" msgid="7836326833170390688">"Nombor Telefon"</string>
-</resources>
diff --git a/chips/sample/res/values-nb/strings.xml b/chips/sample/res/values-nb/strings.xml
deleted file mode 100644
index 3bff3e2..0000000
--- a/chips/sample/res/values-nb/strings.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--  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 xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="app_name" msgid="4076638519189386225">"Chips-eksempel"</string>
-    <string name="email_addresses" msgid="5320415175940315400">"E-postadresser"</string>
-    <string name="phone_numbers" msgid="7836326833170390688">"Telefonnumre"</string>
-</resources>
diff --git a/chips/sample/res/values-nl/strings.xml b/chips/sample/res/values-nl/strings.xml
deleted file mode 100644
index 8951311..0000000
--- a/chips/sample/res/values-nl/strings.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--  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 xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="app_name" msgid="4076638519189386225">"Chipsvoorbeeld"</string>
-    <string name="email_addresses" msgid="5320415175940315400">"E-mailadressen"</string>
-    <string name="phone_numbers" msgid="7836326833170390688">"Telefoonnummers"</string>
-</resources>
diff --git a/chips/sample/res/values-pl/strings.xml b/chips/sample/res/values-pl/strings.xml
deleted file mode 100644
index fedec0d..0000000
--- a/chips/sample/res/values-pl/strings.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--  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 xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="app_name" msgid="4076638519189386225">"Próbka chipsów"</string>
-    <string name="email_addresses" msgid="5320415175940315400">"Adresy e-mail"</string>
-    <string name="phone_numbers" msgid="7836326833170390688">"Numery telefonów"</string>
-</resources>
diff --git a/chips/sample/res/values-pt-rPT/strings.xml b/chips/sample/res/values-pt-rPT/strings.xml
deleted file mode 100644
index 951d30a..0000000
--- a/chips/sample/res/values-pt-rPT/strings.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--  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 xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="app_name" msgid="4076638519189386225">"Amostra de Chips"</string>
-    <string name="email_addresses" msgid="5320415175940315400">"Endereços de email"</string>
-    <string name="phone_numbers" msgid="7836326833170390688">"Números de telefone"</string>
-</resources>
diff --git a/chips/sample/res/values-pt/strings.xml b/chips/sample/res/values-pt/strings.xml
deleted file mode 100644
index 9d2e732..0000000
--- a/chips/sample/res/values-pt/strings.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--  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 xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="app_name" msgid="4076638519189386225">"Amostra de chips"</string>
-    <string name="email_addresses" msgid="5320415175940315400">"Endereços de e-mail"</string>
-    <string name="phone_numbers" msgid="7836326833170390688">"Números de telefone"</string>
-</resources>
diff --git a/chips/sample/res/values-ro/strings.xml b/chips/sample/res/values-ro/strings.xml
deleted file mode 100644
index bcffb5e..0000000
--- a/chips/sample/res/values-ro/strings.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--  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 xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="app_name" msgid="4076638519189386225">"Mostră Chips"</string>
-    <string name="email_addresses" msgid="5320415175940315400">"Adrese de e-mail"</string>
-    <string name="phone_numbers" msgid="7836326833170390688">"Numere de telefon"</string>
-</resources>
diff --git a/chips/sample/res/values-ru/strings.xml b/chips/sample/res/values-ru/strings.xml
deleted file mode 100644
index 10f052e..0000000
--- a/chips/sample/res/values-ru/strings.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--  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 xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="app_name" msgid="4076638519189386225">"Chips Sample"</string>
-    <string name="email_addresses" msgid="5320415175940315400">"Адреса эл. почты"</string>
-    <string name="phone_numbers" msgid="7836326833170390688">"Номера телефонов"</string>
-</resources>
diff --git a/chips/sample/res/values-sk/strings.xml b/chips/sample/res/values-sk/strings.xml
deleted file mode 100644
index 1297298..0000000
--- a/chips/sample/res/values-sk/strings.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--  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 xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="app_name" msgid="4076638519189386225">"Ukážka čipov"</string>
-    <string name="email_addresses" msgid="5320415175940315400">"E-mailové adresy"</string>
-    <string name="phone_numbers" msgid="7836326833170390688">"Telefónne čísla"</string>
-</resources>
diff --git a/chips/sample/res/values-sl/strings.xml b/chips/sample/res/values-sl/strings.xml
deleted file mode 100644
index 0e1c855..0000000
--- a/chips/sample/res/values-sl/strings.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--  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 xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="app_name" msgid="4076638519189386225">"Vzorec čipov"</string>
-    <string name="email_addresses" msgid="5320415175940315400">"E-poštni naslovi"</string>
-    <string name="phone_numbers" msgid="7836326833170390688">"Telefonske številke"</string>
-</resources>
diff --git a/chips/sample/res/values-sr/strings.xml b/chips/sample/res/values-sr/strings.xml
deleted file mode 100644
index dbd91a5..0000000
--- a/chips/sample/res/values-sr/strings.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--  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 xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="app_name" msgid="4076638519189386225">"Пример чипова"</string>
-    <string name="email_addresses" msgid="5320415175940315400">"Адресе е-поште"</string>
-    <string name="phone_numbers" msgid="7836326833170390688">"Бројеви телефона"</string>
-</resources>
diff --git a/chips/sample/res/values-sv/strings.xml b/chips/sample/res/values-sv/strings.xml
deleted file mode 100644
index d787c85..0000000
--- a/chips/sample/res/values-sv/strings.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--  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 xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="app_name" msgid="4076638519189386225">"Chipsprov"</string>
-    <string name="email_addresses" msgid="5320415175940315400">"E-postadresser"</string>
-    <string name="phone_numbers" msgid="7836326833170390688">"Telefonnummer"</string>
-</resources>
diff --git a/chips/sample/res/values-sw/strings.xml b/chips/sample/res/values-sw/strings.xml
deleted file mode 100644
index 5afd792..0000000
--- a/chips/sample/res/values-sw/strings.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--  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 xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="app_name" msgid="4076638519189386225">"Sampuli ya Chips"</string>
-    <string name="email_addresses" msgid="5320415175940315400">"Anwani za Barua Pepe"</string>
-    <string name="phone_numbers" msgid="7836326833170390688">"Nambari za Simu"</string>
-</resources>
diff --git a/chips/sample/res/values-th/strings.xml b/chips/sample/res/values-th/strings.xml
deleted file mode 100644
index 80bf67d..0000000
--- a/chips/sample/res/values-th/strings.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--  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 xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="app_name" msgid="4076638519189386225">"ตัวอย่างชิป"</string>
-    <string name="email_addresses" msgid="5320415175940315400">"ที่อยู่อีเมล"</string>
-    <string name="phone_numbers" msgid="7836326833170390688">"หมายเลขโทรศัพท์"</string>
-</resources>
diff --git a/chips/sample/res/values-tl/strings.xml b/chips/sample/res/values-tl/strings.xml
deleted file mode 100644
index 411e0d4..0000000
--- a/chips/sample/res/values-tl/strings.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--  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 xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="app_name" msgid="4076638519189386225">"Sample ng Mga Chip"</string>
-    <string name="email_addresses" msgid="5320415175940315400">"Mga Email Address"</string>
-    <string name="phone_numbers" msgid="7836326833170390688">"Mga Numero ng Telepono"</string>
-</resources>
diff --git a/chips/sample/res/values-tr/strings.xml b/chips/sample/res/values-tr/strings.xml
deleted file mode 100644
index dad01bb..0000000
--- a/chips/sample/res/values-tr/strings.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--  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 xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="app_name" msgid="4076638519189386225">"Fiş Örneği"</string>
-    <string name="email_addresses" msgid="5320415175940315400">"E-posta Adresleri"</string>
-    <string name="phone_numbers" msgid="7836326833170390688">"Telefon Numaraları"</string>
-</resources>
diff --git a/chips/sample/res/values-uk/strings.xml b/chips/sample/res/values-uk/strings.xml
deleted file mode 100644
index f09cb8c..0000000
--- a/chips/sample/res/values-uk/strings.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--  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 xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="app_name" msgid="4076638519189386225">"Chips Sample"</string>
-    <string name="email_addresses" msgid="5320415175940315400">"Електронні адреси"</string>
-    <string name="phone_numbers" msgid="7836326833170390688">"Номери телефонів"</string>
-</resources>
diff --git a/chips/sample/res/values-vi/strings.xml b/chips/sample/res/values-vi/strings.xml
deleted file mode 100644
index b9bc474..0000000
--- a/chips/sample/res/values-vi/strings.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--  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 xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="app_name" msgid="4076638519189386225">"Mẫu chip"</string>
-    <string name="email_addresses" msgid="5320415175940315400">"Địa chỉ email"</string>
-    <string name="phone_numbers" msgid="7836326833170390688">"Số điện thoại"</string>
-</resources>
diff --git a/chips/sample/res/values-zh-rCN/strings.xml b/chips/sample/res/values-zh-rCN/strings.xml
deleted file mode 100644
index ebee45c..0000000
--- a/chips/sample/res/values-zh-rCN/strings.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--  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 xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="app_name" msgid="4076638519189386225">"Chips Sample"</string>
-    <string name="email_addresses" msgid="5320415175940315400">"电子邮件地址"</string>
-    <string name="phone_numbers" msgid="7836326833170390688">"电话号码"</string>
-</resources>
diff --git a/chips/sample/res/values-zh-rHK/strings.xml b/chips/sample/res/values-zh-rHK/strings.xml
deleted file mode 100644
index d2c3bb0..0000000
--- a/chips/sample/res/values-zh-rHK/strings.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--  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 xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="app_name" msgid="4076638519189386225">"Chips Sample"</string>
-    <string name="email_addresses" msgid="5320415175940315400">"電郵地址"</string>
-    <string name="phone_numbers" msgid="7836326833170390688">"電話號碼"</string>
-</resources>
diff --git a/chips/sample/res/values-zh-rTW/strings.xml b/chips/sample/res/values-zh-rTW/strings.xml
deleted file mode 100644
index b502833..0000000
--- a/chips/sample/res/values-zh-rTW/strings.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--  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 xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="app_name" msgid="4076638519189386225">"Chips Sample"</string>
-    <string name="email_addresses" msgid="5320415175940315400">"電子郵件地址"</string>
-    <string name="phone_numbers" msgid="7836326833170390688">"電話號碼"</string>
-</resources>
diff --git a/chips/sample/res/values-zu/strings.xml b/chips/sample/res/values-zu/strings.xml
deleted file mode 100644
index 6a106b7..0000000
--- a/chips/sample/res/values-zu/strings.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--  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 xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="app_name" msgid="4076638519189386225">"Isempula yama-chip"</string>
-    <string name="email_addresses" msgid="5320415175940315400">"Amakheli we-imeyili"</string>
-    <string name="phone_numbers" msgid="7836326833170390688">"Izinombolo zefoni"</string>
-</resources>
diff --git a/chips/sample/res/values/strings.xml b/chips/sample/res/values/strings.xml
deleted file mode 100644
index a40b20d..0000000
--- a/chips/sample/res/values/strings.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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" translatable="false">Chips Sample</string>
-    <string name="email_addresses">Email Addresses</string>
-    <string name="phone_numbers">Phone Numbers</string>
-
-</resources>
diff --git a/chips/sample/src/com/android/ex/chips/sample/MainActivity.java b/chips/sample/src/com/android/ex/chips/sample/MainActivity.java
deleted file mode 100644
index 0622e65..0000000
--- a/chips/sample/src/com/android/ex/chips/sample/MainActivity.java
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * 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.android.ex.chips.sample;
-
-import android.os.Bundle;
-import android.text.util.Rfc822Tokenizer;
-import android.widget.MultiAutoCompleteTextView;
-import android.app.Activity;
-
-import com.android.ex.chips.BaseRecipientAdapter;
-import com.android.ex.chips.RecipientEditTextView;
-
-public class MainActivity extends Activity {
-
-    @Override
-    protected void onCreate(final Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-        setContentView(R.layout.activity_main);
-
-        final RecipientEditTextView emailRetv =
-                (RecipientEditTextView) findViewById(R.id.email_retv);
-        emailRetv.setTokenizer(new Rfc822Tokenizer());
-        emailRetv.setAdapter(new BaseRecipientAdapter(this) { });
-
-        final RecipientEditTextView phoneRetv =
-                (RecipientEditTextView) findViewById(R.id.phone_retv);
-        phoneRetv.setTokenizer(new MultiAutoCompleteTextView.CommaTokenizer());
-        phoneRetv.setAdapter(
-                new BaseRecipientAdapter(BaseRecipientAdapter.QUERY_TYPE_PHONE, this) { });
-    }
-
-}
diff --git a/chips/src/com/android/ex/chips/BaseRecipientAdapter.java b/chips/src/com/android/ex/chips/BaseRecipientAdapter.java
deleted file mode 100644
index 468e168..0000000
--- a/chips/src/com/android/ex/chips/BaseRecipientAdapter.java
+++ /dev/null
@@ -1,1005 +0,0 @@
-/*
- * 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.android.ex.chips;
-
-import android.accounts.Account;
-import android.content.ContentResolver;
-import android.content.Context;
-import android.content.pm.PackageManager;
-import android.content.pm.PackageManager.NameNotFoundException;
-import android.content.res.Resources;
-import android.database.Cursor;
-import android.graphics.Bitmap;
-import android.graphics.BitmapFactory;
-import android.net.Uri;
-import android.os.AsyncTask;
-import android.os.Handler;
-import android.os.Message;
-import android.provider.ContactsContract;
-import android.provider.ContactsContract.CommonDataKinds.Photo;
-import android.provider.ContactsContract.Directory;
-import android.support.v4.util.LruCache;
-import android.text.TextUtils;
-import android.text.util.Rfc822Token;
-import android.util.Log;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.AutoCompleteTextView;
-import android.widget.BaseAdapter;
-import android.widget.Filter;
-import android.widget.Filterable;
-
-import com.android.ex.chips.DropdownChipLayouter.AdapterType;
-
-import java.io.ByteArrayOutputStream;
-import java.io.FileNotFoundException;
-import java.io.IOException;
-import java.io.InputStream;
-import java.util.ArrayList;
-import java.util.HashSet;
-import java.util.LinkedHashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-
-/**
- * Adapter for showing a recipient list.
- */
-public class BaseRecipientAdapter extends BaseAdapter implements Filterable, AccountSpecifier {
-    private static final String TAG = "BaseRecipientAdapter";
-
-    private static final boolean DEBUG = false;
-
-    /**
-     * The preferred number of results to be retrieved. This number may be
-     * exceeded if there are several directories configured, because we will use
-     * the same limit for all directories.
-     */
-    private static final int DEFAULT_PREFERRED_MAX_RESULT_COUNT = 10;
-
-    /**
-     * The number of extra entries requested to allow for duplicates. Duplicates
-     * are removed from the overall result.
-     */
-    static final int ALLOWANCE_FOR_DUPLICATES = 5;
-
-    // This is ContactsContract.PRIMARY_ACCOUNT_NAME. Available from ICS as hidden
-    static final String PRIMARY_ACCOUNT_NAME = "name_for_primary_account";
-    // This is ContactsContract.PRIMARY_ACCOUNT_TYPE. Available from ICS as hidden
-    static final String PRIMARY_ACCOUNT_TYPE = "type_for_primary_account";
-
-    /** The number of photos cached in this Adapter. */
-    private static final int PHOTO_CACHE_SIZE = 20;
-
-    /**
-     * The "Waiting for more contacts" message will be displayed if search is not complete
-     * within this many milliseconds.
-     */
-    private static final int MESSAGE_SEARCH_PENDING_DELAY = 1000;
-    /** Used to prepare "Waiting for more contacts" message. */
-    private static final int MESSAGE_SEARCH_PENDING = 1;
-
-    public static final int QUERY_TYPE_EMAIL = 0;
-    public static final int QUERY_TYPE_PHONE = 1;
-
-    private final Queries.Query mQuery;
-    private final int mQueryType;
-
-    /**
-     * Model object for a {@link Directory} row.
-     */
-    public final static class DirectorySearchParams {
-        public long directoryId;
-        public String directoryType;
-        public String displayName;
-        public String accountName;
-        public String accountType;
-        public CharSequence constraint;
-        public DirectoryFilter filter;
-    }
-
-    private static class PhotoQuery {
-        public static final String[] PROJECTION = {
-            Photo.PHOTO
-        };
-
-        public static final int PHOTO = 0;
-    }
-
-    protected static class DirectoryListQuery {
-
-        public static final Uri URI =
-                Uri.withAppendedPath(ContactsContract.AUTHORITY_URI, "directories");
-        public static final String[] PROJECTION = {
-            Directory._ID,              // 0
-            Directory.ACCOUNT_NAME,     // 1
-            Directory.ACCOUNT_TYPE,     // 2
-            Directory.DISPLAY_NAME,     // 3
-            Directory.PACKAGE_NAME,     // 4
-            Directory.TYPE_RESOURCE_ID, // 5
-        };
-
-        public static final int ID = 0;
-        public static final int ACCOUNT_NAME = 1;
-        public static final int ACCOUNT_TYPE = 2;
-        public static final int DISPLAY_NAME = 3;
-        public static final int PACKAGE_NAME = 4;
-        public static final int TYPE_RESOURCE_ID = 5;
-    }
-
-    /** Used to temporarily hold results in Cursor objects. */
-    protected static class TemporaryEntry {
-        public final String displayName;
-        public final String destination;
-        public final int destinationType;
-        public final String destinationLabel;
-        public final long contactId;
-        public final Long directoryId;
-        public final long dataId;
-        public final String thumbnailUriString;
-        public final int displayNameSource;
-        public final String lookupKey;
-
-        public TemporaryEntry(
-                String displayName,
-                String destination,
-                int destinationType,
-                String destinationLabel,
-                long contactId,
-                Long directoryId,
-                long dataId,
-                String thumbnailUriString,
-                int displayNameSource,
-                String lookupKey) {
-            this.displayName = displayName;
-            this.destination = destination;
-            this.destinationType = destinationType;
-            this.destinationLabel = destinationLabel;
-            this.contactId = contactId;
-            this.directoryId = directoryId;
-            this.dataId = dataId;
-            this.thumbnailUriString = thumbnailUriString;
-            this.displayNameSource = displayNameSource;
-            this.lookupKey = lookupKey;
-        }
-
-        public TemporaryEntry(Cursor cursor, Long directoryId) {
-            this.displayName = cursor.getString(Queries.Query.NAME);
-            this.destination = cursor.getString(Queries.Query.DESTINATION);
-            this.destinationType = cursor.getInt(Queries.Query.DESTINATION_TYPE);
-            this.destinationLabel = cursor.getString(Queries.Query.DESTINATION_LABEL);
-            this.contactId = cursor.getLong(Queries.Query.CONTACT_ID);
-            this.directoryId = directoryId;
-            this.dataId = cursor.getLong(Queries.Query.DATA_ID);
-            this.thumbnailUriString = cursor.getString(Queries.Query.PHOTO_THUMBNAIL_URI);
-            this.displayNameSource = cursor.getInt(Queries.Query.DISPLAY_NAME_SOURCE);
-            this.lookupKey = cursor.getString(Queries.Query.LOOKUP_KEY);
-        }
-    }
-
-    /**
-     * Used to pass results from {@link DefaultFilter#performFiltering(CharSequence)} to
-     * {@link DefaultFilter#publishResults(CharSequence, android.widget.Filter.FilterResults)}
-     */
-    private static class DefaultFilterResult {
-        public final List<RecipientEntry> entries;
-        public final LinkedHashMap<Long, List<RecipientEntry>> entryMap;
-        public final List<RecipientEntry> nonAggregatedEntries;
-        public final Set<String> existingDestinations;
-        public final List<DirectorySearchParams> paramsList;
-
-        public DefaultFilterResult(List<RecipientEntry> entries,
-                LinkedHashMap<Long, List<RecipientEntry>> entryMap,
-                List<RecipientEntry> nonAggregatedEntries,
-                Set<String> existingDestinations,
-                List<DirectorySearchParams> paramsList) {
-            this.entries = entries;
-            this.entryMap = entryMap;
-            this.nonAggregatedEntries = nonAggregatedEntries;
-            this.existingDestinations = existingDestinations;
-            this.paramsList = paramsList;
-        }
-    }
-
-    /**
-     * An asynchronous filter used for loading two data sets: email rows from the local
-     * contact provider and the list of {@link Directory}'s.
-     */
-    private final class DefaultFilter extends Filter {
-
-        @Override
-        protected FilterResults performFiltering(CharSequence constraint) {
-            if (DEBUG) {
-                Log.d(TAG, "start filtering. constraint: " + constraint + ", thread:"
-                        + Thread.currentThread());
-            }
-
-            final FilterResults results = new FilterResults();
-            Cursor defaultDirectoryCursor = null;
-            Cursor directoryCursor = null;
-
-            if (TextUtils.isEmpty(constraint)) {
-                clearTempEntries();
-                // Return empty results.
-                return results;
-            }
-
-            try {
-                defaultDirectoryCursor = doQuery(constraint, mPreferredMaxResultCount,
-                        null /* directoryId */);
-
-                if (defaultDirectoryCursor == null) {
-                    if (DEBUG) {
-                        Log.w(TAG, "null cursor returned for default Email filter query.");
-                    }
-                } else {
-                    // These variables will become mEntries, mEntryMap, mNonAggregatedEntries, and
-                    // mExistingDestinations. Here we shouldn't use those member variables directly
-                    // since this method is run outside the UI thread.
-                    final LinkedHashMap<Long, List<RecipientEntry>> entryMap =
-                            new LinkedHashMap<Long, List<RecipientEntry>>();
-                    final List<RecipientEntry> nonAggregatedEntries =
-                            new ArrayList<RecipientEntry>();
-                    final Set<String> existingDestinations = new HashSet<String>();
-
-                    while (defaultDirectoryCursor.moveToNext()) {
-                        // Note: At this point each entry doesn't contain any photo
-                        // (thus getPhotoBytes() returns null).
-                        putOneEntry(new TemporaryEntry(defaultDirectoryCursor,
-                                null /* directoryId */),
-                                true, entryMap, nonAggregatedEntries, existingDestinations);
-                    }
-
-                    // We'll copy this result to mEntry in publicResults() (run in the UX thread).
-                    final List<RecipientEntry> entries = constructEntryList(
-                            entryMap, nonAggregatedEntries);
-
-                    // After having local results, check the size of results. If the results are
-                    // not enough, we search remote directories, which will take longer time.
-                    final int limit = mPreferredMaxResultCount - existingDestinations.size();
-                    final List<DirectorySearchParams> paramsList;
-                    if (limit > 0) {
-                        if (DEBUG) {
-                            Log.d(TAG, "More entries should be needed (current: "
-                                    + existingDestinations.size()
-                                    + ", remaining limit: " + limit + ") ");
-                        }
-                        directoryCursor = mContentResolver.query(
-                                DirectoryListQuery.URI, DirectoryListQuery.PROJECTION,
-                                null, null, null);
-                        paramsList = setupOtherDirectories(mContext, directoryCursor, mAccount);
-                    } else {
-                        // We don't need to search other directories.
-                        paramsList = null;
-                    }
-
-                    results.values = new DefaultFilterResult(
-                            entries, entryMap, nonAggregatedEntries,
-                            existingDestinations, paramsList);
-                    results.count = 1;
-                }
-            } finally {
-                if (defaultDirectoryCursor != null) {
-                    defaultDirectoryCursor.close();
-                }
-                if (directoryCursor != null) {
-                    directoryCursor.close();
-                }
-            }
-            return results;
-        }
-
-        @Override
-        protected void publishResults(final CharSequence constraint, FilterResults results) {
-            // If a user types a string very quickly and database is slow, "constraint" refers to
-            // an older text which shows inconsistent results for users obsolete (b/4998713).
-            // TODO: Fix it.
-            mCurrentConstraint = constraint;
-
-            clearTempEntries();
-
-            if (results.values != null) {
-                DefaultFilterResult defaultFilterResult = (DefaultFilterResult) results.values;
-                mEntryMap = defaultFilterResult.entryMap;
-                mNonAggregatedEntries = defaultFilterResult.nonAggregatedEntries;
-                mExistingDestinations = defaultFilterResult.existingDestinations;
-
-                // If there are no local results, in the new result set, cache off what had been
-                // shown to the user for use until the first directory result is returned
-                if (defaultFilterResult.entries.size() == 0 &&
-                        defaultFilterResult.paramsList != null) {
-                    cacheCurrentEntries();
-                }
-
-                updateEntries(defaultFilterResult.entries);
-
-                // We need to search other remote directories, doing other Filter requests.
-                if (defaultFilterResult.paramsList != null) {
-                    final int limit = mPreferredMaxResultCount -
-                            defaultFilterResult.existingDestinations.size();
-                    startSearchOtherDirectories(constraint, defaultFilterResult.paramsList, limit);
-                }
-            }
-
-        }
-
-        @Override
-        public CharSequence convertResultToString(Object resultValue) {
-            final RecipientEntry entry = (RecipientEntry)resultValue;
-            final String displayName = entry.getDisplayName();
-            final String emailAddress = entry.getDestination();
-            if (TextUtils.isEmpty(displayName) || TextUtils.equals(displayName, emailAddress)) {
-                 return emailAddress;
-            } else {
-                return new Rfc822Token(displayName, emailAddress, null).toString();
-            }
-        }
-    }
-
-    /**
-     * An asynchronous filter that performs search in a particular directory.
-     */
-    protected class DirectoryFilter extends Filter {
-        private final DirectorySearchParams mParams;
-        private int mLimit;
-
-        public DirectoryFilter(DirectorySearchParams params) {
-            mParams = params;
-        }
-
-        public synchronized void setLimit(int limit) {
-            this.mLimit = limit;
-        }
-
-        public synchronized int getLimit() {
-            return this.mLimit;
-        }
-
-        @Override
-        protected FilterResults performFiltering(CharSequence constraint) {
-            if (DEBUG) {
-                Log.d(TAG, "DirectoryFilter#performFiltering. directoryId: " + mParams.directoryId
-                        + ", constraint: " + constraint + ", thread: " + Thread.currentThread());
-            }
-            final FilterResults results = new FilterResults();
-            results.values = null;
-            results.count = 0;
-
-            if (!TextUtils.isEmpty(constraint)) {
-                final ArrayList<TemporaryEntry> tempEntries = new ArrayList<TemporaryEntry>();
-
-                Cursor cursor = null;
-                try {
-                    // We don't want to pass this Cursor object to UI thread (b/5017608).
-                    // Assuming the result should contain fairly small results (at most ~10),
-                    // We just copy everything to local structure.
-                    cursor = doQuery(constraint, getLimit(), mParams.directoryId);
-
-                    if (cursor != null) {
-                        while (cursor.moveToNext()) {
-                            tempEntries.add(new TemporaryEntry(cursor, mParams.directoryId));
-                        }
-                    }
-                } finally {
-                    if (cursor != null) {
-                        cursor.close();
-                    }
-                }
-                if (!tempEntries.isEmpty()) {
-                    results.values = tempEntries;
-                    results.count = 1;
-                }
-            }
-
-            if (DEBUG) {
-                Log.v(TAG, "finished loading directory \"" + mParams.displayName + "\"" +
-                        " with query " + constraint);
-            }
-
-            return results;
-        }
-
-        @Override
-        protected void publishResults(final CharSequence constraint, FilterResults results) {
-            if (DEBUG) {
-                Log.d(TAG, "DirectoryFilter#publishResult. constraint: " + constraint
-                        + ", mCurrentConstraint: " + mCurrentConstraint);
-            }
-            mDelayedMessageHandler.removeDelayedLoadMessage();
-            // Check if the received result matches the current constraint
-            // If not - the user must have continued typing after the request was issued, which
-            // means several member variables (like mRemainingDirectoryLoad) are already
-            // overwritten so shouldn't be touched here anymore.
-            if (TextUtils.equals(constraint, mCurrentConstraint)) {
-                if (results.count > 0) {
-                    @SuppressWarnings("unchecked")
-                    final ArrayList<TemporaryEntry> tempEntries =
-                            (ArrayList<TemporaryEntry>) results.values;
-
-                    for (TemporaryEntry tempEntry : tempEntries) {
-                        putOneEntry(tempEntry, mParams.directoryId == Directory.DEFAULT,
-                                mEntryMap, mNonAggregatedEntries, mExistingDestinations);
-                    }
-                }
-
-                // If there are remaining directories, set up delayed message again.
-                mRemainingDirectoryCount--;
-                if (mRemainingDirectoryCount > 0) {
-                    if (DEBUG) {
-                        Log.d(TAG, "Resend delayed load message. Current mRemainingDirectoryLoad: "
-                                + mRemainingDirectoryCount);
-                    }
-                    mDelayedMessageHandler.sendDelayedLoadMessage();
-                }
-
-                // If this directory result has some items, or there are no more directories that
-                // we are waiting for, clear the temp results
-                if (results.count > 0 || mRemainingDirectoryCount == 0) {
-                    // Clear the temp entries
-                    clearTempEntries();
-                }
-            }
-
-            // Show the list again without "waiting" message.
-            updateEntries(constructEntryList(mEntryMap, mNonAggregatedEntries));
-        }
-    }
-
-    private final Context mContext;
-    private final ContentResolver mContentResolver;
-    private final LayoutInflater mInflater;
-    private Account mAccount;
-    private final int mPreferredMaxResultCount;
-    private DropdownChipLayouter mDropdownChipLayouter;
-
-    /**
-     * {@link #mEntries} is responsible for showing every result for this Adapter. To
-     * construct it, we use {@link #mEntryMap}, {@link #mNonAggregatedEntries}, and
-     * {@link #mExistingDestinations}.
-     *
-     * First, each destination (an email address or a phone number) with a valid contactId is
-     * inserted into {@link #mEntryMap} and grouped by the contactId. Destinations without valid
-     * contactId (possible if they aren't in local storage) are stored in
-     * {@link #mNonAggregatedEntries}.
-     * Duplicates are removed using {@link #mExistingDestinations}.
-     *
-     * After having all results from Cursor objects, all destinations in mEntryMap are copied to
-     * {@link #mEntries}. If the number of destinations is not enough (i.e. less than
-     * {@link #mPreferredMaxResultCount}), destinations in mNonAggregatedEntries are also used.
-     *
-     * These variables are only used in UI thread, thus should not be touched in
-     * performFiltering() methods.
-     */
-    private LinkedHashMap<Long, List<RecipientEntry>> mEntryMap;
-    private List<RecipientEntry> mNonAggregatedEntries;
-    private Set<String> mExistingDestinations;
-    /** Note: use {@link #updateEntries(List)} to update this variable. */
-    private List<RecipientEntry> mEntries;
-    private List<RecipientEntry> mTempEntries;
-
-    /** The number of directories this adapter is waiting for results. */
-    private int mRemainingDirectoryCount;
-
-    /**
-     * Used to ignore asynchronous queries with a different constraint, which may happen when
-     * users type characters quickly.
-     */
-    private CharSequence mCurrentConstraint;
-
-    private final LruCache<Uri, byte[]> mPhotoCacheMap;
-
-    /**
-     * Handler specific for maintaining "Waiting for more contacts" message, which will be shown
-     * when:
-     * - there are directories to be searched
-     * - results from directories are slow to come
-     */
-    private final class DelayedMessageHandler extends Handler {
-        @Override
-        public void handleMessage(Message msg) {
-            if (mRemainingDirectoryCount > 0) {
-                updateEntries(constructEntryList(mEntryMap, mNonAggregatedEntries));
-            }
-        }
-
-        public void sendDelayedLoadMessage() {
-            sendMessageDelayed(obtainMessage(MESSAGE_SEARCH_PENDING, 0, 0, null),
-                    MESSAGE_SEARCH_PENDING_DELAY);
-        }
-
-        public void removeDelayedLoadMessage() {
-            removeMessages(MESSAGE_SEARCH_PENDING);
-        }
-    }
-
-    private final DelayedMessageHandler mDelayedMessageHandler = new DelayedMessageHandler();
-
-    private EntriesUpdatedObserver mEntriesUpdatedObserver;
-
-    /**
-     * Constructor for email queries.
-     */
-    public BaseRecipientAdapter(Context context) {
-        this(context, DEFAULT_PREFERRED_MAX_RESULT_COUNT, QUERY_TYPE_EMAIL);
-    }
-
-    public BaseRecipientAdapter(Context context, int preferredMaxResultCount) {
-        this(context, preferredMaxResultCount, QUERY_TYPE_EMAIL);
-    }
-
-    public BaseRecipientAdapter(int queryMode, Context context) {
-        this(context, DEFAULT_PREFERRED_MAX_RESULT_COUNT, queryMode);
-    }
-
-    public BaseRecipientAdapter(int queryMode, Context context, int preferredMaxResultCount) {
-        this(context, preferredMaxResultCount, queryMode);
-    }
-
-    public BaseRecipientAdapter(Context context, int preferredMaxResultCount, int queryMode) {
-        mContext = context;
-        mContentResolver = context.getContentResolver();
-        mInflater = LayoutInflater.from(context);
-        mPreferredMaxResultCount = preferredMaxResultCount;
-        mPhotoCacheMap = new LruCache<Uri, byte[]>(PHOTO_CACHE_SIZE);
-        mQueryType = queryMode;
-
-        if (queryMode == QUERY_TYPE_EMAIL) {
-            mQuery = Queries.EMAIL;
-        } else if (queryMode == QUERY_TYPE_PHONE) {
-            mQuery = Queries.PHONE;
-        } else {
-            mQuery = Queries.EMAIL;
-            Log.e(TAG, "Unsupported query type: " + queryMode);
-        }
-    }
-
-    public Context getContext() {
-        return mContext;
-    }
-
-    public int getQueryType() {
-        return mQueryType;
-    }
-
-    public void setDropdownChipLayouter(DropdownChipLayouter dropdownChipLayouter) {
-        mDropdownChipLayouter = dropdownChipLayouter;
-        mDropdownChipLayouter.setQuery(mQuery);
-    }
-
-    public DropdownChipLayouter getDropdownChipLayouter() {
-        return mDropdownChipLayouter;
-    }
-
-    /**
-     * Set the account when known. Causes the search to prioritize contacts from that account.
-     */
-    @Override
-    public void setAccount(Account account) {
-        mAccount = account;
-    }
-
-    /** Will be called from {@link AutoCompleteTextView} to prepare auto-complete list. */
-    @Override
-    public Filter getFilter() {
-        return new DefaultFilter();
-    }
-
-    /**
-     * An extesion to {@link RecipientAlternatesAdapter#getMatchingRecipients} that allows
-     * additional sources of contacts to be considered as matching recipients.
-     * @param addresses A set of addresses to be matched
-     * @return A list of matches or null if none found
-     */
-    public Map<String, RecipientEntry> getMatchingRecipients(Set<String> addresses) {
-        return null;
-    }
-
-    public static List<DirectorySearchParams> setupOtherDirectories(Context context,
-            Cursor directoryCursor, Account account) {
-        final PackageManager packageManager = context.getPackageManager();
-        final List<DirectorySearchParams> paramsList = new ArrayList<DirectorySearchParams>();
-        DirectorySearchParams preferredDirectory = null;
-        while (directoryCursor.moveToNext()) {
-            final long id = directoryCursor.getLong(DirectoryListQuery.ID);
-
-            // Skip the local invisible directory, because the default directory already includes
-            // all local results.
-            if (id == Directory.LOCAL_INVISIBLE) {
-                continue;
-            }
-
-            final DirectorySearchParams params = new DirectorySearchParams();
-            final String packageName = directoryCursor.getString(DirectoryListQuery.PACKAGE_NAME);
-            final int resourceId = directoryCursor.getInt(DirectoryListQuery.TYPE_RESOURCE_ID);
-            params.directoryId = id;
-            params.displayName = directoryCursor.getString(DirectoryListQuery.DISPLAY_NAME);
-            params.accountName = directoryCursor.getString(DirectoryListQuery.ACCOUNT_NAME);
-            params.accountType = directoryCursor.getString(DirectoryListQuery.ACCOUNT_TYPE);
-            if (packageName != null && resourceId != 0) {
-                try {
-                    final Resources resources =
-                            packageManager.getResourcesForApplication(packageName);
-                    params.directoryType = resources.getString(resourceId);
-                    if (params.directoryType == null) {
-                        Log.e(TAG, "Cannot resolve directory name: "
-                                + resourceId + "@" + packageName);
-                    }
-                } catch (NameNotFoundException e) {
-                    Log.e(TAG, "Cannot resolve directory name: "
-                            + resourceId + "@" + packageName, e);
-                }
-            }
-
-            // If an account has been provided and we found a directory that
-            // corresponds to that account, place that directory second, directly
-            // underneath the local contacts.
-            if (account != null && account.name.equals(params.accountName) &&
-                    account.type.equals(params.accountType)) {
-                preferredDirectory = params;
-            } else {
-                paramsList.add(params);
-            }
-        }
-
-        if (preferredDirectory != null) {
-            paramsList.add(1, preferredDirectory);
-        }
-
-        return paramsList;
-    }
-
-    /**
-     * Starts search in other directories using {@link Filter}. Results will be handled in
-     * {@link DirectoryFilter}.
-     */
-    protected void startSearchOtherDirectories(
-            CharSequence constraint, List<DirectorySearchParams> paramsList, int limit) {
-        final int count = paramsList.size();
-        // Note: skipping the default partition (index 0), which has already been loaded
-        for (int i = 1; i < count; i++) {
-            final DirectorySearchParams params = paramsList.get(i);
-            params.constraint = constraint;
-            if (params.filter == null) {
-                params.filter = new DirectoryFilter(params);
-            }
-            params.filter.setLimit(limit);
-            params.filter.filter(constraint);
-        }
-
-        // Directory search started. We may show "waiting" message if directory results are slow
-        // enough.
-        mRemainingDirectoryCount = count - 1;
-        mDelayedMessageHandler.sendDelayedLoadMessage();
-    }
-
-    private static void putOneEntry(TemporaryEntry entry, boolean isAggregatedEntry,
-            LinkedHashMap<Long, List<RecipientEntry>> entryMap,
-            List<RecipientEntry> nonAggregatedEntries,
-            Set<String> existingDestinations) {
-        if (existingDestinations.contains(entry.destination)) {
-            return;
-        }
-
-        existingDestinations.add(entry.destination);
-
-        if (!isAggregatedEntry) {
-            nonAggregatedEntries.add(RecipientEntry.constructTopLevelEntry(
-                    entry.displayName,
-                    entry.displayNameSource,
-                    entry.destination, entry.destinationType, entry.destinationLabel,
-                    entry.contactId, entry.directoryId, entry.dataId, entry.thumbnailUriString,
-                    true, entry.lookupKey));
-        } else if (entryMap.containsKey(entry.contactId)) {
-            // We already have a section for the person.
-            final List<RecipientEntry> entryList = entryMap.get(entry.contactId);
-            entryList.add(RecipientEntry.constructSecondLevelEntry(
-                    entry.displayName,
-                    entry.displayNameSource,
-                    entry.destination, entry.destinationType, entry.destinationLabel,
-                    entry.contactId, entry.directoryId, entry.dataId, entry.thumbnailUriString,
-                    true, entry.lookupKey));
-        } else {
-            final List<RecipientEntry> entryList = new ArrayList<RecipientEntry>();
-            entryList.add(RecipientEntry.constructTopLevelEntry(
-                    entry.displayName,
-                    entry.displayNameSource,
-                    entry.destination, entry.destinationType, entry.destinationLabel,
-                    entry.contactId, entry.directoryId, entry.dataId, entry.thumbnailUriString,
-                    true, entry.lookupKey));
-            entryMap.put(entry.contactId, entryList);
-        }
-    }
-
-    /**
-     * Constructs an actual list for this Adapter using {@link #mEntryMap}. Also tries to
-     * fetch a cached photo for each contact entry (other than separators), or request another
-     * thread to get one from directories.
-     */
-    private List<RecipientEntry> constructEntryList(
-            LinkedHashMap<Long, List<RecipientEntry>> entryMap,
-            List<RecipientEntry> nonAggregatedEntries) {
-        final List<RecipientEntry> entries = new ArrayList<RecipientEntry>();
-        int validEntryCount = 0;
-        for (Map.Entry<Long, List<RecipientEntry>> mapEntry : entryMap.entrySet()) {
-            final List<RecipientEntry> entryList = mapEntry.getValue();
-            final int size = entryList.size();
-            for (int i = 0; i < size; i++) {
-                RecipientEntry entry = entryList.get(i);
-                entries.add(entry);
-                tryFetchPhoto(entry);
-                validEntryCount++;
-            }
-            if (validEntryCount > mPreferredMaxResultCount) {
-                break;
-            }
-        }
-        if (validEntryCount <= mPreferredMaxResultCount) {
-            for (RecipientEntry entry : nonAggregatedEntries) {
-                if (validEntryCount > mPreferredMaxResultCount) {
-                    break;
-                }
-                entries.add(entry);
-                tryFetchPhoto(entry);
-
-                validEntryCount++;
-            }
-        }
-
-        return entries;
-    }
-
-
-    public interface EntriesUpdatedObserver {
-        public void onChanged(List<RecipientEntry> entries);
-    }
-
-    public void registerUpdateObserver(EntriesUpdatedObserver observer) {
-        mEntriesUpdatedObserver = observer;
-    }
-
-    /** Resets {@link #mEntries} and notify the event to its parent ListView. */
-    private void updateEntries(List<RecipientEntry> newEntries) {
-        mEntries = newEntries;
-        mEntriesUpdatedObserver.onChanged(newEntries);
-        notifyDataSetChanged();
-    }
-
-    private void cacheCurrentEntries() {
-        mTempEntries = mEntries;
-    }
-
-    private void clearTempEntries() {
-        mTempEntries = null;
-    }
-
-    protected List<RecipientEntry> getEntries() {
-        return mTempEntries != null ? mTempEntries : mEntries;
-    }
-
-    private void tryFetchPhoto(final RecipientEntry entry) {
-        final Uri photoThumbnailUri = entry.getPhotoThumbnailUri();
-        if (photoThumbnailUri != null) {
-            final byte[] photoBytes = mPhotoCacheMap.get(photoThumbnailUri);
-            if (photoBytes != null) {
-                entry.setPhotoBytes(photoBytes);
-                // notifyDataSetChanged() should be called by a caller.
-            } else {
-                if (DEBUG) {
-                    Log.d(TAG, "No photo cache for " + entry.getDisplayName()
-                            + ". Fetch one asynchronously");
-                }
-                fetchPhotoAsync(entry, photoThumbnailUri);
-            }
-        }
-    }
-
-    // For reading photos for directory contacts, this is the chunksize for
-    // copying from the inputstream to the output stream.
-    private static final int BUFFER_SIZE = 1024*16;
-
-    private void fetchPhotoAsync(final RecipientEntry entry, final Uri photoThumbnailUri) {
-        final AsyncTask<Void, Void, byte[]> photoLoadTask = new AsyncTask<Void, Void, byte[]>() {
-            @Override
-            protected byte[] doInBackground(Void... params) {
-                // First try running a query. Images for local contacts are
-                // loaded by sending a query to the ContactsProvider.
-                final Cursor photoCursor = mContentResolver.query(
-                        photoThumbnailUri, PhotoQuery.PROJECTION, null, null, null);
-                if (photoCursor != null) {
-                    try {
-                        if (photoCursor.moveToFirst()) {
-                            return photoCursor.getBlob(PhotoQuery.PHOTO);
-                        }
-                    } finally {
-                        photoCursor.close();
-                    }
-                } else {
-                    // If the query fails, try streaming the URI directly.
-                    // For remote directory images, this URI resolves to the
-                    // directory provider and the images are loaded by sending
-                    // an openFile call to the provider.
-                    try {
-                        InputStream is = mContentResolver.openInputStream(
-                                photoThumbnailUri);
-                        if (is != null) {
-                            byte[] buffer = new byte[BUFFER_SIZE];
-                            ByteArrayOutputStream baos = new ByteArrayOutputStream();
-                            try {
-                                int size;
-                                while ((size = is.read(buffer)) != -1) {
-                                    baos.write(buffer, 0, size);
-                                }
-                            } finally {
-                                is.close();
-                            }
-                            return baos.toByteArray();
-                        }
-                    } catch (IOException ex) {
-                        // ignore
-                    }
-                }
-                return null;
-            }
-
-            @Override
-            protected void onPostExecute(final byte[] photoBytes) {
-                entry.setPhotoBytes(photoBytes);
-                if (photoBytes != null) {
-                    mPhotoCacheMap.put(photoThumbnailUri, photoBytes);
-                    notifyDataSetChanged();
-                }
-            }
-        };
-        photoLoadTask.executeOnExecutor(AsyncTask.SERIAL_EXECUTOR);
-    }
-
-    protected void fetchPhoto(final RecipientEntry entry, final Uri photoThumbnailUri) {
-        byte[] photoBytes = mPhotoCacheMap.get(photoThumbnailUri);
-        if (photoBytes != null) {
-            entry.setPhotoBytes(photoBytes);
-            return;
-        }
-        final Cursor photoCursor = mContentResolver.query(photoThumbnailUri, PhotoQuery.PROJECTION,
-                null, null, null);
-        if (photoCursor != null) {
-            try {
-                if (photoCursor.moveToFirst()) {
-                    photoBytes = photoCursor.getBlob(PhotoQuery.PHOTO);
-                    entry.setPhotoBytes(photoBytes);
-                    mPhotoCacheMap.put(photoThumbnailUri, photoBytes);
-                }
-            } finally {
-                photoCursor.close();
-            }
-        } else {
-            InputStream inputStream = null;
-            ByteArrayOutputStream outputStream = null;
-            try {
-                inputStream = mContentResolver.openInputStream(photoThumbnailUri);
-                final Bitmap bitmap = BitmapFactory.decodeStream(inputStream);
-
-                if (bitmap != null) {
-                    outputStream = new ByteArrayOutputStream();
-                    bitmap.compress(Bitmap.CompressFormat.PNG, 100, outputStream);
-                    photoBytes = outputStream.toByteArray();
-
-                    entry.setPhotoBytes(photoBytes);
-                    mPhotoCacheMap.put(photoThumbnailUri, photoBytes);
-                }
-            } catch (final FileNotFoundException e) {
-                Log.w(TAG, "Error opening InputStream for photo", e);
-            } finally {
-                try {
-                    if (inputStream != null) {
-                        inputStream.close();
-                    }
-                } catch (IOException e) {
-                    Log.e(TAG, "Error closing photo input stream", e);
-                }
-                try {
-                    if (outputStream != null) {
-                        outputStream.close();
-                    }
-                } catch (IOException e) {
-                    Log.e(TAG, "Error closing photo output stream", e);
-                }
-            }
-        }
-    }
-
-    private Cursor doQuery(CharSequence constraint, int limit, Long directoryId) {
-        final Uri.Builder builder = mQuery.getContentFilterUri().buildUpon()
-                .appendPath(constraint.toString())
-                .appendQueryParameter(ContactsContract.LIMIT_PARAM_KEY,
-                        String.valueOf(limit + ALLOWANCE_FOR_DUPLICATES));
-        if (directoryId != null) {
-            builder.appendQueryParameter(ContactsContract.DIRECTORY_PARAM_KEY,
-                    String.valueOf(directoryId));
-        }
-        if (mAccount != null) {
-            builder.appendQueryParameter(PRIMARY_ACCOUNT_NAME, mAccount.name);
-            builder.appendQueryParameter(PRIMARY_ACCOUNT_TYPE, mAccount.type);
-        }
-        final long start = System.currentTimeMillis();
-        final Cursor cursor = mContentResolver.query(
-                builder.build(), mQuery.getProjection(), null, null, null);
-        final long end = System.currentTimeMillis();
-        if (DEBUG) {
-            Log.d(TAG, "Time for autocomplete (query: " + constraint
-                    + ", directoryId: " + directoryId + ", num_of_results: "
-                    + (cursor != null ? cursor.getCount() : "null") + "): "
-                    + (end - start) + " ms");
-        }
-        return cursor;
-    }
-
-    // TODO: This won't be used at all. We should find better way to quit the thread..
-    /*public void close() {
-        mEntries = null;
-        mPhotoCacheMap.evictAll();
-        if (!sPhotoHandlerThread.quit()) {
-            Log.w(TAG, "Failed to quit photo handler thread, ignoring it.");
-        }
-    }*/
-
-    @Override
-    public int getCount() {
-        final List<RecipientEntry> entries = getEntries();
-        return entries != null ? entries.size() : 0;
-    }
-
-    @Override
-    public RecipientEntry getItem(int position) {
-        return getEntries().get(position);
-    }
-
-    @Override
-    public long getItemId(int position) {
-        return position;
-    }
-
-    @Override
-    public int getViewTypeCount() {
-        return RecipientEntry.ENTRY_TYPE_SIZE;
-    }
-
-    @Override
-    public int getItemViewType(int position) {
-        return getEntries().get(position).getEntryType();
-    }
-
-    @Override
-    public boolean isEnabled(int position) {
-        return getEntries().get(position).isSelectable();
-    }
-
-    @Override
-    public View getView(int position, View convertView, ViewGroup parent) {
-        final RecipientEntry entry = getEntries().get(position);
-
-        final String constraint = mCurrentConstraint == null ? null :
-                mCurrentConstraint.toString();
-
-        return mDropdownChipLayouter.bindView(convertView, parent, entry, position,
-                AdapterType.BASE_RECIPIENT, constraint);
-    }
-
-    public Account getAccount() {
-        return mAccount;
-    }
-}
diff --git a/chips/src/com/android/ex/chips/ChipsUtil.java b/chips/src/com/android/ex/chips/ChipsUtil.java
deleted file mode 100644
index 559b2c9..0000000
--- a/chips/src/com/android/ex/chips/ChipsUtil.java
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
- * 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.android.ex.chips;
-
-import android.os.Build;
-
-public class ChipsUtil {
-
-    /**
-     * @return true when the caller can use Chips UI in its environment.
-     */
-    public static boolean supportsChipsUi() {
-        return Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH;
-    }
-}
\ No newline at end of file
diff --git a/chips/src/com/android/ex/chips/DropdownChipLayouter.java b/chips/src/com/android/ex/chips/DropdownChipLayouter.java
deleted file mode 100644
index 6b0e78e..0000000
--- a/chips/src/com/android/ex/chips/DropdownChipLayouter.java
+++ /dev/null
@@ -1,274 +0,0 @@
-package com.android.ex.chips;
-
-import android.content.Context;
-import android.graphics.Bitmap;
-import android.graphics.BitmapFactory;
-import android.net.Uri;
-import android.text.TextUtils;
-import android.text.util.Rfc822Tokenizer;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.ImageView;
-import android.widget.TextView;
-
-import com.android.ex.chips.Queries.Query;
-
-/**
- * A class that inflates and binds the views in the dropdown list from
- * RecipientEditTextView.
- */
-public class DropdownChipLayouter {
-    /**
-     * The type of adapter that is requesting a chip layout.
-     */
-    public enum AdapterType {
-        BASE_RECIPIENT,
-        RECIPIENT_ALTERNATES,
-        SINGLE_RECIPIENT
-    }
-
-    private final LayoutInflater mInflater;
-    private final Context mContext;
-    private Query mQuery;
-
-    public DropdownChipLayouter(LayoutInflater inflater, Context context) {
-        mInflater = inflater;
-        mContext = context;
-    }
-
-    public void setQuery(Query query) {
-        mQuery = query;
-    }
-
-
-    /**
-     * Layouts and binds recipient information to the view. If convertView is null, inflates a new
-     * view with getItemLaytout().
-     *
-     * @param convertView The view to bind information to.
-     * @param parent The parent to bind the view to if we inflate a new view.
-     * @param entry The recipient entry to get information from.
-     * @param position The position in the list.
-     * @param type The adapter type that is requesting the bind.
-     * @param constraint The constraint typed in the auto complete view.
-     *
-     * @return A view ready to be shown in the drop down list.
-     */
-    public View bindView(View convertView, ViewGroup parent, RecipientEntry entry, int position,
-        AdapterType type, String constraint) {
-        // Default to show all the information
-        String displayName = entry.getDisplayName();
-        String destination = entry.getDestination();
-        boolean showImage = true;
-        CharSequence destinationType = getDestinationType(entry);
-
-        final View itemView = reuseOrInflateView(convertView, parent, type);
-
-        final ViewHolder viewHolder = new ViewHolder(itemView);
-
-        // Hide some information depending on the entry type and adapter type
-        switch (type) {
-            case BASE_RECIPIENT:
-                if (TextUtils.isEmpty(displayName) || TextUtils.equals(displayName, destination)) {
-                    displayName = destination;
-
-                    // We only show the destination for secondary entries, so clear it only for the
-                    // first level.
-                    if (entry.isFirstLevel()) {
-                        destination = null;
-                    }
-                }
-
-                if (!entry.isFirstLevel()) {
-                    displayName = null;
-                    showImage = false;
-                }
-                break;
-            case RECIPIENT_ALTERNATES:
-                if (position != 0) {
-                    displayName = null;
-                    showImage = false;
-                }
-                break;
-            case SINGLE_RECIPIENT:
-                destination = Rfc822Tokenizer.tokenize(entry.getDestination())[0].getAddress();
-                destinationType = null;
-        }
-
-        // Bind the information to the view
-        bindTextToView(displayName, viewHolder.displayNameView);
-        bindTextToView(destination, viewHolder.destinationView);
-        bindTextToView(destinationType, viewHolder.destinationTypeView);
-        bindIconToView(showImage, entry, viewHolder.imageView, type);
-
-        return itemView;
-    }
-
-    /**
-     * Returns a new view with {@link #getItemLayoutResId()}.
-     */
-    public View newView() {
-        return mInflater.inflate(getItemLayoutResId(), null);
-    }
-
-    /**
-     * Returns the same view, or inflates a new one if the given view was null.
-     */
-    protected View reuseOrInflateView(View convertView, ViewGroup parent, AdapterType type) {
-        int itemLayout = getItemLayoutResId();
-        switch (type) {
-            case BASE_RECIPIENT:
-            case RECIPIENT_ALTERNATES:
-                break;
-            case SINGLE_RECIPIENT:
-                itemLayout = getAlternateItemLayoutResId();
-                break;
-        }
-        return convertView != null ? convertView : mInflater.inflate(itemLayout, parent, false);
-    }
-
-    /**
-     * Binds the text to the given text view. If the text was null, hides the text view.
-     */
-    protected void bindTextToView(CharSequence text, TextView view) {
-        if (view == null) {
-            return;
-        }
-
-        if (text != null) {
-            view.setText(text);
-            view.setVisibility(View.VISIBLE);
-        } else {
-            view.setVisibility(View.GONE);
-        }
-    }
-
-    /**
-     * Binds the avatar icon to the image view. If we don't want to show the image, hides the
-     * image view.
-     */
-    protected void bindIconToView(boolean showImage, RecipientEntry entry, ImageView view,
-        AdapterType type) {
-        if (view == null) {
-            return;
-        }
-
-        if (showImage) {
-            switch (type) {
-                case BASE_RECIPIENT:
-                    byte[] photoBytes = entry.getPhotoBytes();
-                    if (photoBytes != null && photoBytes.length > 0) {
-                        final Bitmap photo = BitmapFactory.decodeByteArray(photoBytes, 0,
-                            photoBytes.length);
-                        view.setImageBitmap(photo);
-                    } else {
-                        view.setImageResource(getDefaultPhotoResId());
-                    }
-                    break;
-                case RECIPIENT_ALTERNATES:
-                    Uri thumbnailUri = entry.getPhotoThumbnailUri();
-                    if (thumbnailUri != null) {
-                        // TODO: see if this needs to be done outside the main thread
-                        // as it may be too slow to get immediately.
-                        view.setImageURI(thumbnailUri);
-                    } else {
-                        view.setImageResource(getDefaultPhotoResId());
-                    }
-                    break;
-                case SINGLE_RECIPIENT:
-                default:
-                    break;
-            }
-            view.setVisibility(View.VISIBLE);
-        } else {
-            view.setVisibility(View.GONE);
-        }
-    }
-
-    protected CharSequence getDestinationType(RecipientEntry entry) {
-        return mQuery.getTypeLabel(mContext.getResources(), entry.getDestinationType(),
-            entry.getDestinationLabel()).toString().toUpperCase();
-    }
-
-    /**
-     * Returns a layout id for each item inside auto-complete list.
-     *
-     * Each View must contain two TextViews (for display name and destination) and one ImageView
-     * (for photo). Ids for those should be available via {@link #getDisplayNameResId()},
-     * {@link #getDestinationResId()}, and {@link #getPhotoResId()}.
-     */
-    protected int getItemLayoutResId() {
-        return R.layout.chips_recipient_dropdown_item;
-    }
-
-    /**
-     * Returns a layout id for each item inside alternate auto-complete list.
-     *
-     * Each View must contain two TextViews (for display name and destination) and one ImageView
-     * (for photo). Ids for those should be available via {@link #getDisplayNameResId()},
-     * {@link #getDestinationResId()}, and {@link #getPhotoResId()}.
-     */
-    protected int getAlternateItemLayoutResId() {
-        return R.layout.chips_alternate_item;
-    }
-
-    /**
-     * Returns a resource ID representing an image which should be shown when ther's no relevant
-     * photo is available.
-     */
-    protected int getDefaultPhotoResId() {
-        return R.drawable.ic_contact_picture;
-    }
-
-    /**
-     * Returns an id for TextView in an item View for showing a display name. By default
-     * {@link android.R.id#title} is returned.
-     */
-    protected int getDisplayNameResId() {
-        return android.R.id.title;
-    }
-
-    /**
-     * Returns an id for TextView in an item View for showing a destination
-     * (an email address or a phone number).
-     * By default {@link android.R.id#text1} is returned.
-     */
-    protected int getDestinationResId() {
-        return android.R.id.text1;
-    }
-
-    /**
-     * Returns an id for TextView in an item View for showing the type of the destination.
-     * By default {@link android.R.id#text2} is returned.
-     */
-    protected int getDestinationTypeResId() {
-        return android.R.id.text2;
-    }
-
-    /**
-     * Returns an id for ImageView in an item View for showing photo image for a person. In default
-     * {@link android.R.id#icon} is returned.
-     */
-    protected int getPhotoResId() {
-        return android.R.id.icon;
-    }
-
-    /**
-     * A holder class the view. Uses the getters in DropdownChipLayouter to find the id of the
-     * corresponding views.
-     */
-    protected class ViewHolder {
-        public final TextView displayNameView;
-        public final TextView destinationView;
-        public final TextView destinationTypeView;
-        public final ImageView imageView;
-
-        public ViewHolder(View view) {
-            displayNameView = (TextView) view.findViewById(getDisplayNameResId());
-            destinationView = (TextView) view.findViewById(getDestinationResId());
-            destinationTypeView = (TextView) view.findViewById(getDestinationTypeResId());
-            imageView = (ImageView) view.findViewById(getPhotoResId());
-        }
-    }
-}
diff --git a/chips/src/com/android/ex/chips/Queries.java b/chips/src/com/android/ex/chips/Queries.java
deleted file mode 100644
index 1e66b96..0000000
--- a/chips/src/com/android/ex/chips/Queries.java
+++ /dev/null
@@ -1,107 +0,0 @@
-/*
- * Copyright (C) 2012 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.android.ex.chips;
-
-import android.content.res.Resources;
-import android.net.Uri;
-import android.provider.ContactsContract;
-import android.provider.ContactsContract.CommonDataKinds.Email;
-import android.provider.ContactsContract.CommonDataKinds.Phone;
-import android.provider.ContactsContract.Contacts;
-
-/**
- * Phone and Email queries for supporting Chips UI.
- */
-/* package */ class Queries {
-
-    public static final Query PHONE = new Query(new String[] {
-            Contacts.DISPLAY_NAME,                          // 0
-            Phone.NUMBER,                                   // 1
-            Phone.TYPE,                                     // 2
-            Phone.LABEL,                                    // 3
-            Phone.CONTACT_ID,                               // 4
-            Phone._ID,                                      // 5
-            Contacts.PHOTO_THUMBNAIL_URI,                   // 6
-            Contacts.DISPLAY_NAME_SOURCE,                   // 7
-            Contacts.LOOKUP_KEY,                            // 8
-            ContactsContract.CommonDataKinds.Email.MIMETYPE // 9
-        }, Phone.CONTENT_FILTER_URI, Phone.CONTENT_URI) {
-
-            @Override
-            public CharSequence getTypeLabel(Resources res, int type, CharSequence label) {
-                return Phone.getTypeLabel(res, type, label);
-            }
-
-    };
-
-    public static final Query EMAIL = new Query(new String[]{
-            Contacts.DISPLAY_NAME,                          // 0
-            Email.DATA,                                     // 1
-            Email.TYPE,                                     // 2
-            Email.LABEL,                                    // 3
-            Email.CONTACT_ID,                               // 4
-            Email._ID,                                      // 5
-            Contacts.PHOTO_THUMBNAIL_URI,                   // 6
-            Contacts.DISPLAY_NAME_SOURCE,                   // 7
-            Contacts.LOOKUP_KEY,                            // 8
-            ContactsContract.CommonDataKinds.Email.MIMETYPE // 9
-        }, Email.CONTENT_FILTER_URI, Email.CONTENT_URI) {
-
-            @Override
-            public CharSequence getTypeLabel(Resources res, int type, CharSequence label) {
-                return Email.getTypeLabel(res, type, label);
-            }
-
-    };
-
-    static abstract class Query {
-        private final String[] mProjection;
-        private final Uri mContentFilterUri;
-        private final Uri mContentUri;
-
-        public static final int NAME = 0;                // String
-        public static final int DESTINATION = 1;         // String
-        public static final int DESTINATION_TYPE = 2;    // int
-        public static final int DESTINATION_LABEL = 3;   // String
-        public static final int CONTACT_ID = 4;          // long
-        public static final int DATA_ID = 5;             // long
-        public static final int PHOTO_THUMBNAIL_URI = 6; // String
-        public static final int DISPLAY_NAME_SOURCE = 7; // int
-        public static final int LOOKUP_KEY = 8;          // String
-        public static final int MIME_TYPE = 9;           // String
-
-        public Query(String[] projection, Uri contentFilter, Uri content) {
-            mProjection = projection;
-            mContentFilterUri = contentFilter;
-            mContentUri = content;
-        }
-
-        public String[] getProjection() {
-            return mProjection;
-        }
-
-        public Uri getContentFilterUri() {
-            return mContentFilterUri;
-        }
-
-        public Uri getContentUri() {
-            return mContentUri;
-        }
-
-        public abstract CharSequence getTypeLabel(Resources res, int type, CharSequence label);
-    }
-}
diff --git a/chips/src/com/android/ex/chips/RecipientAlternatesAdapter.java b/chips/src/com/android/ex/chips/RecipientAlternatesAdapter.java
deleted file mode 100644
index f6f662d..0000000
--- a/chips/src/com/android/ex/chips/RecipientAlternatesAdapter.java
+++ /dev/null
@@ -1,576 +0,0 @@
-/*
- * 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.android.ex.chips;
-
-import android.accounts.Account;
-import android.content.ContentResolver;
-import android.content.Context;
-import android.database.Cursor;
-import android.database.MatrixCursor;
-import android.net.Uri;
-import android.provider.ContactsContract;
-import android.provider.ContactsContract.Contacts;
-import android.text.TextUtils;
-import android.text.util.Rfc822Token;
-import android.text.util.Rfc822Tokenizer;
-import android.util.Log;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.CursorAdapter;
-
-import com.android.ex.chips.BaseRecipientAdapter.DirectoryListQuery;
-import com.android.ex.chips.BaseRecipientAdapter.DirectorySearchParams;
-import com.android.ex.chips.DropdownChipLayouter.AdapterType;
-import com.android.ex.chips.Queries.Query;
-
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-
-/**
- * RecipientAlternatesAdapter backs the RecipientEditTextView for managing contacts
- * queried by email or by phone number.
- */
-public class RecipientAlternatesAdapter extends CursorAdapter {
-    static final int MAX_LOOKUPS = 50;
-
-    private final long mCurrentId;
-
-    private int mCheckedItemPosition = -1;
-
-    private OnCheckedItemChangedListener mCheckedItemChangedListener;
-
-    private static final String TAG = "RecipAlternates";
-
-    public static final int QUERY_TYPE_EMAIL = 0;
-    public static final int QUERY_TYPE_PHONE = 1;
-    private final Long mDirectoryId;
-    private DropdownChipLayouter mDropdownChipLayouter;
-
-    private static final Map<String, String> sCorrectedPhotoUris = new HashMap<String, String>();
-
-    public interface RecipientMatchCallback {
-        public void matchesFound(Map<String, RecipientEntry> results);
-        /**
-         * Called with all addresses that could not be resolved to valid recipients.
-         */
-        public void matchesNotFound(Set<String> unfoundAddresses);
-    }
-
-    public static void getMatchingRecipients(Context context, BaseRecipientAdapter adapter,
-            ArrayList<String> inAddresses, Account account, RecipientMatchCallback callback) {
-        getMatchingRecipients(context, adapter, inAddresses, QUERY_TYPE_EMAIL, account, callback);
-    }
-
-    /**
-     * Get a HashMap of address to RecipientEntry that contains all contact
-     * information for a contact with the provided address, if one exists. This
-     * may block the UI, so run it in an async task.
-     *
-     * @param context Context.
-     * @param inAddresses Array of addresses on which to perform the lookup.
-     * @param callback RecipientMatchCallback called when a match or matches are found.
-     * @return HashMap<String,RecipientEntry>
-     */
-    public static void getMatchingRecipients(Context context, BaseRecipientAdapter adapter,
-            ArrayList<String> inAddresses, int addressType, Account account,
-            RecipientMatchCallback callback) {
-        Queries.Query query;
-        if (addressType == QUERY_TYPE_EMAIL) {
-            query = Queries.EMAIL;
-        } else {
-            query = Queries.PHONE;
-        }
-        int addressesSize = Math.min(MAX_LOOKUPS, inAddresses.size());
-        HashSet<String> addresses = new HashSet<String>();
-        StringBuilder bindString = new StringBuilder();
-        // Create the "?" string and set up arguments.
-        for (int i = 0; i < addressesSize; i++) {
-            Rfc822Token[] tokens = Rfc822Tokenizer.tokenize(inAddresses.get(i).toLowerCase());
-            addresses.add(tokens.length > 0 ? tokens[0].getAddress() : inAddresses.get(i));
-            bindString.append("?");
-            if (i < addressesSize - 1) {
-                bindString.append(",");
-            }
-        }
-
-        if (Log.isLoggable(TAG, Log.DEBUG)) {
-            Log.d(TAG, "Doing reverse lookup for " + addresses.toString());
-        }
-
-        String[] addressArray = new String[addresses.size()];
-        addresses.toArray(addressArray);
-        HashMap<String, RecipientEntry> recipientEntries = null;
-        Cursor c = null;
-
-        try {
-            c = context.getContentResolver().query(
-                    query.getContentUri(),
-                    query.getProjection(),
-                    query.getProjection()[Queries.Query.DESTINATION] + " IN ("
-                            + bindString.toString() + ")", addressArray, null);
-            recipientEntries = processContactEntries(c, null /* directoryId */);
-            callback.matchesFound(recipientEntries);
-        } finally {
-            if (c != null) {
-                c.close();
-            }
-        }
-        // See if any entries did not resolve; if so, we need to check other
-        // directories
-        final Set<String> matchesNotFound = new HashSet<String>();
-        if (recipientEntries.size() < addresses.size()) {
-            final List<DirectorySearchParams> paramsList;
-            Cursor directoryCursor = null;
-            try {
-                directoryCursor = context.getContentResolver().query(DirectoryListQuery.URI,
-                        DirectoryListQuery.PROJECTION, null, null, null);
-                if (directoryCursor == null) {
-                    paramsList = null;
-                } else {
-                    paramsList = BaseRecipientAdapter.setupOtherDirectories(context,
-                            directoryCursor, account);
-                }
-            } finally {
-                if (directoryCursor != null) {
-                    directoryCursor.close();
-                }
-            }
-            // Run a directory query for each unmatched recipient.
-            HashSet<String> unresolvedAddresses = new HashSet<String>();
-            for (String address : addresses) {
-                if (!recipientEntries.containsKey(address)) {
-                    unresolvedAddresses.add(address);
-                }
-            }
-
-            matchesNotFound.addAll(unresolvedAddresses);
-
-            if (paramsList != null) {
-                Cursor directoryContactsCursor = null;
-                for (String unresolvedAddress : unresolvedAddresses) {
-                    Long directoryId = null;
-                    for (int i = 0; i < paramsList.size(); i++) {
-                        try {
-                            directoryContactsCursor = doQuery(unresolvedAddress, 1,
-                                    paramsList.get(i).directoryId, account,
-                                    context.getContentResolver(), query);
-                        } finally {
-                            if (directoryContactsCursor != null
-                                    && directoryContactsCursor.getCount() == 0) {
-                                directoryContactsCursor.close();
-                                directoryContactsCursor = null;
-                            } else {
-                                directoryId = paramsList.get(i).directoryId;
-                                break;
-                            }
-                        }
-                    }
-                    if (directoryContactsCursor != null) {
-                        try {
-                            final Map<String, RecipientEntry> entries =
-                                    processContactEntries(directoryContactsCursor, directoryId);
-
-                            for (final String address : entries.keySet()) {
-                                matchesNotFound.remove(address);
-                            }
-
-                            callback.matchesFound(entries);
-                        } finally {
-                            directoryContactsCursor.close();
-                        }
-                    }
-                }
-            }
-        }
-
-        // If no matches found in contact provider or the directories, try the extension
-        // matcher.
-        // todo (aalbert): This whole method needs to be in the adapter?
-        if (adapter != null) {
-            final Map<String, RecipientEntry> entries =
-                    adapter.getMatchingRecipients(matchesNotFound);
-            if (entries != null && entries.size() > 0) {
-                callback.matchesFound(entries);
-                for (final String address : entries.keySet()) {
-                    matchesNotFound.remove(address);
-                }
-            }
-        }
-        callback.matchesNotFound(matchesNotFound);
-    }
-
-    private static HashMap<String, RecipientEntry> processContactEntries(Cursor c,
-            Long directoryId) {
-        HashMap<String, RecipientEntry> recipientEntries = new HashMap<String, RecipientEntry>();
-        if (c != null && c.moveToFirst()) {
-            do {
-                String address = c.getString(Queries.Query.DESTINATION);
-
-                final RecipientEntry newRecipientEntry = RecipientEntry.constructTopLevelEntry(
-                        c.getString(Queries.Query.NAME),
-                        c.getInt(Queries.Query.DISPLAY_NAME_SOURCE),
-                        c.getString(Queries.Query.DESTINATION),
-                        c.getInt(Queries.Query.DESTINATION_TYPE),
-                        c.getString(Queries.Query.DESTINATION_LABEL),
-                        c.getLong(Queries.Query.CONTACT_ID),
-                        directoryId,
-                        c.getLong(Queries.Query.DATA_ID),
-                        c.getString(Queries.Query.PHOTO_THUMBNAIL_URI),
-                        true,
-                        c.getString(Queries.Query.LOOKUP_KEY));
-
-                /*
-                 * In certain situations, we may have two results for one address, where one of the
-                 * results is just the email address, and the other has a name and photo, so we want
-                 * to use the better one.
-                 */
-                final RecipientEntry recipientEntry =
-                        getBetterRecipient(recipientEntries.get(address), newRecipientEntry);
-
-                recipientEntries.put(address, recipientEntry);
-                if (Log.isLoggable(TAG, Log.DEBUG)) {
-                    Log.d(TAG, "Received reverse look up information for " + address
-                            + " RESULTS: "
-                            + " NAME : " + c.getString(Queries.Query.NAME)
-                            + " CONTACT ID : " + c.getLong(Queries.Query.CONTACT_ID)
-                            + " ADDRESS :" + c.getString(Queries.Query.DESTINATION));
-                }
-            } while (c.moveToNext());
-        }
-        return recipientEntries;
-    }
-
-    /**
-     * Given two {@link RecipientEntry}s for the same email address, this will return the one that
-     * contains more complete information for display purposes. Defaults to <code>entry2</code> if
-     * no significant differences are found.
-     */
-    static RecipientEntry getBetterRecipient(final RecipientEntry entry1,
-            final RecipientEntry entry2) {
-        // If only one has passed in, use it
-        if (entry2 == null) {
-            return entry1;
-        }
-
-        if (entry1 == null) {
-            return entry2;
-        }
-
-        // If only one has a display name, use it
-        if (!TextUtils.isEmpty(entry1.getDisplayName())
-                && TextUtils.isEmpty(entry2.getDisplayName())) {
-            return entry1;
-        }
-
-        if (!TextUtils.isEmpty(entry2.getDisplayName())
-                && TextUtils.isEmpty(entry1.getDisplayName())) {
-            return entry2;
-        }
-
-        // If only one has a display name that is not the same as the destination, use it
-        if (!TextUtils.equals(entry1.getDisplayName(), entry1.getDestination())
-                && TextUtils.equals(entry2.getDisplayName(), entry2.getDestination())) {
-            return entry1;
-        }
-
-        if (!TextUtils.equals(entry2.getDisplayName(), entry2.getDestination())
-                && TextUtils.equals(entry1.getDisplayName(), entry1.getDestination())) {
-            return entry2;
-        }
-
-        // If only one has a photo, use it
-        if ((entry1.getPhotoThumbnailUri() != null || entry1.getPhotoBytes() != null)
-                && (entry2.getPhotoThumbnailUri() == null && entry2.getPhotoBytes() == null)) {
-            return entry1;
-        }
-
-        if ((entry2.getPhotoThumbnailUri() != null || entry2.getPhotoBytes() != null)
-                && (entry1.getPhotoThumbnailUri() == null && entry1.getPhotoBytes() == null)) {
-            return entry2;
-        }
-
-        // Go with the second option as a default
-        return entry2;
-    }
-
-    private static Cursor doQuery(CharSequence constraint, int limit, Long directoryId,
-            Account account, ContentResolver resolver, Query query) {
-        final Uri.Builder builder = query
-                .getContentFilterUri()
-                .buildUpon()
-                .appendPath(constraint.toString())
-                .appendQueryParameter(ContactsContract.LIMIT_PARAM_KEY,
-                        String.valueOf(limit + BaseRecipientAdapter.ALLOWANCE_FOR_DUPLICATES));
-        if (directoryId != null) {
-            builder.appendQueryParameter(ContactsContract.DIRECTORY_PARAM_KEY,
-                    String.valueOf(directoryId));
-        }
-        if (account != null) {
-            builder.appendQueryParameter(BaseRecipientAdapter.PRIMARY_ACCOUNT_NAME, account.name);
-            builder.appendQueryParameter(BaseRecipientAdapter.PRIMARY_ACCOUNT_TYPE, account.type);
-        }
-        final Cursor cursor = resolver.query(builder.build(), query.getProjection(), null, null,
-                null);
-        return cursor;
-    }
-
-    public RecipientAlternatesAdapter(Context context, long contactId, Long directoryId,
-            String lookupKey, long currentId, int queryMode, OnCheckedItemChangedListener listener,
-            DropdownChipLayouter dropdownChipLayouter) {
-        super(context,
-                getCursorForConstruction(context, contactId, directoryId, lookupKey, queryMode), 0);
-        mCurrentId = currentId;
-        mDirectoryId = directoryId;
-        mCheckedItemChangedListener = listener;
-
-        mDropdownChipLayouter = dropdownChipLayouter;
-    }
-
-    private static Cursor getCursorForConstruction(Context context, long contactId,
-            Long directoryId, String lookupKey, int queryType) {
-        final Cursor cursor;
-        final String desiredMimeType;
-        if (queryType == QUERY_TYPE_EMAIL) {
-            final Uri uri;
-            final StringBuilder selection = new StringBuilder();
-            selection.append(Queries.EMAIL.getProjection()[Queries.Query.CONTACT_ID]);
-            selection.append(" = ?");
-
-            if (directoryId == null || lookupKey == null) {
-                uri = Queries.EMAIL.getContentUri();
-                desiredMimeType = null;
-            } else {
-                final Uri.Builder builder = Contacts.getLookupUri(contactId, lookupKey).buildUpon();
-                builder.appendPath(Contacts.Entity.CONTENT_DIRECTORY)
-                        .appendQueryParameter(ContactsContract.DIRECTORY_PARAM_KEY,
-                                String.valueOf(directoryId));
-                uri = builder.build();
-                desiredMimeType = ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE;
-            }
-            cursor = context.getContentResolver().query(
-                    uri,
-                    Queries.EMAIL.getProjection(),
-                    selection.toString(), new String[] {
-                        String.valueOf(contactId)
-                    }, null);
-        } else {
-            final Uri uri;
-            final StringBuilder selection = new StringBuilder();
-            selection.append(Queries.PHONE.getProjection()[Queries.Query.CONTACT_ID]);
-            selection.append(" = ?");
-
-            if (lookupKey == null) {
-                uri = Queries.PHONE.getContentUri();
-                desiredMimeType = null;
-            } else {
-                final Uri.Builder builder = Contacts.getLookupUri(contactId, lookupKey).buildUpon();
-                builder.appendPath(Contacts.Entity.CONTENT_DIRECTORY)
-                        .appendQueryParameter(ContactsContract.DIRECTORY_PARAM_KEY,
-                                String.valueOf(directoryId));
-                uri = builder.build();
-                desiredMimeType = ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE;
-            }
-            cursor = context.getContentResolver().query(
-                    uri,
-                    Queries.PHONE.getProjection(),
-                    selection.toString(), new String[] {
-                        String.valueOf(contactId)
-                    }, null);
-        }
-
-        final Cursor resultCursor = removeUndesiredDestinations(cursor, desiredMimeType, lookupKey);
-        cursor.close();
-
-        return resultCursor;
-    }
-
-    /**
-     * @return a new cursor based on the given cursor with all duplicate destinations removed.
-     *
-     * It's only intended to use for the alternate list, so...
-     * - This method ignores all other fields and dedupe solely on the destination.  Normally,
-     * if a cursor contains multiple contacts and they have the same destination, we'd still want
-     * to show both.
-     * - This method creates a MatrixCursor, so all data will be kept in memory.  We wouldn't want
-     * to do this if the original cursor is large, but it's okay here because the alternate list
-     * won't be that big.
-     *
-     * @param desiredMimeType If this is non-<code>null</code>, only entries with this mime type
-     *            will be added to the cursor
-     * @param lookupKey The lookup key used for this contact if there isn't one in the cursor. This
-     *            should be the same one used in the query that returned the cursor
-     */
-    // Visible for testing
-    static Cursor removeUndesiredDestinations(final Cursor original, final String desiredMimeType,
-            final String lookupKey) {
-        final MatrixCursor result = new MatrixCursor(
-                original.getColumnNames(), original.getCount());
-        final HashSet<String> destinationsSeen = new HashSet<String>();
-
-        String defaultDisplayName = null;
-        String defaultPhotoThumbnailUri = null;
-        int defaultDisplayNameSource = 0;
-
-        // Find some nice defaults in case we need them
-        original.moveToPosition(-1);
-        while (original.moveToNext()) {
-            final String mimeType = original.getString(Query.MIME_TYPE);
-
-            if (ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE.equals(
-                    mimeType)) {
-                // Store this data
-                defaultDisplayName = original.getString(Query.NAME);
-                defaultPhotoThumbnailUri = original.getString(Query.PHOTO_THUMBNAIL_URI);
-                defaultDisplayNameSource = original.getInt(Query.DISPLAY_NAME_SOURCE);
-                break;
-            }
-        }
-
-        original.moveToPosition(-1);
-        while (original.moveToNext()) {
-            if (desiredMimeType != null) {
-                final String mimeType = original.getString(Query.MIME_TYPE);
-                if (!desiredMimeType.equals(mimeType)) {
-                    continue;
-                }
-            }
-            final String destination = original.getString(Query.DESTINATION);
-            if (destinationsSeen.contains(destination)) {
-                continue;
-            }
-            destinationsSeen.add(destination);
-
-            final Object[] row = new Object[] {
-                    original.getString(Query.NAME),
-                    original.getString(Query.DESTINATION),
-                    original.getInt(Query.DESTINATION_TYPE),
-                    original.getString(Query.DESTINATION_LABEL),
-                    original.getLong(Query.CONTACT_ID),
-                    original.getLong(Query.DATA_ID),
-                    original.getString(Query.PHOTO_THUMBNAIL_URI),
-                    original.getInt(Query.DISPLAY_NAME_SOURCE),
-                    original.getString(Query.LOOKUP_KEY),
-                    original.getString(Query.MIME_TYPE)
-            };
-
-            if (row[Query.NAME] == null) {
-                row[Query.NAME] = defaultDisplayName;
-            }
-            if (row[Query.PHOTO_THUMBNAIL_URI] == null) {
-                row[Query.PHOTO_THUMBNAIL_URI] = defaultPhotoThumbnailUri;
-            }
-            if ((Integer) row[Query.DISPLAY_NAME_SOURCE] == 0) {
-                row[Query.DISPLAY_NAME_SOURCE] = defaultDisplayNameSource;
-            }
-            if (row[Query.LOOKUP_KEY] == null) {
-                row[Query.LOOKUP_KEY] = lookupKey;
-            }
-
-            // Ensure we don't have two '?' like content://.../...?account_name=...?sz=...
-            final String photoThumbnailUri = (String) row[Query.PHOTO_THUMBNAIL_URI];
-            if (photoThumbnailUri != null) {
-                if (sCorrectedPhotoUris.containsKey(photoThumbnailUri)) {
-                    row[Query.PHOTO_THUMBNAIL_URI] = sCorrectedPhotoUris.get(photoThumbnailUri);
-                } else if (photoThumbnailUri.indexOf('?') != photoThumbnailUri.lastIndexOf('?')) {
-                    final String[] parts = photoThumbnailUri.split("\\?");
-                    final StringBuilder correctedUriBuilder = new StringBuilder();
-                    for (int i = 0; i < parts.length; i++) {
-                        if (i == 1) {
-                            correctedUriBuilder.append("?"); // We only want one of these
-                        } else if (i > 1) {
-                            correctedUriBuilder.append("&"); // And we want these elsewhere
-                        }
-                        correctedUriBuilder.append(parts[i]);
-                    }
-
-                    final String correctedUri = correctedUriBuilder.toString();
-                    sCorrectedPhotoUris.put(photoThumbnailUri, correctedUri);
-                    row[Query.PHOTO_THUMBNAIL_URI] = correctedUri;
-                }
-            }
-
-            result.addRow(row);
-        }
-
-        return result;
-    }
-
-    @Override
-    public long getItemId(int position) {
-        Cursor c = getCursor();
-        if (c.moveToPosition(position)) {
-            c.getLong(Queries.Query.DATA_ID);
-        }
-        return -1;
-    }
-
-    public RecipientEntry getRecipientEntry(int position) {
-        Cursor c = getCursor();
-        c.moveToPosition(position);
-        return RecipientEntry.constructTopLevelEntry(
-                c.getString(Queries.Query.NAME),
-                c.getInt(Queries.Query.DISPLAY_NAME_SOURCE),
-                c.getString(Queries.Query.DESTINATION),
-                c.getInt(Queries.Query.DESTINATION_TYPE),
-                c.getString(Queries.Query.DESTINATION_LABEL),
-                c.getLong(Queries.Query.CONTACT_ID),
-                mDirectoryId,
-                c.getLong(Queries.Query.DATA_ID),
-                c.getString(Queries.Query.PHOTO_THUMBNAIL_URI),
-                true,
-                c.getString(Queries.Query.LOOKUP_KEY));
-    }
-
-    @Override
-    public View getView(int position, View convertView, ViewGroup parent) {
-        Cursor cursor = getCursor();
-        cursor.moveToPosition(position);
-        if (convertView == null) {
-            convertView = mDropdownChipLayouter.newView();
-        }
-        if (cursor.getLong(Queries.Query.DATA_ID) == mCurrentId) {
-            mCheckedItemPosition = position;
-            if (mCheckedItemChangedListener != null) {
-                mCheckedItemChangedListener.onCheckedItemChanged(mCheckedItemPosition);
-            }
-        }
-        bindView(convertView, convertView.getContext(), cursor);
-        return convertView;
-    }
-
-    @Override
-    public void bindView(View view, Context context, Cursor cursor) {
-        int position = cursor.getPosition();
-        RecipientEntry entry = getRecipientEntry(position);
-
-        mDropdownChipLayouter.bindView(view, null, entry, position,
-                AdapterType.RECIPIENT_ALTERNATES, null);
-    }
-
-    @Override
-    public View newView(Context context, Cursor cursor, ViewGroup parent) {
-        return mDropdownChipLayouter.newView();
-    }
-
-    /*package*/ static interface OnCheckedItemChangedListener {
-        public void onCheckedItemChanged(int position);
-    }
-}
diff --git a/chips/src/com/android/ex/chips/RecipientEditTextView.java b/chips/src/com/android/ex/chips/RecipientEditTextView.java
deleted file mode 100644
index 4339b9e..0000000
--- a/chips/src/com/android/ex/chips/RecipientEditTextView.java
+++ /dev/null
@@ -1,2988 +0,0 @@
-/*
-
- * 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.android.ex.chips;
-
-import android.app.Dialog;
-import android.content.ClipData;
-import android.content.ClipDescription;
-import android.content.ClipboardManager;
-import android.content.Context;
-import android.content.DialogInterface;
-import android.content.DialogInterface.OnDismissListener;
-import android.content.res.Resources;
-import android.content.res.TypedArray;
-import android.graphics.Bitmap;
-import android.graphics.BitmapFactory;
-import android.graphics.Canvas;
-import android.graphics.Matrix;
-import android.graphics.Paint;
-import android.graphics.Point;
-import android.graphics.Rect;
-import android.graphics.RectF;
-import android.graphics.drawable.BitmapDrawable;
-import android.graphics.drawable.Drawable;
-import android.os.AsyncTask;
-import android.os.Build;
-import android.os.Handler;
-import android.os.Looper;
-import android.os.Message;
-import android.os.Parcelable;
-import android.text.Editable;
-import android.text.InputType;
-import android.text.Layout;
-import android.text.Spannable;
-import android.text.SpannableString;
-import android.text.SpannableStringBuilder;
-import android.text.Spanned;
-import android.text.TextPaint;
-import android.text.TextUtils;
-import android.text.TextWatcher;
-import android.text.method.QwertyKeyListener;
-import android.text.style.ImageSpan;
-import android.text.util.Rfc822Token;
-import android.text.util.Rfc822Tokenizer;
-import android.util.AttributeSet;
-import android.util.Log;
-import android.util.TypedValue;
-import android.view.ActionMode;
-import android.view.ActionMode.Callback;
-import android.view.DragEvent;
-import android.view.GestureDetector;
-import android.view.KeyEvent;
-import android.view.LayoutInflater;
-import android.view.Menu;
-import android.view.MenuItem;
-import android.view.MotionEvent;
-import android.view.View;
-import android.view.View.OnClickListener;
-import android.view.ViewParent;
-import android.view.inputmethod.EditorInfo;
-import android.view.inputmethod.InputConnection;
-import android.widget.AdapterView;
-import android.widget.AdapterView.OnItemClickListener;
-import android.widget.Button;
-import android.widget.Filterable;
-import android.widget.ListAdapter;
-import android.widget.ListPopupWindow;
-import android.widget.ListView;
-import android.widget.MultiAutoCompleteTextView;
-import android.widget.ScrollView;
-import android.widget.TextView;
-
-import com.android.ex.chips.RecipientAlternatesAdapter.RecipientMatchCallback;
-import com.android.ex.chips.recipientchip.DrawableRecipientChip;
-import com.android.ex.chips.recipientchip.InvisibleRecipientChip;
-import com.android.ex.chips.recipientchip.VisibleRecipientChip;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-/**
- * RecipientEditTextView is an auto complete text view for use with applications
- * that use the new Chips UI for addressing a message to recipients.
- */
-public class RecipientEditTextView extends MultiAutoCompleteTextView implements
-        OnItemClickListener, Callback, RecipientAlternatesAdapter.OnCheckedItemChangedListener,
-        GestureDetector.OnGestureListener, OnDismissListener, OnClickListener,
-        TextView.OnEditorActionListener {
-
-    private static final char COMMIT_CHAR_COMMA = ',';
-
-    private static final char COMMIT_CHAR_SEMICOLON = ';';
-
-    private static final char COMMIT_CHAR_SPACE = ' ';
-
-    private static final String SEPARATOR = String.valueOf(COMMIT_CHAR_COMMA)
-            + String.valueOf(COMMIT_CHAR_SPACE);
-
-    private static final String TAG = "RecipientEditTextView";
-
-    private static int DISMISS = "dismiss".hashCode();
-
-    private static final long DISMISS_DELAY = 300;
-
-    // TODO: get correct number/ algorithm from with UX.
-    // Visible for testing.
-    /*package*/ static final int CHIP_LIMIT = 2;
-
-    private static final int MAX_CHIPS_PARSED = 50;
-
-    private static int sSelectedTextColor = -1;
-
-    // Resources for displaying chips.
-    private Drawable mChipBackground = null;
-
-    private Drawable mChipDelete = null;
-
-    private Drawable mInvalidChipBackground;
-
-    private Drawable mChipBackgroundPressed;
-
-    private float mChipHeight;
-
-    private float mChipFontSize;
-
-    private float mLineSpacingExtra;
-
-    private int mChipPadding;
-
-    /**
-     * Enumerator for avatar position. See attr.xml for more details.
-     * 0 for end, 1 for start.
-     */
-    private int mAvatarPosition;
-
-    private static final int AVATAR_POSITION_END = 0;
-
-    private static final int AVATAR_POSITION_START = 1;
-
-    /**
-     * Enumerator for image span alignment. See attr.xml for more details.
-     * 0 for bottom, 1 for baseline.
-     */
-    private int mImageSpanAlignment;
-
-    private static final int IMAGE_SPAN_ALIGNMENT_BOTTOM = 0;
-
-    private static final int IMAGE_SPAN_ALIGNMENT_BASELINE = 1;
-
-
-    private boolean mDisableDelete;
-
-    private Tokenizer mTokenizer;
-
-    private Validator mValidator;
-
-    private DrawableRecipientChip mSelectedChip;
-
-    private Bitmap mDefaultContactPhoto;
-
-    private ImageSpan mMoreChip;
-
-    private TextView mMoreItem;
-
-    // VisibleForTesting
-    final ArrayList<String> mPendingChips = new ArrayList<String>();
-
-    private Handler mHandler;
-
-    private int mPendingChipsCount = 0;
-
-    private boolean mNoChips = false;
-
-    private ListPopupWindow mAlternatesPopup;
-
-    private ListPopupWindow mAddressPopup;
-
-    // VisibleForTesting
-    ArrayList<DrawableRecipientChip> mTemporaryRecipients;
-
-    private ArrayList<DrawableRecipientChip> mRemovedSpans;
-
-    private boolean mShouldShrink = true;
-
-    // Chip copy fields.
-    private GestureDetector mGestureDetector;
-
-    private Dialog mCopyDialog;
-
-    private String mCopyAddress;
-
-    /**
-     * Used with {@link #mAlternatesPopup}. Handles clicks to alternate addresses for a
-     * selected chip.
-     */
-    private OnItemClickListener mAlternatesListener;
-
-    private int mCheckedItem;
-
-    private TextWatcher mTextWatcher;
-
-    // Obtain the enclosing scroll view, if it exists, so that the view can be
-    // scrolled to show the last line of chips content.
-    private ScrollView mScrollView;
-
-    private boolean mTriedGettingScrollView;
-
-    private boolean mDragEnabled = false;
-
-    // This pattern comes from android.util.Patterns. It has been tweaked to handle a "1" before
-    // parens, so numbers such as "1 (425) 222-2342" match.
-    private static final Pattern PHONE_PATTERN
-        = Pattern.compile(                                  // sdd = space, dot, or dash
-                "(\\+[0-9]+[\\- \\.]*)?"                    // +<digits><sdd>*
-                + "(1?[ ]*\\([0-9]+\\)[\\- \\.]*)?"         // 1(<digits>)<sdd>*
-                + "([0-9][0-9\\- \\.][0-9\\- \\.]+[0-9])"); // <digit><digit|sdd>+<digit>
-
-    private final Runnable mAddTextWatcher = new Runnable() {
-        @Override
-        public void run() {
-            if (mTextWatcher == null) {
-                mTextWatcher = new RecipientTextWatcher();
-                addTextChangedListener(mTextWatcher);
-            }
-        }
-    };
-
-    private IndividualReplacementTask mIndividualReplacements;
-
-    private Runnable mHandlePendingChips = new Runnable() {
-
-        @Override
-        public void run() {
-            handlePendingChips();
-        }
-
-    };
-
-    private Runnable mDelayedShrink = new Runnable() {
-
-        @Override
-        public void run() {
-            shrink();
-        }
-
-    };
-
-    private int mMaxLines;
-
-    private static int sExcessTopPadding = -1;
-
-    private int mActionBarHeight;
-
-    private boolean mAttachedToWindow;
-
-    private DropdownChipLayouter mDropdownChipLayouter;
-
-    public RecipientEditTextView(Context context, AttributeSet attrs) {
-        super(context, attrs);
-        setChipDimensions(context, attrs);
-        if (sSelectedTextColor == -1) {
-            sSelectedTextColor = context.getResources().getColor(android.R.color.white);
-        }
-        mAlternatesPopup = new ListPopupWindow(context);
-        mAddressPopup = new ListPopupWindow(context);
-        mCopyDialog = new Dialog(context);
-        mAlternatesListener = new OnItemClickListener() {
-            @Override
-            public void onItemClick(AdapterView<?> adapterView,View view, int position,
-                    long rowId) {
-                mAlternatesPopup.setOnItemClickListener(null);
-                replaceChip(mSelectedChip, ((RecipientAlternatesAdapter) adapterView.getAdapter())
-                        .getRecipientEntry(position));
-                Message delayed = Message.obtain(mHandler, DISMISS);
-                delayed.obj = mAlternatesPopup;
-                mHandler.sendMessageDelayed(delayed, DISMISS_DELAY);
-                clearComposingText();
-            }
-        };
-        setInputType(getInputType() | InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS);
-        setOnItemClickListener(this);
-        setCustomSelectionActionModeCallback(this);
-        mHandler = new Handler() {
-            @Override
-            public void handleMessage(Message msg) {
-                if (msg.what == DISMISS) {
-                    ((ListPopupWindow) msg.obj).dismiss();
-                    return;
-                }
-                super.handleMessage(msg);
-            }
-        };
-        mTextWatcher = new RecipientTextWatcher();
-        addTextChangedListener(mTextWatcher);
-        mGestureDetector = new GestureDetector(context, this);
-        setOnEditorActionListener(this);
-
-        setDropdownChipLayouter(new DropdownChipLayouter(LayoutInflater.from(context), context));
-    }
-
-    protected void setDropdownChipLayouter(DropdownChipLayouter dropdownChipLayouter) {
-        mDropdownChipLayouter = dropdownChipLayouter;
-    }
-
-    @Override
-    protected void onDetachedFromWindow() {
-        super.onDetachedFromWindow();
-        mAttachedToWindow = false;
-    }
-
-    @Override
-    protected void onAttachedToWindow() {
-        super.onAttachedToWindow();
-        mAttachedToWindow = true;
-    }
-
-    @Override
-    public boolean onEditorAction(TextView view, int action, KeyEvent keyEvent) {
-        if (action == EditorInfo.IME_ACTION_DONE) {
-            if (commitDefault()) {
-                return true;
-            }
-            if (mSelectedChip != null) {
-                clearSelectedChip();
-                return true;
-            } else if (focusNext()) {
-                return true;
-            }
-        }
-        return false;
-    }
-
-    @Override
-    public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
-        InputConnection connection = super.onCreateInputConnection(outAttrs);
-        int imeActions = outAttrs.imeOptions&EditorInfo.IME_MASK_ACTION;
-        if ((imeActions&EditorInfo.IME_ACTION_DONE) != 0) {
-            // clear the existing action
-            outAttrs.imeOptions ^= imeActions;
-            // set the DONE action
-            outAttrs.imeOptions |= EditorInfo.IME_ACTION_DONE;
-        }
-        if ((outAttrs.imeOptions&EditorInfo.IME_FLAG_NO_ENTER_ACTION) != 0) {
-            outAttrs.imeOptions &= ~EditorInfo.IME_FLAG_NO_ENTER_ACTION;
-        }
-
-        outAttrs.actionId = EditorInfo.IME_ACTION_DONE;
-        outAttrs.actionLabel = getContext().getString(R.string.done);
-        return connection;
-    }
-
-    /*package*/ DrawableRecipientChip getLastChip() {
-        DrawableRecipientChip last = null;
-        DrawableRecipientChip[] chips = getSortedRecipients();
-        if (chips != null && chips.length > 0) {
-            last = chips[chips.length - 1];
-        }
-        return last;
-    }
-
-    @Override
-    public void onSelectionChanged(int start, int end) {
-        // When selection changes, see if it is inside the chips area.
-        // If so, move the cursor back after the chips again.
-        DrawableRecipientChip last = getLastChip();
-        if (last != null && start < getSpannable().getSpanEnd(last)) {
-            // Grab the last chip and set the cursor to after it.
-            setSelection(Math.min(getSpannable().getSpanEnd(last) + 1, getText().length()));
-        }
-        super.onSelectionChanged(start, end);
-    }
-
-    @Override
-    public void onRestoreInstanceState(Parcelable state) {
-        if (!TextUtils.isEmpty(getText())) {
-            super.onRestoreInstanceState(null);
-        } else {
-            super.onRestoreInstanceState(state);
-        }
-    }
-
-    @Override
-    public Parcelable onSaveInstanceState() {
-        // If the user changes orientation while they are editing, just roll back the selection.
-        clearSelectedChip();
-        return super.onSaveInstanceState();
-    }
-
-    /**
-     * Convenience method: Append the specified text slice to the TextView's
-     * display buffer, upgrading it to BufferType.EDITABLE if it was
-     * not already editable. Commas are excluded as they are added automatically
-     * by the view.
-     */
-    @Override
-    public void append(CharSequence text, int start, int end) {
-        // We don't care about watching text changes while appending.
-        if (mTextWatcher != null) {
-            removeTextChangedListener(mTextWatcher);
-        }
-        super.append(text, start, end);
-        if (!TextUtils.isEmpty(text) && TextUtils.getTrimmedLength(text) > 0) {
-            String displayString = text.toString();
-
-            if (!displayString.trim().endsWith(String.valueOf(COMMIT_CHAR_COMMA))) {
-                // We have no separator, so we should add it
-                super.append(SEPARATOR, 0, SEPARATOR.length());
-                displayString += SEPARATOR;
-            }
-
-            if (!TextUtils.isEmpty(displayString)
-                    && TextUtils.getTrimmedLength(displayString) > 0) {
-                mPendingChipsCount++;
-                mPendingChips.add(displayString);
-            }
-        }
-        // Put a message on the queue to make sure we ALWAYS handle pending
-        // chips.
-        if (mPendingChipsCount > 0) {
-            postHandlePendingChips();
-        }
-        mHandler.post(mAddTextWatcher);
-    }
-
-    @Override
-    public void onFocusChanged(boolean hasFocus, int direction, Rect previous) {
-        super.onFocusChanged(hasFocus, direction, previous);
-        if (!hasFocus) {
-            shrink();
-        } else {
-            expand();
-        }
-    }
-
-    private int getExcessTopPadding() {
-        if (sExcessTopPadding == -1) {
-            sExcessTopPadding = (int) (mChipHeight + mLineSpacingExtra);
-        }
-        return sExcessTopPadding;
-    }
-
-    @Override
-    public <T extends ListAdapter & Filterable> void setAdapter(T adapter) {
-        super.setAdapter(adapter);
-        BaseRecipientAdapter baseAdapter = (BaseRecipientAdapter) adapter;
-        baseAdapter.registerUpdateObserver(new BaseRecipientAdapter.EntriesUpdatedObserver() {
-            @Override
-            public void onChanged(List<RecipientEntry> entries) {
-                // Scroll the chips field to the top of the screen so
-                // that the user can see as many results as possible.
-                if (entries != null && entries.size() > 0) {
-                    scrollBottomIntoView();
-                }
-            }
-        });
-        baseAdapter.setDropdownChipLayouter(mDropdownChipLayouter);
-    }
-
-    protected void scrollBottomIntoView() {
-        if (mScrollView != null && mShouldShrink) {
-            int[] location = new int[2];
-            getLocationOnScreen(location);
-            int height = getHeight();
-            int currentPos = location[1] + height;
-            // Desired position shows at least 1 line of chips below the action
-            // bar. We add excess padding to make sure this is always below other
-            // content.
-            int desiredPos = (int) mChipHeight + mActionBarHeight + getExcessTopPadding();
-            if (currentPos > desiredPos) {
-                mScrollView.scrollBy(0, currentPos - desiredPos);
-            }
-        }
-    }
-
-    protected ScrollView getScrollView() {
-        return mScrollView;
-    }
-
-    @Override
-    public void performValidation() {
-        // Do nothing. Chips handles its own validation.
-    }
-
-    private void shrink() {
-        if (mTokenizer == null) {
-            return;
-        }
-        long contactId = mSelectedChip != null ? mSelectedChip.getEntry().getContactId() : -1;
-        if (mSelectedChip != null && contactId != RecipientEntry.INVALID_CONTACT
-                && (!isPhoneQuery() && contactId != RecipientEntry.GENERATED_CONTACT)) {
-            clearSelectedChip();
-        } else {
-            if (getWidth() <= 0) {
-                // We don't have the width yet which means the view hasn't been drawn yet
-                // and there is no reason to attempt to commit chips yet.
-                // This focus lost must be the result of an orientation change
-                // or an initial rendering.
-                // Re-post the shrink for later.
-                mHandler.removeCallbacks(mDelayedShrink);
-                mHandler.post(mDelayedShrink);
-                return;
-            }
-            // Reset any pending chips as they would have been handled
-            // when the field lost focus.
-            if (mPendingChipsCount > 0) {
-                postHandlePendingChips();
-            } else {
-                Editable editable = getText();
-                int end = getSelectionEnd();
-                int start = mTokenizer.findTokenStart(editable, end);
-                DrawableRecipientChip[] chips =
-                        getSpannable().getSpans(start, end, DrawableRecipientChip.class);
-                if ((chips == null || chips.length == 0)) {
-                    Editable text = getText();
-                    int whatEnd = mTokenizer.findTokenEnd(text, start);
-                    // This token was already tokenized, so skip past the ending token.
-                    if (whatEnd < text.length() && text.charAt(whatEnd) == ',') {
-                        whatEnd = movePastTerminators(whatEnd);
-                    }
-                    // In the middle of chip; treat this as an edit
-                    // and commit the whole token.
-                    int selEnd = getSelectionEnd();
-                    if (whatEnd != selEnd) {
-                        handleEdit(start, whatEnd);
-                    } else {
-                        commitChip(start, end, editable);
-                    }
-                }
-            }
-            mHandler.post(mAddTextWatcher);
-        }
-        createMoreChip();
-    }
-
-    private void expand() {
-        if (mShouldShrink) {
-            setMaxLines(Integer.MAX_VALUE);
-        }
-        removeMoreChip();
-        setCursorVisible(true);
-        Editable text = getText();
-        setSelection(text != null && text.length() > 0 ? text.length() : 0);
-        // If there are any temporary chips, try replacing them now that the user
-        // has expanded the field.
-        if (mTemporaryRecipients != null && mTemporaryRecipients.size() > 0) {
-            new RecipientReplacementTask().execute();
-            mTemporaryRecipients = null;
-        }
-    }
-
-    private CharSequence ellipsizeText(CharSequence text, TextPaint paint, float maxWidth) {
-        paint.setTextSize(mChipFontSize);
-        if (maxWidth <= 0 && Log.isLoggable(TAG, Log.DEBUG)) {
-            Log.d(TAG, "Max width is negative: " + maxWidth);
-        }
-        return TextUtils.ellipsize(text, paint, maxWidth,
-                TextUtils.TruncateAt.END);
-    }
-
-    /**
-     * Creates a bitmap of the given contact on a selected chip.
-     *
-     * @param contact The recipient entry to pull data from.
-     * @param paint The paint to use to draw the bitmap.
-     */
-    private Bitmap createSelectedChip(RecipientEntry contact, TextPaint paint) {
-        paint.setColor(sSelectedTextColor);
-        Bitmap photo;
-        if (mDisableDelete) {
-            // Show the avatar instead if we don't want to delete
-            photo = getAvatarIcon(contact);
-        } else {
-            photo = ((BitmapDrawable) mChipDelete).getBitmap();
-        }
-        return createChipBitmap(contact, paint, photo, mChipBackgroundPressed);
-    }
-
-    /**
-     * Creates a bitmap of the given contact on a selected chip.
-     *
-     * @param contact The recipient entry to pull data from.
-     * @param paint The paint to use to draw the bitmap.
-     */
-    // TODO: Is leaveBlankIconSpacer obsolete now that we have left and right attributes?
-    private Bitmap createUnselectedChip(RecipientEntry contact, TextPaint paint,
-            boolean leaveBlankIconSpacer) {
-        Drawable background = getChipBackground(contact);
-        Bitmap photo = getAvatarIcon(contact);
-        paint.setColor(getContext().getResources().getColor(android.R.color.black));
-        return createChipBitmap(contact, paint, photo, background);
-    }
-
-    private Bitmap createChipBitmap(RecipientEntry contact, TextPaint paint, Bitmap icon,
-        Drawable background) {
-        if (background == null) {
-            Log.w(TAG, "Unable to draw a background for the chips as it was never set");
-            return Bitmap.createBitmap(
-                    (int) mChipHeight * 2, (int) mChipHeight, Bitmap.Config.ARGB_8888);
-        }
-
-        Rect backgroundPadding = new Rect();
-        background.getPadding(backgroundPadding);
-
-        // Ellipsize the text so that it takes AT MOST the entire width of the
-        // autocomplete text entry area. Make sure to leave space for padding
-        // on the sides.
-        int height = (int) mChipHeight;
-        // Since the icon is a square, it's width is equal to the maximum height it can be inside
-        // the chip.
-        int iconWidth = height - backgroundPadding.top - backgroundPadding.bottom;
-        float[] widths = new float[1];
-        paint.getTextWidths(" ", widths);
-        CharSequence ellipsizedText = ellipsizeText(createChipDisplayText(contact), paint,
-                calculateAvailableWidth() - iconWidth - widths[0] - backgroundPadding.left
-                    - backgroundPadding.right);;
-        int textWidth = (int) paint.measureText(ellipsizedText, 0, ellipsizedText.length());
-
-        // Make sure there is a minimum chip width so the user can ALWAYS
-        // tap a chip without difficulty.
-        int width = Math.max(iconWidth * 2, textWidth + (mChipPadding * 2) + iconWidth
-                + backgroundPadding.left + backgroundPadding.right);
-
-        // Create the background of the chip.
-        Bitmap tmpBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
-        Canvas canvas = new Canvas(tmpBitmap);
-
-        // Draw the background drawable
-        background.setBounds(0, 0, width, height);
-        background.draw(canvas);
-        // Draw the text vertically aligned
-        int textX = shouldPositionAvatarOnRight() ?
-                mChipPadding + backgroundPadding.left :
-                width - backgroundPadding.right - mChipPadding - textWidth;
-        canvas.drawText(ellipsizedText, 0, ellipsizedText.length(),
-                textX, getTextYOffset(ellipsizedText.toString(), paint, height), paint);
-        if (icon != null) {
-            // Draw the icon
-            int iconX = shouldPositionAvatarOnRight() ?
-                    width - backgroundPadding.right - iconWidth :
-                    backgroundPadding.left;
-            RectF src = new RectF(0, 0, icon.getWidth(), icon.getHeight());
-            RectF dst = new RectF(iconX,
-                    0 + backgroundPadding.top,
-                    iconX + iconWidth,
-                    height - backgroundPadding.bottom);
-            drawIconOnCanvas(icon, canvas, paint, src, dst);
-        }
-        return tmpBitmap;
-    }
-
-    /**
-     * Returns true if the avatar should be positioned at the right edge of the chip.
-     * Takes into account both the set avatar position (start or end) as well as whether
-     * the layout direction is LTR or RTL.
-     */
-    private boolean shouldPositionAvatarOnRight() {
-        final boolean isRtl = Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1 ?
-                getLayoutDirection() == LAYOUT_DIRECTION_RTL : false;
-        final boolean assignedPosition = mAvatarPosition == AVATAR_POSITION_END;
-        // If in Rtl mode, the position should be flipped.
-        return isRtl ? !assignedPosition : assignedPosition;
-    }
-
-    /**
-     * Returns the avatar icon to use for this recipient entry. Returns null if we don't want to
-     * draw an icon for this recipient.
-     */
-    private Bitmap getAvatarIcon(RecipientEntry contact) {
-        // Don't draw photos for recipients that have been typed in OR generated on the fly.
-        long contactId = contact.getContactId();
-        boolean drawPhotos = isPhoneQuery() ?
-                contactId != RecipientEntry.INVALID_CONTACT
-                : (contactId != RecipientEntry.INVALID_CONTACT
-                        && (contactId != RecipientEntry.GENERATED_CONTACT &&
-                                !TextUtils.isEmpty(contact.getDisplayName())));
-
-        if (drawPhotos) {
-            byte[] photoBytes = contact.getPhotoBytes();
-            // There may not be a photo yet if anything but the first contact address
-            // was selected.
-            if (photoBytes == null && contact.getPhotoThumbnailUri() != null) {
-                // TODO: cache this in the recipient entry?
-                getAdapter().fetchPhoto(contact, contact.getPhotoThumbnailUri());
-                photoBytes = contact.getPhotoBytes();
-            }
-            if (photoBytes != null) {
-                return BitmapFactory.decodeByteArray(photoBytes, 0, photoBytes.length);
-            } else {
-                // TODO: can the scaled down default photo be cached?
-                return mDefaultContactPhoto;
-            }
-        }
-
-        return null;
-    }
-
-    /**
-     * Get the background drawable for a RecipientChip.
-     */
-    // Visible for testing.
-    /* package */Drawable getChipBackground(RecipientEntry contact) {
-        return contact.isValid() ? mChipBackground : mInvalidChipBackground;
-    }
-
-    /**
-     * Given a height, returns a Y offset that will draw the text in the middle of the height.
-     */
-    protected float getTextYOffset(String text, TextPaint paint, int height) {
-        Rect bounds = new Rect();
-        paint.getTextBounds(text, 0, text.length(), bounds);
-        int textHeight = bounds.bottom - bounds.top ;
-        return height - ((height - textHeight) / 2) - (int)paint.descent();
-    }
-
-    /**
-     * Draws the icon onto the canvas given the source rectangle of the bitmap and the destination
-     * rectangle of the canvas.
-     */
-    protected void drawIconOnCanvas(Bitmap icon, Canvas canvas, Paint paint, RectF src, RectF dst) {
-        Matrix matrix = new Matrix();
-        matrix.setRectToRect(src, dst, Matrix.ScaleToFit.FILL);
-        canvas.drawBitmap(icon, matrix, paint);
-    }
-
-    private DrawableRecipientChip constructChipSpan(RecipientEntry contact, boolean pressed,
-            boolean leaveIconSpace) throws NullPointerException {
-        if (mChipBackground == null) {
-            throw new NullPointerException(
-                    "Unable to render any chips as setChipDimensions was not called.");
-        }
-
-        TextPaint paint = getPaint();
-        float defaultSize = paint.getTextSize();
-        int defaultColor = paint.getColor();
-
-        Bitmap tmpBitmap;
-        if (pressed) {
-            tmpBitmap = createSelectedChip(contact, paint);
-
-        } else {
-            tmpBitmap = createUnselectedChip(contact, paint, leaveIconSpace);
-        }
-
-        // Pass the full text, un-ellipsized, to the chip.
-        Drawable result = new BitmapDrawable(getResources(), tmpBitmap);
-        result.setBounds(0, 0, tmpBitmap.getWidth(), tmpBitmap.getHeight());
-        DrawableRecipientChip recipientChip =
-                new VisibleRecipientChip(result, contact, getImageSpanAlignment());
-        // Return text to the original size.
-        paint.setTextSize(defaultSize);
-        paint.setColor(defaultColor);
-        return recipientChip;
-    }
-
-    private int getImageSpanAlignment() {
-        switch (mImageSpanAlignment) {
-            case IMAGE_SPAN_ALIGNMENT_BASELINE:
-                return ImageSpan.ALIGN_BASELINE;
-            case IMAGE_SPAN_ALIGNMENT_BOTTOM:
-                return ImageSpan.ALIGN_BOTTOM;
-            default:
-                return ImageSpan.ALIGN_BOTTOM;
-        }
-    }
-
-    /**
-     * Calculate the bottom of the line the chip will be located on using:
-     * 1) which line the chip appears on
-     * 2) the height of a chip
-     * 3) padding built into the edit text view
-     */
-    private int calculateOffsetFromBottom(int line) {
-        // Line offsets start at zero.
-        int actualLine = getLineCount() - (line + 1);
-        return -((actualLine * ((int) mChipHeight) + getPaddingBottom()) + getPaddingTop())
-                + getDropDownVerticalOffset();
-    }
-
-    /**
-     * Get the max amount of space a chip can take up. The formula takes into
-     * account the width of the EditTextView, any view padding, and padding
-     * that will be added to the chip.
-     */
-    private float calculateAvailableWidth() {
-        return getWidth() - getPaddingLeft() - getPaddingRight() - (mChipPadding * 2);
-    }
-
-
-    private void setChipDimensions(Context context, AttributeSet attrs) {
-        TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.RecipientEditTextView, 0,
-                0);
-        Resources r = getContext().getResources();
-
-        mChipBackground = a.getDrawable(R.styleable.RecipientEditTextView_chipBackground);
-        if (mChipBackground == null) {
-            mChipBackground = r.getDrawable(R.drawable.chip_background);
-        }
-        mChipBackgroundPressed = a
-                .getDrawable(R.styleable.RecipientEditTextView_chipBackgroundPressed);
-        if (mChipBackgroundPressed == null) {
-            mChipBackgroundPressed = r.getDrawable(R.drawable.chip_background_selected);
-        }
-        mChipDelete = a.getDrawable(R.styleable.RecipientEditTextView_chipDelete);
-        if (mChipDelete == null) {
-            mChipDelete = r.getDrawable(R.drawable.chip_delete);
-        }
-        mChipPadding = a.getDimensionPixelSize(R.styleable.RecipientEditTextView_chipPadding, -1);
-        if (mChipPadding == -1) {
-            mChipPadding = (int) r.getDimension(R.dimen.chip_padding);
-        }
-
-        mDefaultContactPhoto = BitmapFactory.decodeResource(r, R.drawable.ic_contact_picture);
-
-        mMoreItem = (TextView) LayoutInflater.from(getContext()).inflate(R.layout.more_item, null);
-
-        mChipHeight = a.getDimensionPixelSize(R.styleable.RecipientEditTextView_chipHeight, -1);
-        if (mChipHeight == -1) {
-            mChipHeight = r.getDimension(R.dimen.chip_height);
-        }
-        mChipFontSize = a.getDimensionPixelSize(R.styleable.RecipientEditTextView_chipFontSize, -1);
-        if (mChipFontSize == -1) {
-            mChipFontSize = r.getDimension(R.dimen.chip_text_size);
-        }
-        mInvalidChipBackground = a
-                .getDrawable(R.styleable.RecipientEditTextView_invalidChipBackground);
-        if (mInvalidChipBackground == null) {
-            mInvalidChipBackground = r.getDrawable(R.drawable.chip_background_invalid);
-        }
-        mAvatarPosition = a.getInt(R.styleable.RecipientEditTextView_avatarPosition, 0);
-        mImageSpanAlignment = a.getInt(R.styleable.RecipientEditTextView_imageSpanAlignment, 0);
-        mDisableDelete = a.getBoolean(R.styleable.RecipientEditTextView_disableDelete, false);
-
-        mLineSpacingExtra =  r.getDimension(R.dimen.line_spacing_extra);
-        mMaxLines = r.getInteger(R.integer.chips_max_lines);
-        TypedValue tv = new TypedValue();
-        if (context.getTheme().resolveAttribute(android.R.attr.actionBarSize, tv, true)) {
-            mActionBarHeight = TypedValue.complexToDimensionPixelSize(tv.data, getResources()
-                    .getDisplayMetrics());
-        }
-
-        a.recycle();
-    }
-
-    // Visible for testing.
-    /* package */ void setMoreItem(TextView moreItem) {
-        mMoreItem = moreItem;
-    }
-
-
-    // Visible for testing.
-    /* package */ void setChipBackground(Drawable chipBackground) {
-        mChipBackground = chipBackground;
-    }
-
-    // Visible for testing.
-    /* package */ void setChipHeight(int height) {
-        mChipHeight = height;
-    }
-
-    public float getChipHeight() {
-        return mChipHeight;
-    }
-
-    /**
-     * Set whether to shrink the recipients field such that at most
-     * one line of recipients chips are shown when the field loses
-     * focus. By default, the number of displayed recipients will be
-     * limited and a "more" chip will be shown when focus is lost.
-     * @param shrink
-     */
-    public void setOnFocusListShrinkRecipients(boolean shrink) {
-        mShouldShrink = shrink;
-    }
-
-    @Override
-    public void onSizeChanged(int width, int height, int oldw, int oldh) {
-        super.onSizeChanged(width, height, oldw, oldh);
-        if (width != 0 && height != 0) {
-            if (mPendingChipsCount > 0) {
-                postHandlePendingChips();
-            } else {
-                checkChipWidths();
-            }
-        }
-        // Try to find the scroll view parent, if it exists.
-        if (mScrollView == null && !mTriedGettingScrollView) {
-            ViewParent parent = getParent();
-            while (parent != null && !(parent instanceof ScrollView)) {
-                parent = parent.getParent();
-            }
-            if (parent != null) {
-                mScrollView = (ScrollView) parent;
-            }
-            mTriedGettingScrollView = true;
-        }
-    }
-
-    private void postHandlePendingChips() {
-        mHandler.removeCallbacks(mHandlePendingChips);
-        mHandler.post(mHandlePendingChips);
-    }
-
-    private void checkChipWidths() {
-        // Check the widths of the associated chips.
-        DrawableRecipientChip[] chips = getSortedRecipients();
-        if (chips != null) {
-            Rect bounds;
-            for (DrawableRecipientChip chip : chips) {
-                bounds = chip.getBounds();
-                if (getWidth() > 0 && bounds.right - bounds.left >
-                        getWidth() - getPaddingLeft() - getPaddingRight()) {
-                    // Need to redraw that chip.
-                    replaceChip(chip, chip.getEntry());
-                }
-            }
-        }
-    }
-
-    // Visible for testing.
-    /*package*/ void handlePendingChips() {
-        if (getViewWidth() <= 0) {
-            // The widget has not been sized yet.
-            // This will be called as a result of onSizeChanged
-            // at a later point.
-            return;
-        }
-        if (mPendingChipsCount <= 0) {
-            return;
-        }
-
-        synchronized (mPendingChips) {
-            Editable editable = getText();
-            // Tokenize!
-            if (mPendingChipsCount <= MAX_CHIPS_PARSED) {
-                for (int i = 0; i < mPendingChips.size(); i++) {
-                    String current = mPendingChips.get(i);
-                    int tokenStart = editable.toString().indexOf(current);
-                    // Always leave a space at the end between tokens.
-                    int tokenEnd = tokenStart + current.length() - 1;
-                    if (tokenStart >= 0) {
-                        // When we have a valid token, include it with the token
-                        // to the left.
-                        if (tokenEnd < editable.length() - 2
-                                && editable.charAt(tokenEnd) == COMMIT_CHAR_COMMA) {
-                            tokenEnd++;
-                        }
-                        createReplacementChip(tokenStart, tokenEnd, editable, i < CHIP_LIMIT
-                                || !mShouldShrink);
-                    }
-                    mPendingChipsCount--;
-                }
-                sanitizeEnd();
-            } else {
-                mNoChips = true;
-            }
-
-            if (mTemporaryRecipients != null && mTemporaryRecipients.size() > 0
-                    && mTemporaryRecipients.size() <= RecipientAlternatesAdapter.MAX_LOOKUPS) {
-                if (hasFocus() || mTemporaryRecipients.size() < CHIP_LIMIT) {
-                    new RecipientReplacementTask().execute();
-                    mTemporaryRecipients = null;
-                } else {
-                    // Create the "more" chip
-                    mIndividualReplacements = new IndividualReplacementTask();
-                    mIndividualReplacements.execute(new ArrayList<DrawableRecipientChip>(
-                            mTemporaryRecipients.subList(0, CHIP_LIMIT)));
-                    if (mTemporaryRecipients.size() > CHIP_LIMIT) {
-                        mTemporaryRecipients = new ArrayList<DrawableRecipientChip>(
-                                mTemporaryRecipients.subList(CHIP_LIMIT,
-                                        mTemporaryRecipients.size()));
-                    } else {
-                        mTemporaryRecipients = null;
-                    }
-                    createMoreChip();
-                }
-            } else {
-                // There are too many recipients to look up, so just fall back
-                // to showing addresses for all of them.
-                mTemporaryRecipients = null;
-                createMoreChip();
-            }
-            mPendingChipsCount = 0;
-            mPendingChips.clear();
-        }
-    }
-
-    // Visible for testing.
-    /*package*/ int getViewWidth() {
-        return getWidth();
-    }
-
-    /**
-     * Remove any characters after the last valid chip.
-     */
-    // Visible for testing.
-    /*package*/ void sanitizeEnd() {
-        // Don't sanitize while we are waiting for pending chips to complete.
-        if (mPendingChipsCount > 0) {
-            return;
-        }
-        // Find the last chip; eliminate any commit characters after it.
-        DrawableRecipientChip[] chips = getSortedRecipients();
-        Spannable spannable = getSpannable();
-        if (chips != null && chips.length > 0) {
-            int end;
-            mMoreChip = getMoreChip();
-            if (mMoreChip != null) {
-                end = spannable.getSpanEnd(mMoreChip);
-            } else {
-                end = getSpannable().getSpanEnd(getLastChip());
-            }
-            Editable editable = getText();
-            int length = editable.length();
-            if (length > end) {
-                // See what characters occur after that and eliminate them.
-                if (Log.isLoggable(TAG, Log.DEBUG)) {
-                    Log.d(TAG, "There were extra characters after the last tokenizable entry."
-                            + editable);
-                }
-                editable.delete(end + 1, length);
-            }
-        }
-    }
-
-    /**
-     * Create a chip that represents just the email address of a recipient. At some later
-     * point, this chip will be attached to a real contact entry, if one exists.
-     */
-    // VisibleForTesting
-    void createReplacementChip(int tokenStart, int tokenEnd, Editable editable,
-            boolean visible) {
-        if (alreadyHasChip(tokenStart, tokenEnd)) {
-            // There is already a chip present at this location.
-            // Don't recreate it.
-            return;
-        }
-        String token = editable.toString().substring(tokenStart, tokenEnd);
-        final String trimmedToken = token.trim();
-        int commitCharIndex = trimmedToken.lastIndexOf(COMMIT_CHAR_COMMA);
-        if (commitCharIndex != -1 && commitCharIndex == trimmedToken.length() - 1) {
-            token = trimmedToken.substring(0, trimmedToken.length() - 1);
-        }
-        RecipientEntry entry = createTokenizedEntry(token);
-        if (entry != null) {
-            DrawableRecipientChip chip = null;
-            try {
-                if (!mNoChips) {
-                    /*
-                     * leave space for the contact icon if this is not just an
-                     * email address
-                     */
-                    boolean leaveSpace = TextUtils.isEmpty(entry.getDisplayName())
-                            || TextUtils.equals(entry.getDisplayName(),
-                                    entry.getDestination());
-                    chip = visible ?
-                            constructChipSpan(entry, false, leaveSpace)
-                            : new InvisibleRecipientChip(entry);
-                }
-            } catch (NullPointerException e) {
-                Log.e(TAG, e.getMessage(), e);
-            }
-            editable.setSpan(chip, tokenStart, tokenEnd, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
-            // Add this chip to the list of entries "to replace"
-            if (chip != null) {
-                if (mTemporaryRecipients == null) {
-                    mTemporaryRecipients = new ArrayList<DrawableRecipientChip>();
-                }
-                chip.setOriginalText(token);
-                mTemporaryRecipients.add(chip);
-            }
-        }
-    }
-
-    private static boolean isPhoneNumber(String number) {
-        // TODO: replace this function with libphonenumber's isPossibleNumber (see
-        // PhoneNumberUtil). One complication is that it requires the sender's region which
-        // comes from the CurrentCountryIso. For now, let's just do this simple match.
-        if (TextUtils.isEmpty(number)) {
-            return false;
-        }
-
-        Matcher match = PHONE_PATTERN.matcher(number);
-        return match.matches();
-    }
-
-    // VisibleForTesting
-    RecipientEntry createTokenizedEntry(final String token) {
-        if (TextUtils.isEmpty(token)) {
-            return null;
-        }
-        if (isPhoneQuery() && isPhoneNumber(token)) {
-            return RecipientEntry.constructFakePhoneEntry(token, true);
-        }
-        Rfc822Token[] tokens = Rfc822Tokenizer.tokenize(token);
-        String display = null;
-        boolean isValid = isValid(token);
-        if (isValid && tokens != null && tokens.length > 0) {
-            // If we can get a name from tokenizing, then generate an entry from
-            // this.
-            display = tokens[0].getName();
-            if (!TextUtils.isEmpty(display)) {
-                return RecipientEntry.constructGeneratedEntry(display, tokens[0].getAddress(),
-                        isValid);
-            } else {
-                display = tokens[0].getAddress();
-                if (!TextUtils.isEmpty(display)) {
-                    return RecipientEntry.constructFakeEntry(display, isValid);
-                }
-            }
-        }
-        // Unable to validate the token or to create a valid token from it.
-        // Just create a chip the user can edit.
-        String validatedToken = null;
-        if (mValidator != null && !isValid) {
-            // Try fixing up the entry using the validator.
-            validatedToken = mValidator.fixText(token).toString();
-            if (!TextUtils.isEmpty(validatedToken)) {
-                if (validatedToken.contains(token)) {
-                    // protect against the case of a validator with a null
-                    // domain,
-                    // which doesn't add a domain to the token
-                    Rfc822Token[] tokenized = Rfc822Tokenizer.tokenize(validatedToken);
-                    if (tokenized.length > 0) {
-                        validatedToken = tokenized[0].getAddress();
-                        isValid = true;
-                    }
-                } else {
-                    // We ran into a case where the token was invalid and
-                    // removed
-                    // by the validator. In this case, just use the original
-                    // token
-                    // and let the user sort out the error chip.
-                    validatedToken = null;
-                    isValid = false;
-                }
-            }
-        }
-        // Otherwise, fallback to just creating an editable email address chip.
-        return RecipientEntry.constructFakeEntry(
-                !TextUtils.isEmpty(validatedToken) ? validatedToken : token, isValid);
-    }
-
-    private boolean isValid(String text) {
-        return mValidator == null ? true : mValidator.isValid(text);
-    }
-
-    private static String tokenizeAddress(String destination) {
-        Rfc822Token[] tokens = Rfc822Tokenizer.tokenize(destination);
-        if (tokens != null && tokens.length > 0) {
-            return tokens[0].getAddress();
-        }
-        return destination;
-    }
-
-    @Override
-    public void setTokenizer(Tokenizer tokenizer) {
-        mTokenizer = tokenizer;
-        super.setTokenizer(mTokenizer);
-    }
-
-    @Override
-    public void setValidator(Validator validator) {
-        mValidator = validator;
-        super.setValidator(validator);
-    }
-
-    /**
-     * We cannot use the default mechanism for replaceText. Instead,
-     * we override onItemClickListener so we can get all the associated
-     * contact information including display text, address, and id.
-     */
-    @Override
-    protected void replaceText(CharSequence text) {
-        return;
-    }
-
-    /**
-     * Dismiss any selected chips when the back key is pressed.
-     */
-    @Override
-    public boolean onKeyPreIme(int keyCode, KeyEvent event) {
-        if (keyCode == KeyEvent.KEYCODE_BACK && mSelectedChip != null) {
-            clearSelectedChip();
-            return true;
-        }
-        return super.onKeyPreIme(keyCode, event);
-    }
-
-    /**
-     * Monitor key presses in this view to see if the user types
-     * any commit keys, which consist of ENTER, TAB, or DPAD_CENTER.
-     * If the user has entered text that has contact matches and types
-     * a commit key, create a chip from the topmost matching contact.
-     * If the user has entered text that has no contact matches and types
-     * a commit key, then create a chip from the text they have entered.
-     */
-    @Override
-    public boolean onKeyUp(int keyCode, KeyEvent event) {
-        switch (keyCode) {
-            case KeyEvent.KEYCODE_TAB:
-                if (event.hasNoModifiers()) {
-                    if (mSelectedChip != null) {
-                        clearSelectedChip();
-                    } else {
-                        commitDefault();
-                    }
-                }
-                break;
-        }
-        return super.onKeyUp(keyCode, event);
-    }
-
-    private boolean focusNext() {
-        View next = focusSearch(View.FOCUS_DOWN);
-        if (next != null) {
-            next.requestFocus();
-            return true;
-        }
-        return false;
-    }
-
-    /**
-     * Create a chip from the default selection. If the popup is showing, the
-     * default is the first item in the popup suggestions list. Otherwise, it is
-     * whatever the user had typed in. End represents where the the tokenizer
-     * should search for a token to turn into a chip.
-     * @return If a chip was created from a real contact.
-     */
-    private boolean commitDefault() {
-        // If there is no tokenizer, don't try to commit.
-        if (mTokenizer == null) {
-            return false;
-        }
-        Editable editable = getText();
-        int end = getSelectionEnd();
-        int start = mTokenizer.findTokenStart(editable, end);
-
-        if (shouldCreateChip(start, end)) {
-            int whatEnd = mTokenizer.findTokenEnd(getText(), start);
-            // In the middle of chip; treat this as an edit
-            // and commit the whole token.
-            whatEnd = movePastTerminators(whatEnd);
-            if (whatEnd != getSelectionEnd()) {
-                handleEdit(start, whatEnd);
-                return true;
-            }
-            return commitChip(start, end , editable);
-        }
-        return false;
-    }
-
-    private void commitByCharacter() {
-        // We can't possibly commit by character if we can't tokenize.
-        if (mTokenizer == null) {
-            return;
-        }
-        Editable editable = getText();
-        int end = getSelectionEnd();
-        int start = mTokenizer.findTokenStart(editable, end);
-        if (shouldCreateChip(start, end)) {
-            commitChip(start, end, editable);
-        }
-        setSelection(getText().length());
-    }
-
-    private boolean commitChip(int start, int end, Editable editable) {
-        ListAdapter adapter = getAdapter();
-        if (adapter != null && adapter.getCount() > 0 && enoughToFilter()
-                && end == getSelectionEnd() && !isPhoneQuery()) {
-            // choose the first entry.
-            submitItemAtPosition(0);
-            dismissDropDown();
-            return true;
-        } else {
-            int tokenEnd = mTokenizer.findTokenEnd(editable, start);
-            if (editable.length() > tokenEnd + 1) {
-                char charAt = editable.charAt(tokenEnd + 1);
-                if (charAt == COMMIT_CHAR_COMMA || charAt == COMMIT_CHAR_SEMICOLON) {
-                    tokenEnd++;
-                }
-            }
-            String text = editable.toString().substring(start, tokenEnd).trim();
-            clearComposingText();
-            if (text != null && text.length() > 0 && !text.equals(" ")) {
-                RecipientEntry entry = createTokenizedEntry(text);
-                if (entry != null) {
-                    QwertyKeyListener.markAsReplaced(editable, start, end, "");
-                    CharSequence chipText = createChip(entry, false);
-                    if (chipText != null && start > -1 && end > -1) {
-                        editable.replace(start, end, chipText);
-                    }
-                }
-                // Only dismiss the dropdown if it is related to the text we
-                // just committed.
-                // For paste, it may not be as there are possibly multiple
-                // tokens being added.
-                if (end == getSelectionEnd()) {
-                    dismissDropDown();
-                }
-                sanitizeBetween();
-                return true;
-            }
-        }
-        return false;
-    }
-
-    // Visible for testing.
-    /* package */ void sanitizeBetween() {
-        // Don't sanitize while we are waiting for content to chipify.
-        if (mPendingChipsCount > 0) {
-            return;
-        }
-        // Find the last chip.
-        DrawableRecipientChip[] recips = getSortedRecipients();
-        if (recips != null && recips.length > 0) {
-            DrawableRecipientChip last = recips[recips.length - 1];
-            DrawableRecipientChip beforeLast = null;
-            if (recips.length > 1) {
-                beforeLast = recips[recips.length - 2];
-            }
-            int startLooking = 0;
-            int end = getSpannable().getSpanStart(last);
-            if (beforeLast != null) {
-                startLooking = getSpannable().getSpanEnd(beforeLast);
-                Editable text = getText();
-                if (startLooking == -1 || startLooking > text.length() - 1) {
-                    // There is nothing after this chip.
-                    return;
-                }
-                if (text.charAt(startLooking) == ' ') {
-                    startLooking++;
-                }
-            }
-            if (startLooking >= 0 && end >= 0 && startLooking < end) {
-                getText().delete(startLooking, end);
-            }
-        }
-    }
-
-    private boolean shouldCreateChip(int start, int end) {
-        return !mNoChips && hasFocus() && enoughToFilter() && !alreadyHasChip(start, end);
-    }
-
-    private boolean alreadyHasChip(int start, int end) {
-        if (mNoChips) {
-            return true;
-        }
-        DrawableRecipientChip[] chips =
-                getSpannable().getSpans(start, end, DrawableRecipientChip.class);
-        if ((chips == null || chips.length == 0)) {
-            return false;
-        }
-        return true;
-    }
-
-    private void handleEdit(int start, int end) {
-        if (start == -1 || end == -1) {
-            // This chip no longer exists in the field.
-            dismissDropDown();
-            return;
-        }
-        // This is in the middle of a chip, so select out the whole chip
-        // and commit it.
-        Editable editable = getText();
-        setSelection(end);
-        String text = getText().toString().substring(start, end);
-        if (!TextUtils.isEmpty(text)) {
-            RecipientEntry entry = RecipientEntry.constructFakeEntry(text, isValid(text));
-            QwertyKeyListener.markAsReplaced(editable, start, end, "");
-            CharSequence chipText = createChip(entry, false);
-            int selEnd = getSelectionEnd();
-            if (chipText != null && start > -1 && selEnd > -1) {
-                editable.replace(start, selEnd, chipText);
-            }
-        }
-        dismissDropDown();
-    }
-
-    /**
-     * If there is a selected chip, delegate the key events
-     * to the selected chip.
-     */
-    @Override
-    public boolean onKeyDown(int keyCode, KeyEvent event) {
-        if (mSelectedChip != null && keyCode == KeyEvent.KEYCODE_DEL) {
-            if (mAlternatesPopup != null && mAlternatesPopup.isShowing()) {
-                mAlternatesPopup.dismiss();
-            }
-            removeChip(mSelectedChip);
-        }
-
-        switch (keyCode) {
-            case KeyEvent.KEYCODE_ENTER:
-            case KeyEvent.KEYCODE_DPAD_CENTER:
-                if (event.hasNoModifiers()) {
-                    if (commitDefault()) {
-                        return true;
-                    }
-                    if (mSelectedChip != null) {
-                        clearSelectedChip();
-                        return true;
-                    } else if (focusNext()) {
-                        return true;
-                    }
-                }
-                break;
-        }
-
-        return super.onKeyDown(keyCode, event);
-    }
-
-    // Visible for testing.
-    /* package */ Spannable getSpannable() {
-        return getText();
-    }
-
-    private int getChipStart(DrawableRecipientChip chip) {
-        return getSpannable().getSpanStart(chip);
-    }
-
-    private int getChipEnd(DrawableRecipientChip chip) {
-        return getSpannable().getSpanEnd(chip);
-    }
-
-    /**
-     * Instead of filtering on the entire contents of the edit box,
-     * this subclass method filters on the range from
-     * {@link Tokenizer#findTokenStart} to {@link #getSelectionEnd}
-     * if the length of that range meets or exceeds {@link #getThreshold}
-     * and makes sure that the range is not already a Chip.
-     */
-    @Override
-    protected void performFiltering(CharSequence text, int keyCode) {
-        boolean isCompletedToken = isCompletedToken(text);
-        if (enoughToFilter() && !isCompletedToken) {
-            int end = getSelectionEnd();
-            int start = mTokenizer.findTokenStart(text, end);
-            // If this is a RecipientChip, don't filter
-            // on its contents.
-            Spannable span = getSpannable();
-            DrawableRecipientChip[] chips = span.getSpans(start, end, DrawableRecipientChip.class);
-            if (chips != null && chips.length > 0) {
-                dismissDropDown();
-                return;
-            }
-        } else if (isCompletedToken) {
-            dismissDropDown();
-            return;
-        }
-        super.performFiltering(text, keyCode);
-    }
-
-    // Visible for testing.
-    /*package*/ boolean isCompletedToken(CharSequence text) {
-        if (TextUtils.isEmpty(text)) {
-            return false;
-        }
-        // Check to see if this is a completed token before filtering.
-        int end = text.length();
-        int start = mTokenizer.findTokenStart(text, end);
-        String token = text.toString().substring(start, end).trim();
-        if (!TextUtils.isEmpty(token)) {
-            char atEnd = token.charAt(token.length() - 1);
-            return atEnd == COMMIT_CHAR_COMMA || atEnd == COMMIT_CHAR_SEMICOLON;
-        }
-        return false;
-    }
-
-    private void clearSelectedChip() {
-        if (mSelectedChip != null) {
-            unselectChip(mSelectedChip);
-            mSelectedChip = null;
-        }
-        setCursorVisible(true);
-    }
-
-    /**
-     * Monitor touch events in the RecipientEditTextView.
-     * If the view does not have focus, any tap on the view
-     * will just focus the view. If the view has focus, determine
-     * if the touch target is a recipient chip. If it is and the chip
-     * is not selected, select it and clear any other selected chips.
-     * If it isn't, then select that chip.
-     */
-    @Override
-    public boolean onTouchEvent(MotionEvent event) {
-        if (!isFocused()) {
-            // Ignore any chip taps until this view is focused.
-            return super.onTouchEvent(event);
-        }
-        boolean handled = super.onTouchEvent(event);
-        int action = event.getAction();
-        boolean chipWasSelected = false;
-        if (mSelectedChip == null) {
-            mGestureDetector.onTouchEvent(event);
-        }
-        if (mCopyAddress == null && action == MotionEvent.ACTION_UP) {
-            float x = event.getX();
-            float y = event.getY();
-            int offset = putOffsetInRange(x, y);
-            DrawableRecipientChip currentChip = findChip(offset);
-            if (currentChip != null) {
-                if (action == MotionEvent.ACTION_UP) {
-                    if (mSelectedChip != null && mSelectedChip != currentChip) {
-                        clearSelectedChip();
-                        mSelectedChip = selectChip(currentChip);
-                    } else if (mSelectedChip == null) {
-                        setSelection(getText().length());
-                        commitDefault();
-                        mSelectedChip = selectChip(currentChip);
-                    } else {
-                        onClick(mSelectedChip, offset, x, y);
-                    }
-                }
-                chipWasSelected = true;
-                handled = true;
-            } else if (mSelectedChip != null && shouldShowEditableText(mSelectedChip)) {
-                chipWasSelected = true;
-            }
-        }
-        if (action == MotionEvent.ACTION_UP && !chipWasSelected) {
-            clearSelectedChip();
-        }
-        return handled;
-    }
-
-    private void scrollLineIntoView(int line) {
-        if (mScrollView != null) {
-            mScrollView.smoothScrollBy(0, calculateOffsetFromBottom(line));
-        }
-    }
-
-    private void showAlternates(final DrawableRecipientChip currentChip,
-            final ListPopupWindow alternatesPopup, final int width) {
-        new AsyncTask<Void, Void, ListAdapter>() {
-            @Override
-            protected ListAdapter doInBackground(final Void... params) {
-                return createAlternatesAdapter(currentChip);
-            }
-
-            @Override
-            protected void onPostExecute(final ListAdapter result) {
-                if (!mAttachedToWindow) {
-                    return;
-                }
-                int line = getLayout().getLineForOffset(getChipStart(currentChip));
-                int bottom;
-                if (line == getLineCount() -1) {
-                    bottom = 0;
-                } else {
-                    bottom = -(int) ((mChipHeight + (2 * mLineSpacingExtra)) * (Math
-                            .abs(getLineCount() - 1 - line)));
-                }
-                // Align the alternates popup with the left side of the View,
-                // regardless of the position of the chip tapped.
-                alternatesPopup.setWidth(width);
-                alternatesPopup.setAnchorView(RecipientEditTextView.this);
-                alternatesPopup.setVerticalOffset(bottom);
-                alternatesPopup.setAdapter(result);
-                alternatesPopup.setOnItemClickListener(mAlternatesListener);
-                // Clear the checked item.
-                mCheckedItem = -1;
-                alternatesPopup.show();
-                ListView listView = alternatesPopup.getListView();
-                listView.setChoiceMode(ListView.CHOICE_MODE_SINGLE);
-                // Checked item would be -1 if the adapter has not
-                // loaded the view that should be checked yet. The
-                // variable will be set correctly when onCheckedItemChanged
-                // is called in a separate thread.
-                if (mCheckedItem != -1) {
-                    listView.setItemChecked(mCheckedItem, true);
-                    mCheckedItem = -1;
-                }
-            }
-        }.execute((Void[]) null);
-    }
-
-    private ListAdapter createAlternatesAdapter(DrawableRecipientChip chip) {
-        return new RecipientAlternatesAdapter(getContext(), chip.getContactId(),
-                chip.getDirectoryId(), chip.getLookupKey(), chip.getDataId(),
-                getAdapter().getQueryType(), this, mDropdownChipLayouter);
-    }
-
-    private ListAdapter createSingleAddressAdapter(DrawableRecipientChip currentChip) {
-        return new SingleRecipientArrayAdapter(getContext(), currentChip.getEntry(),
-                mDropdownChipLayouter);
-    }
-
-    @Override
-    public void onCheckedItemChanged(int position) {
-        ListView listView = mAlternatesPopup.getListView();
-        if (listView != null && listView.getCheckedItemCount() == 0) {
-            listView.setItemChecked(position, true);
-        }
-        mCheckedItem = position;
-    }
-
-    private int putOffsetInRange(final float x, final float y) {
-        final int offset;
-
-        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
-            offset = getOffsetForPosition(x, y);
-        } else {
-            offset = supportGetOffsetForPosition(x, y);
-        }
-
-        return putOffsetInRange(offset);
-    }
-
-    // TODO: This algorithm will need a lot of tweaking after more people have used
-    // the chips ui. This attempts to be "forgiving" to fat finger touches by favoring
-    // what comes before the finger.
-    private int putOffsetInRange(int o) {
-        int offset = o;
-        Editable text = getText();
-        int length = text.length();
-        // Remove whitespace from end to find "real end"
-        int realLength = length;
-        for (int i = length - 1; i >= 0; i--) {
-            if (text.charAt(i) == ' ') {
-                realLength--;
-            } else {
-                break;
-            }
-        }
-
-        // If the offset is beyond or at the end of the text,
-        // leave it alone.
-        if (offset >= realLength) {
-            return offset;
-        }
-        Editable editable = getText();
-        while (offset >= 0 && findText(editable, offset) == -1 && findChip(offset) == null) {
-            // Keep walking backward!
-            offset--;
-        }
-        return offset;
-    }
-
-    private static int findText(Editable text, int offset) {
-        if (text.charAt(offset) != ' ') {
-            return offset;
-        }
-        return -1;
-    }
-
-    private DrawableRecipientChip findChip(int offset) {
-        DrawableRecipientChip[] chips =
-                getSpannable().getSpans(0, getText().length(), DrawableRecipientChip.class);
-        // Find the chip that contains this offset.
-        for (int i = 0; i < chips.length; i++) {
-            DrawableRecipientChip chip = chips[i];
-            int start = getChipStart(chip);
-            int end = getChipEnd(chip);
-            if (offset >= start && offset <= end) {
-                return chip;
-            }
-        }
-        return null;
-    }
-
-    // Visible for testing.
-    // Use this method to generate text to add to the list of addresses.
-    /* package */String createAddressText(RecipientEntry entry) {
-        String display = entry.getDisplayName();
-        String address = entry.getDestination();
-        if (TextUtils.isEmpty(display) || TextUtils.equals(display, address)) {
-            display = null;
-        }
-        String trimmedDisplayText;
-        if (isPhoneQuery() && isPhoneNumber(address)) {
-            trimmedDisplayText = address.trim();
-        } else {
-            if (address != null) {
-                // Tokenize out the address in case the address already
-                // contained the username as well.
-                Rfc822Token[] tokenized = Rfc822Tokenizer.tokenize(address);
-                if (tokenized != null && tokenized.length > 0) {
-                    address = tokenized[0].getAddress();
-                }
-            }
-            Rfc822Token token = new Rfc822Token(display, address, null);
-            trimmedDisplayText = token.toString().trim();
-        }
-        int index = trimmedDisplayText.indexOf(",");
-        return mTokenizer != null && !TextUtils.isEmpty(trimmedDisplayText)
-                && index < trimmedDisplayText.length() - 1 ? (String) mTokenizer
-                .terminateToken(trimmedDisplayText) : trimmedDisplayText;
-    }
-
-    // Visible for testing.
-    // Use this method to generate text to display in a chip.
-    /*package*/ String createChipDisplayText(RecipientEntry entry) {
-        String display = entry.getDisplayName();
-        String address = entry.getDestination();
-        if (TextUtils.isEmpty(display) || TextUtils.equals(display, address)) {
-            display = null;
-        }
-        if (!TextUtils.isEmpty(display)) {
-            return display;
-        } else if (!TextUtils.isEmpty(address)){
-            return address;
-        } else {
-            return new Rfc822Token(display, address, null).toString();
-        }
-    }
-
-    private CharSequence createChip(RecipientEntry entry, boolean pressed) {
-        String displayText = createAddressText(entry);
-        if (TextUtils.isEmpty(displayText)) {
-            return null;
-        }
-        SpannableString chipText = null;
-        // Always leave a blank space at the end of a chip.
-        int textLength = displayText.length() - 1;
-        chipText = new SpannableString(displayText);
-        if (!mNoChips) {
-            try {
-                DrawableRecipientChip chip = constructChipSpan(entry, pressed,
-                        false /* leave space for contact icon */);
-                chipText.setSpan(chip, 0, textLength,
-                        Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
-                chip.setOriginalText(chipText.toString());
-            } catch (NullPointerException e) {
-                Log.e(TAG, e.getMessage(), e);
-                return null;
-            }
-        }
-        return chipText;
-    }
-
-    /**
-     * When an item in the suggestions list has been clicked, create a chip from the
-     * contact information of the selected item.
-     */
-    @Override
-    public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
-        if (position < 0) {
-            return;
-        }
-        submitItemAtPosition(position);
-    }
-
-    private void submitItemAtPosition(int position) {
-        RecipientEntry entry = createValidatedEntry(getAdapter().getItem(position));
-        if (entry == null) {
-            return;
-        }
-        clearComposingText();
-
-        int end = getSelectionEnd();
-        int start = mTokenizer.findTokenStart(getText(), end);
-
-        Editable editable = getText();
-        QwertyKeyListener.markAsReplaced(editable, start, end, "");
-        CharSequence chip = createChip(entry, false);
-        if (chip != null && start >= 0 && end >= 0) {
-            editable.replace(start, end, chip);
-        }
-        sanitizeBetween();
-    }
-
-    private RecipientEntry createValidatedEntry(RecipientEntry item) {
-        if (item == null) {
-            return null;
-        }
-        final RecipientEntry entry;
-        // If the display name and the address are the same, or if this is a
-        // valid contact, but the destination is invalid, then make this a fake
-        // recipient that is editable.
-        String destination = item.getDestination();
-        if (!isPhoneQuery() && item.getContactId() == RecipientEntry.GENERATED_CONTACT) {
-            entry = RecipientEntry.constructGeneratedEntry(item.getDisplayName(),
-                    destination, item.isValid());
-        } else if (RecipientEntry.isCreatedRecipient(item.getContactId())
-                && (TextUtils.isEmpty(item.getDisplayName())
-                        || TextUtils.equals(item.getDisplayName(), destination)
-                        || (mValidator != null && !mValidator.isValid(destination)))) {
-            entry = RecipientEntry.constructFakeEntry(destination, item.isValid());
-        } else {
-            entry = item;
-        }
-        return entry;
-    }
-
-    /** Returns a collection of contact Id for each chip inside this View. */
-    /* package */ Collection<Long> getContactIds() {
-        final Set<Long> result = new HashSet<Long>();
-        DrawableRecipientChip[] chips = getSortedRecipients();
-        if (chips != null) {
-            for (DrawableRecipientChip chip : chips) {
-                result.add(chip.getContactId());
-            }
-        }
-        return result;
-    }
-
-
-    /** Returns a collection of data Id for each chip inside this View. May be null. */
-    /* package */ Collection<Long> getDataIds() {
-        final Set<Long> result = new HashSet<Long>();
-        DrawableRecipientChip [] chips = getSortedRecipients();
-        if (chips != null) {
-            for (DrawableRecipientChip chip : chips) {
-                result.add(chip.getDataId());
-            }
-        }
-        return result;
-    }
-
-    // Visible for testing.
-    /* package */DrawableRecipientChip[] getSortedRecipients() {
-        DrawableRecipientChip[] recips = getSpannable()
-                .getSpans(0, getText().length(), DrawableRecipientChip.class);
-        ArrayList<DrawableRecipientChip> recipientsList = new ArrayList<DrawableRecipientChip>(
-                Arrays.asList(recips));
-        final Spannable spannable = getSpannable();
-        Collections.sort(recipientsList, new Comparator<DrawableRecipientChip>() {
-
-            @Override
-            public int compare(DrawableRecipientChip first, DrawableRecipientChip second) {
-                int firstStart = spannable.getSpanStart(first);
-                int secondStart = spannable.getSpanStart(second);
-                if (firstStart < secondStart) {
-                    return -1;
-                } else if (firstStart > secondStart) {
-                    return 1;
-                } else {
-                    return 0;
-                }
-            }
-        });
-        return recipientsList.toArray(new DrawableRecipientChip[recipientsList.size()]);
-    }
-
-    @Override
-    public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
-        return false;
-    }
-
-    @Override
-    public void onDestroyActionMode(ActionMode mode) {
-    }
-
-    @Override
-    public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
-        return false;
-    }
-
-    /**
-     * No chips are selectable.
-     */
-    @Override
-    public boolean onCreateActionMode(ActionMode mode, Menu menu) {
-        return false;
-    }
-
-    // Visible for testing.
-    /* package */ImageSpan getMoreChip() {
-        MoreImageSpan[] moreSpans = getSpannable().getSpans(0, getText().length(),
-                MoreImageSpan.class);
-        return moreSpans != null && moreSpans.length > 0 ? moreSpans[0] : null;
-    }
-
-    private MoreImageSpan createMoreSpan(int count) {
-        String moreText = String.format(mMoreItem.getText().toString(), count);
-        TextPaint morePaint = new TextPaint(getPaint());
-        morePaint.setTextSize(mMoreItem.getTextSize());
-        morePaint.setColor(mMoreItem.getCurrentTextColor());
-        int width = (int)morePaint.measureText(moreText) + mMoreItem.getPaddingLeft()
-                + mMoreItem.getPaddingRight();
-        int height = getLineHeight();
-        Bitmap drawable = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
-        Canvas canvas = new Canvas(drawable);
-        int adjustedHeight = height;
-        Layout layout = getLayout();
-        if (layout != null) {
-            adjustedHeight -= layout.getLineDescent(0);
-        }
-        canvas.drawText(moreText, 0, moreText.length(), 0, adjustedHeight, morePaint);
-
-        Drawable result = new BitmapDrawable(getResources(), drawable);
-        result.setBounds(0, 0, width, height);
-        return new MoreImageSpan(result);
-    }
-
-    // Visible for testing.
-    /*package*/ void createMoreChipPlainText() {
-        // Take the first <= CHIP_LIMIT addresses and get to the end of the second one.
-        Editable text = getText();
-        int start = 0;
-        int end = start;
-        for (int i = 0; i < CHIP_LIMIT; i++) {
-            end = movePastTerminators(mTokenizer.findTokenEnd(text, start));
-            start = end; // move to the next token and get its end.
-        }
-        // Now, count total addresses.
-        start = 0;
-        int tokenCount = countTokens(text);
-        MoreImageSpan moreSpan = createMoreSpan(tokenCount - CHIP_LIMIT);
-        SpannableString chipText = new SpannableString(text.subSequence(end, text.length()));
-        chipText.setSpan(moreSpan, 0, chipText.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
-        text.replace(end, text.length(), chipText);
-        mMoreChip = moreSpan;
-    }
-
-    // Visible for testing.
-    /* package */int countTokens(Editable text) {
-        int tokenCount = 0;
-        int start = 0;
-        while (start < text.length()) {
-            start = movePastTerminators(mTokenizer.findTokenEnd(text, start));
-            tokenCount++;
-            if (start >= text.length()) {
-                break;
-            }
-        }
-        return tokenCount;
-    }
-
-    /**
-     * Create the more chip. The more chip is text that replaces any chips that
-     * do not fit in the pre-defined available space when the
-     * RecipientEditTextView loses focus.
-     */
-    // Visible for testing.
-    /* package */ void createMoreChip() {
-        if (mNoChips) {
-            createMoreChipPlainText();
-            return;
-        }
-
-        if (!mShouldShrink) {
-            return;
-        }
-        ImageSpan[] tempMore = getSpannable().getSpans(0, getText().length(), MoreImageSpan.class);
-        if (tempMore.length > 0) {
-            getSpannable().removeSpan(tempMore[0]);
-        }
-        DrawableRecipientChip[] recipients = getSortedRecipients();
-
-        if (recipients == null || recipients.length <= CHIP_LIMIT) {
-            mMoreChip = null;
-            return;
-        }
-        Spannable spannable = getSpannable();
-        int numRecipients = recipients.length;
-        int overage = numRecipients - CHIP_LIMIT;
-        MoreImageSpan moreSpan = createMoreSpan(overage);
-        mRemovedSpans = new ArrayList<DrawableRecipientChip>();
-        int totalReplaceStart = 0;
-        int totalReplaceEnd = 0;
-        Editable text = getText();
-        for (int i = numRecipients - overage; i < recipients.length; i++) {
-            mRemovedSpans.add(recipients[i]);
-            if (i == numRecipients - overage) {
-                totalReplaceStart = spannable.getSpanStart(recipients[i]);
-            }
-            if (i == recipients.length - 1) {
-                totalReplaceEnd = spannable.getSpanEnd(recipients[i]);
-            }
-            if (mTemporaryRecipients == null || !mTemporaryRecipients.contains(recipients[i])) {
-                int spanStart = spannable.getSpanStart(recipients[i]);
-                int spanEnd = spannable.getSpanEnd(recipients[i]);
-                recipients[i].setOriginalText(text.toString().substring(spanStart, spanEnd));
-            }
-            spannable.removeSpan(recipients[i]);
-        }
-        if (totalReplaceEnd < text.length()) {
-            totalReplaceEnd = text.length();
-        }
-        int end = Math.max(totalReplaceStart, totalReplaceEnd);
-        int start = Math.min(totalReplaceStart, totalReplaceEnd);
-        SpannableString chipText = new SpannableString(text.subSequence(start, end));
-        chipText.setSpan(moreSpan, 0, chipText.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
-        text.replace(start, end, chipText);
-        mMoreChip = moreSpan;
-        // If adding the +more chip goes over the limit, resize accordingly.
-        if (!isPhoneQuery() && getLineCount() > mMaxLines) {
-            setMaxLines(getLineCount());
-        }
-    }
-
-    /**
-     * Replace the more chip, if it exists, with all of the recipient chips it had
-     * replaced when the RecipientEditTextView gains focus.
-     */
-    // Visible for testing.
-    /*package*/ void removeMoreChip() {
-        if (mMoreChip != null) {
-            Spannable span = getSpannable();
-            span.removeSpan(mMoreChip);
-            mMoreChip = null;
-            // Re-add the spans that were removed.
-            if (mRemovedSpans != null && mRemovedSpans.size() > 0) {
-                // Recreate each removed span.
-                DrawableRecipientChip[] recipients = getSortedRecipients();
-                // Start the search for tokens after the last currently visible
-                // chip.
-                if (recipients == null || recipients.length == 0) {
-                    return;
-                }
-                int end = span.getSpanEnd(recipients[recipients.length - 1]);
-                Editable editable = getText();
-                for (DrawableRecipientChip chip : mRemovedSpans) {
-                    int chipStart;
-                    int chipEnd;
-                    String token;
-                    // Need to find the location of the chip, again.
-                    token = (String) chip.getOriginalText();
-                    // As we find the matching recipient for the remove spans,
-                    // reduce the size of the string we need to search.
-                    // That way, if there are duplicates, we always find the correct
-                    // recipient.
-                    chipStart = editable.toString().indexOf(token, end);
-                    end = chipEnd = Math.min(editable.length(), chipStart + token.length());
-                    // Only set the span if we found a matching token.
-                    if (chipStart != -1) {
-                        editable.setSpan(chip, chipStart, chipEnd,
-                                Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
-                    }
-                }
-                mRemovedSpans.clear();
-            }
-        }
-    }
-
-    /**
-     * Show specified chip as selected. If the RecipientChip is just an email address,
-     * selecting the chip will take the contents of the chip and place it at
-     * the end of the RecipientEditTextView for inline editing. If the
-     * RecipientChip is a complete contact, then selecting the chip
-     * will change the background color of the chip, show the delete icon,
-     * and a popup window with the address in use highlighted and any other
-     * alternate addresses for the contact.
-     * @param currentChip Chip to select.
-     * @return A RecipientChip in the selected state or null if the chip
-     * just contained an email address.
-     */
-    private DrawableRecipientChip selectChip(DrawableRecipientChip currentChip) {
-        if (shouldShowEditableText(currentChip)) {
-            CharSequence text = currentChip.getValue();
-            Editable editable = getText();
-            Spannable spannable = getSpannable();
-            int spanStart = spannable.getSpanStart(currentChip);
-            int spanEnd = spannable.getSpanEnd(currentChip);
-            spannable.removeSpan(currentChip);
-            editable.delete(spanStart, spanEnd);
-            setCursorVisible(true);
-            setSelection(editable.length());
-            editable.append(text);
-            return constructChipSpan(
-                    RecipientEntry.constructFakeEntry((String) text, isValid(text.toString())),
-                    true, false);
-        } else if (currentChip.getContactId() == RecipientEntry.GENERATED_CONTACT) {
-            int start = getChipStart(currentChip);
-            int end = getChipEnd(currentChip);
-            getSpannable().removeSpan(currentChip);
-            DrawableRecipientChip newChip;
-            try {
-                if (mNoChips) {
-                    return null;
-                }
-                newChip = constructChipSpan(currentChip.getEntry(), true, false);
-            } catch (NullPointerException e) {
-                Log.e(TAG, e.getMessage(), e);
-                return null;
-            }
-            Editable editable = getText();
-            QwertyKeyListener.markAsReplaced(editable, start, end, "");
-            if (start == -1 || end == -1) {
-                Log.d(TAG, "The chip being selected no longer exists but should.");
-            } else {
-                editable.setSpan(newChip, start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
-            }
-            newChip.setSelected(true);
-            if (shouldShowEditableText(newChip)) {
-                scrollLineIntoView(getLayout().getLineForOffset(getChipStart(newChip)));
-            }
-            showAddress(newChip, mAddressPopup, getWidth());
-            setCursorVisible(false);
-            return newChip;
-        } else {
-            int start = getChipStart(currentChip);
-            int end = getChipEnd(currentChip);
-            getSpannable().removeSpan(currentChip);
-            DrawableRecipientChip newChip;
-            try {
-                newChip = constructChipSpan(currentChip.getEntry(), true, false);
-            } catch (NullPointerException e) {
-                Log.e(TAG, e.getMessage(), e);
-                return null;
-            }
-            Editable editable = getText();
-            QwertyKeyListener.markAsReplaced(editable, start, end, "");
-            if (start == -1 || end == -1) {
-                Log.d(TAG, "The chip being selected no longer exists but should.");
-            } else {
-                editable.setSpan(newChip, start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
-            }
-            newChip.setSelected(true);
-            if (shouldShowEditableText(newChip)) {
-                scrollLineIntoView(getLayout().getLineForOffset(getChipStart(newChip)));
-            }
-            showAlternates(newChip, mAlternatesPopup, getWidth());
-            setCursorVisible(false);
-            return newChip;
-        }
-    }
-
-    private boolean shouldShowEditableText(DrawableRecipientChip currentChip) {
-        long contactId = currentChip.getContactId();
-        return contactId == RecipientEntry.INVALID_CONTACT
-                || (!isPhoneQuery() && contactId == RecipientEntry.GENERATED_CONTACT);
-    }
-
-    private void showAddress(final DrawableRecipientChip currentChip, final ListPopupWindow popup,
-            int width) {
-        if (!mAttachedToWindow) {
-            return;
-        }
-        int line = getLayout().getLineForOffset(getChipStart(currentChip));
-        int bottom = calculateOffsetFromBottom(line);
-        // Align the alternates popup with the left side of the View,
-        // regardless of the position of the chip tapped.
-        popup.setWidth(width);
-        popup.setAnchorView(this);
-        popup.setVerticalOffset(bottom);
-        popup.setAdapter(createSingleAddressAdapter(currentChip));
-        popup.setOnItemClickListener(new OnItemClickListener() {
-            @Override
-            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
-                unselectChip(currentChip);
-                popup.dismiss();
-            }
-        });
-        popup.show();
-        ListView listView = popup.getListView();
-        listView.setChoiceMode(ListView.CHOICE_MODE_SINGLE);
-        listView.setItemChecked(0, true);
-    }
-
-    /**
-     * Remove selection from this chip. Unselecting a RecipientChip will render
-     * the chip without a delete icon and with an unfocused background. This is
-     * called when the RecipientChip no longer has focus.
-     */
-    private void unselectChip(DrawableRecipientChip chip) {
-        int start = getChipStart(chip);
-        int end = getChipEnd(chip);
-        Editable editable = getText();
-        mSelectedChip = null;
-        if (start == -1 || end == -1) {
-            Log.w(TAG, "The chip doesn't exist or may be a chip a user was editing");
-            setSelection(editable.length());
-            commitDefault();
-        } else {
-            getSpannable().removeSpan(chip);
-            QwertyKeyListener.markAsReplaced(editable, start, end, "");
-            editable.removeSpan(chip);
-            try {
-                if (!mNoChips) {
-                    editable.setSpan(constructChipSpan(chip.getEntry(), false, false),
-                            start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
-                }
-            } catch (NullPointerException e) {
-                Log.e(TAG, e.getMessage(), e);
-            }
-        }
-        setCursorVisible(true);
-        setSelection(editable.length());
-        if (mAlternatesPopup != null && mAlternatesPopup.isShowing()) {
-            mAlternatesPopup.dismiss();
-        }
-    }
-
-    /**
-     * Return whether a touch event was inside the delete target of
-     * a selected chip. It is in the delete target if:
-     * 1) the x and y points of the event are within the
-     * delete assset.
-     * 2) the point tapped would have caused a cursor to appear
-     * right after the selected chip.
-     * @return boolean
-     */
-    private boolean isInDelete(DrawableRecipientChip chip, int offset, float x, float y) {
-        // Figure out the bounds of this chip and whether or not
-        // the user clicked in the X portion.
-        // TODO: Should x and y be used, or removed?
-        if (mDisableDelete) {
-            return false;
-        }
-
-        return chip.isSelected() &&
-                ((mAvatarPosition == AVATAR_POSITION_END && offset == getChipEnd(chip)) ||
-                (mAvatarPosition != AVATAR_POSITION_END && offset == getChipStart(chip)));
-    }
-
-    /**
-     * Remove the chip and any text associated with it from the RecipientEditTextView.
-     */
-    // Visible for testing.
-    /* package */void removeChip(DrawableRecipientChip chip) {
-        Spannable spannable = getSpannable();
-        int spanStart = spannable.getSpanStart(chip);
-        int spanEnd = spannable.getSpanEnd(chip);
-        Editable text = getText();
-        int toDelete = spanEnd;
-        boolean wasSelected = chip == mSelectedChip;
-        // Clear that there is a selected chip before updating any text.
-        if (wasSelected) {
-            mSelectedChip = null;
-        }
-        // Always remove trailing spaces when removing a chip.
-        while (toDelete >= 0 && toDelete < text.length() && text.charAt(toDelete) == ' ') {
-            toDelete++;
-        }
-        spannable.removeSpan(chip);
-        if (spanStart >= 0 && toDelete > 0) {
-            text.delete(spanStart, toDelete);
-        }
-        if (wasSelected) {
-            clearSelectedChip();
-        }
-    }
-
-    /**
-     * Replace this currently selected chip with a new chip
-     * that uses the contact data provided.
-     */
-    // Visible for testing.
-    /*package*/ void replaceChip(DrawableRecipientChip chip, RecipientEntry entry) {
-        boolean wasSelected = chip == mSelectedChip;
-        if (wasSelected) {
-            mSelectedChip = null;
-        }
-        int start = getChipStart(chip);
-        int end = getChipEnd(chip);
-        getSpannable().removeSpan(chip);
-        Editable editable = getText();
-        CharSequence chipText = createChip(entry, false);
-        if (chipText != null) {
-            if (start == -1 || end == -1) {
-                Log.e(TAG, "The chip to replace does not exist but should.");
-                editable.insert(0, chipText);
-            } else {
-                if (!TextUtils.isEmpty(chipText)) {
-                    // There may be a space to replace with this chip's new
-                    // associated space. Check for it
-                    int toReplace = end;
-                    while (toReplace >= 0 && toReplace < editable.length()
-                            && editable.charAt(toReplace) == ' ') {
-                        toReplace++;
-                    }
-                    editable.replace(start, toReplace, chipText);
-                }
-            }
-        }
-        setCursorVisible(true);
-        if (wasSelected) {
-            clearSelectedChip();
-        }
-    }
-
-    /**
-     * Handle click events for a chip. When a selected chip receives a click
-     * event, see if that event was in the delete icon. If so, delete it.
-     * Otherwise, unselect the chip.
-     */
-    public void onClick(DrawableRecipientChip chip, int offset, float x, float y) {
-        if (chip.isSelected()) {
-            if (isInDelete(chip, offset, x, y)) {
-                removeChip(chip);
-            } else {
-                clearSelectedChip();
-            }
-        }
-    }
-
-    private boolean chipsPending() {
-        return mPendingChipsCount > 0 || (mRemovedSpans != null && mRemovedSpans.size() > 0);
-    }
-
-    @Override
-    public void removeTextChangedListener(TextWatcher watcher) {
-        mTextWatcher = null;
-        super.removeTextChangedListener(watcher);
-    }
-
-    private class RecipientTextWatcher implements TextWatcher {
-
-        @Override
-        public void afterTextChanged(Editable s) {
-            // If the text has been set to null or empty, make sure we remove
-            // all the spans we applied.
-            if (TextUtils.isEmpty(s)) {
-                // Remove all the chips spans.
-                Spannable spannable = getSpannable();
-                DrawableRecipientChip[] chips = spannable.getSpans(0, getText().length(),
-                        DrawableRecipientChip.class);
-                for (DrawableRecipientChip chip : chips) {
-                    spannable.removeSpan(chip);
-                }
-                if (mMoreChip != null) {
-                    spannable.removeSpan(mMoreChip);
-                }
-                clearSelectedChip();
-                return;
-            }
-            // Get whether there are any recipients pending addition to the
-            // view. If there are, don't do anything in the text watcher.
-            if (chipsPending()) {
-                return;
-            }
-            // If the user is editing a chip, don't clear it.
-            if (mSelectedChip != null) {
-                if (!isGeneratedContact(mSelectedChip)) {
-                    setCursorVisible(true);
-                    setSelection(getText().length());
-                    clearSelectedChip();
-                } else {
-                    return;
-                }
-            }
-            int length = s.length();
-            // Make sure there is content there to parse and that it is
-            // not just the commit character.
-            if (length > 1) {
-                if (lastCharacterIsCommitCharacter(s)) {
-                    commitByCharacter();
-                    return;
-                }
-                char last;
-                int end = getSelectionEnd() == 0 ? 0 : getSelectionEnd() - 1;
-                int len = length() - 1;
-                if (end != len) {
-                    last = s.charAt(end);
-                } else {
-                    last = s.charAt(len);
-                }
-                if (last == COMMIT_CHAR_SPACE) {
-                    if (!isPhoneQuery()) {
-                        // Check if this is a valid email address. If it is,
-                        // commit it.
-                        String text = getText().toString();
-                        int tokenStart = mTokenizer.findTokenStart(text, getSelectionEnd());
-                        String sub = text.substring(tokenStart, mTokenizer.findTokenEnd(text,
-                                tokenStart));
-                        if (!TextUtils.isEmpty(sub) && mValidator != null &&
-                                mValidator.isValid(sub)) {
-                            commitByCharacter();
-                        }
-                    }
-                }
-            }
-        }
-
-        @Override
-        public void onTextChanged(CharSequence s, int start, int before, int count) {
-            // The user deleted some text OR some text was replaced; check to
-            // see if the insertion point is on a space
-            // following a chip.
-            if (before - count == 1) {
-                // If the item deleted is a space, and the thing before the
-                // space is a chip, delete the entire span.
-                int selStart = getSelectionStart();
-                DrawableRecipientChip[] repl = getSpannable().getSpans(selStart, selStart,
-                        DrawableRecipientChip.class);
-                if (repl.length > 0) {
-                    // There is a chip there! Just remove it.
-                    Editable editable = getText();
-                    // Add the separator token.
-                    int tokenStart = mTokenizer.findTokenStart(editable, selStart);
-                    int tokenEnd = mTokenizer.findTokenEnd(editable, tokenStart);
-                    tokenEnd = tokenEnd + 1;
-                    if (tokenEnd > editable.length()) {
-                        tokenEnd = editable.length();
-                    }
-                    editable.delete(tokenStart, tokenEnd);
-                    getSpannable().removeSpan(repl[0]);
-                }
-            } else if (count > before) {
-                if (mSelectedChip != null
-                    && isGeneratedContact(mSelectedChip)) {
-                    if (lastCharacterIsCommitCharacter(s)) {
-                        commitByCharacter();
-                        return;
-                    }
-                }
-            }
-        }
-
-        @Override
-        public void beforeTextChanged(CharSequence s, int start, int count, int after) {
-            // Do nothing.
-        }
-    }
-
-   public boolean lastCharacterIsCommitCharacter(CharSequence s) {
-        char last;
-        int end = getSelectionEnd() == 0 ? 0 : getSelectionEnd() - 1;
-        int len = length() - 1;
-        if (end != len) {
-            last = s.charAt(end);
-        } else {
-            last = s.charAt(len);
-        }
-        return last == COMMIT_CHAR_COMMA || last == COMMIT_CHAR_SEMICOLON;
-    }
-
-    public boolean isGeneratedContact(DrawableRecipientChip chip) {
-        long contactId = chip.getContactId();
-        return contactId == RecipientEntry.INVALID_CONTACT
-                || (!isPhoneQuery() && contactId == RecipientEntry.GENERATED_CONTACT);
-    }
-
-    /**
-     * Handles pasting a {@link ClipData} to this {@link RecipientEditTextView}.
-     */
-    private void handlePasteClip(ClipData clip) {
-        removeTextChangedListener(mTextWatcher);
-
-        if (clip != null && clip.getDescription().hasMimeType(ClipDescription.MIMETYPE_TEXT_PLAIN)){
-            for (int i = 0; i < clip.getItemCount(); i++) {
-                CharSequence paste = clip.getItemAt(i).getText();
-                if (paste != null) {
-                    int start = getSelectionStart();
-                    int end = getSelectionEnd();
-                    Editable editable = getText();
-                    if (start >= 0 && end >= 0 && start != end) {
-                        editable.append(paste, start, end);
-                    } else {
-                        editable.insert(end, paste);
-                    }
-                    handlePasteAndReplace();
-                }
-            }
-        }
-
-        mHandler.post(mAddTextWatcher);
-    }
-
-    @Override
-    public boolean onTextContextMenuItem(int id) {
-        if (id == android.R.id.paste) {
-            ClipboardManager clipboard = (ClipboardManager) getContext().getSystemService(
-                    Context.CLIPBOARD_SERVICE);
-            handlePasteClip(clipboard.getPrimaryClip());
-            return true;
-        }
-        return super.onTextContextMenuItem(id);
-    }
-
-    private void handlePasteAndReplace() {
-        ArrayList<DrawableRecipientChip> created = handlePaste();
-        if (created != null && created.size() > 0) {
-            // Perform reverse lookups on the pasted contacts.
-            IndividualReplacementTask replace = new IndividualReplacementTask();
-            replace.execute(created);
-        }
-    }
-
-    // Visible for testing.
-    /* package */ArrayList<DrawableRecipientChip> handlePaste() {
-        String text = getText().toString();
-        int originalTokenStart = mTokenizer.findTokenStart(text, getSelectionEnd());
-        String lastAddress = text.substring(originalTokenStart);
-        int tokenStart = originalTokenStart;
-        int prevTokenStart = 0;
-        DrawableRecipientChip findChip = null;
-        ArrayList<DrawableRecipientChip> created = new ArrayList<DrawableRecipientChip>();
-        if (tokenStart != 0) {
-            // There are things before this!
-            while (tokenStart != 0 && findChip == null && tokenStart != prevTokenStart) {
-                prevTokenStart = tokenStart;
-                tokenStart = mTokenizer.findTokenStart(text, tokenStart);
-                findChip = findChip(tokenStart);
-                if (tokenStart == originalTokenStart && findChip == null) {
-                    break;
-                }
-            }
-            if (tokenStart != originalTokenStart) {
-                if (findChip != null) {
-                    tokenStart = prevTokenStart;
-                }
-                int tokenEnd;
-                DrawableRecipientChip createdChip;
-                while (tokenStart < originalTokenStart) {
-                    tokenEnd = movePastTerminators(mTokenizer.findTokenEnd(getText().toString(),
-                            tokenStart));
-                    commitChip(tokenStart, tokenEnd, getText());
-                    createdChip = findChip(tokenStart);
-                    if (createdChip == null) {
-                        break;
-                    }
-                    // +1 for the space at the end.
-                    tokenStart = getSpannable().getSpanEnd(createdChip) + 1;
-                    created.add(createdChip);
-                }
-            }
-        }
-        // Take a look at the last token. If the token has been completed with a
-        // commit character, create a chip.
-        if (isCompletedToken(lastAddress)) {
-            Editable editable = getText();
-            tokenStart = editable.toString().indexOf(lastAddress, originalTokenStart);
-            commitChip(tokenStart, editable.length(), editable);
-            created.add(findChip(tokenStart));
-        }
-        return created;
-    }
-
-    // Visible for testing.
-    /* package */int movePastTerminators(int tokenEnd) {
-        if (tokenEnd >= length()) {
-            return tokenEnd;
-        }
-        char atEnd = getText().toString().charAt(tokenEnd);
-        if (atEnd == COMMIT_CHAR_COMMA || atEnd == COMMIT_CHAR_SEMICOLON) {
-            tokenEnd++;
-        }
-        // This token had not only an end token character, but also a space
-        // separating it from the next token.
-        if (tokenEnd < length() && getText().toString().charAt(tokenEnd) == ' ') {
-            tokenEnd++;
-        }
-        return tokenEnd;
-    }
-
-    private class RecipientReplacementTask extends AsyncTask<Void, Void, Void> {
-        private DrawableRecipientChip createFreeChip(RecipientEntry entry) {
-            try {
-                if (mNoChips) {
-                    return null;
-                }
-                return constructChipSpan(entry, false,
-                        false /*leave space for contact icon */);
-            } catch (NullPointerException e) {
-                Log.e(TAG, e.getMessage(), e);
-                return null;
-            }
-        }
-
-        @Override
-        protected void onPreExecute() {
-            // Ensure everything is in chip-form already, so we don't have text that slowly gets
-            // replaced
-            final List<DrawableRecipientChip> originalRecipients =
-                    new ArrayList<DrawableRecipientChip>();
-            final DrawableRecipientChip[] existingChips = getSortedRecipients();
-            for (int i = 0; i < existingChips.length; i++) {
-                originalRecipients.add(existingChips[i]);
-            }
-            if (mRemovedSpans != null) {
-                originalRecipients.addAll(mRemovedSpans);
-            }
-
-            final List<DrawableRecipientChip> replacements =
-                    new ArrayList<DrawableRecipientChip>(originalRecipients.size());
-
-            for (final DrawableRecipientChip chip : originalRecipients) {
-                if (RecipientEntry.isCreatedRecipient(chip.getEntry().getContactId())
-                        && getSpannable().getSpanStart(chip) != -1) {
-                    replacements.add(createFreeChip(chip.getEntry()));
-                } else {
-                    replacements.add(null);
-                }
-            }
-
-            processReplacements(originalRecipients, replacements);
-        }
-
-        @Override
-        protected Void doInBackground(Void... params) {
-            if (mIndividualReplacements != null) {
-                mIndividualReplacements.cancel(true);
-            }
-            // For each chip in the list, look up the matching contact.
-            // If there is a match, replace that chip with the matching
-            // chip.
-            final ArrayList<DrawableRecipientChip> recipients =
-                    new ArrayList<DrawableRecipientChip>();
-            DrawableRecipientChip[] existingChips = getSortedRecipients();
-            for (int i = 0; i < existingChips.length; i++) {
-                recipients.add(existingChips[i]);
-            }
-            if (mRemovedSpans != null) {
-                recipients.addAll(mRemovedSpans);
-            }
-            ArrayList<String> addresses = new ArrayList<String>();
-            DrawableRecipientChip chip;
-            for (int i = 0; i < recipients.size(); i++) {
-                chip = recipients.get(i);
-                if (chip != null) {
-                    addresses.add(createAddressText(chip.getEntry()));
-                }
-            }
-            final BaseRecipientAdapter adapter = getAdapter();
-            RecipientAlternatesAdapter.getMatchingRecipients(getContext(), adapter, addresses,
-                    adapter.getAccount(), new RecipientMatchCallback() {
-                        @Override
-                        public void matchesFound(Map<String, RecipientEntry> entries) {
-                            final ArrayList<DrawableRecipientChip> replacements =
-                                    new ArrayList<DrawableRecipientChip>();
-                            for (final DrawableRecipientChip temp : recipients) {
-                                RecipientEntry entry = null;
-                                if (temp != null && RecipientEntry.isCreatedRecipient(
-                                        temp.getEntry().getContactId())
-                                        && getSpannable().getSpanStart(temp) != -1) {
-                                    // Replace this.
-                                    entry = createValidatedEntry(
-                                            entries.get(tokenizeAddress(temp.getEntry()
-                                                    .getDestination())));
-                                }
-                                if (entry != null) {
-                                    replacements.add(createFreeChip(entry));
-                                } else {
-                                    replacements.add(null);
-                                }
-                            }
-                            processReplacements(recipients, replacements);
-                        }
-
-                        @Override
-                        public void matchesNotFound(final Set<String> unfoundAddresses) {
-                            final List<DrawableRecipientChip> replacements =
-                                    new ArrayList<DrawableRecipientChip>(unfoundAddresses.size());
-
-                            for (final DrawableRecipientChip temp : recipients) {
-                                if (temp != null && RecipientEntry.isCreatedRecipient(
-                                        temp.getEntry().getContactId())
-                                        && getSpannable().getSpanStart(temp) != -1) {
-                                    if (unfoundAddresses.contains(
-                                            temp.getEntry().getDestination())) {
-                                        replacements.add(createFreeChip(temp.getEntry()));
-                                    } else {
-                                        replacements.add(null);
-                                    }
-                                } else {
-                                    replacements.add(null);
-                                }
-                            }
-
-                            processReplacements(recipients, replacements);
-                        }
-                    });
-            return null;
-        }
-
-        private void processReplacements(final List<DrawableRecipientChip> recipients,
-                final List<DrawableRecipientChip> replacements) {
-            if (replacements != null && replacements.size() > 0) {
-                final Runnable runnable = new Runnable() {
-                    @Override
-                    public void run() {
-                        final Editable text = new SpannableStringBuilder(getText());
-                        int i = 0;
-                        for (final DrawableRecipientChip chip : recipients) {
-                            final DrawableRecipientChip replacement = replacements.get(i);
-                            if (replacement != null) {
-                                final RecipientEntry oldEntry = chip.getEntry();
-                                final RecipientEntry newEntry = replacement.getEntry();
-                                final boolean isBetter =
-                                        RecipientAlternatesAdapter.getBetterRecipient(
-                                                oldEntry, newEntry) == newEntry;
-
-                                if (isBetter) {
-                                    // Find the location of the chip in the text currently shown.
-                                    final int start = text.getSpanStart(chip);
-                                    if (start != -1) {
-                                        // Replacing the entirety of what the chip represented,
-                                        // including the extra space dividing it from other chips.
-                                        final int end =
-                                                Math.min(text.getSpanEnd(chip) + 1, text.length());
-                                        text.removeSpan(chip);
-                                        // Make sure we always have just 1 space at the end to
-                                        // separate this chip from the next chip.
-                                        final SpannableString displayText =
-                                                new SpannableString(createAddressText(
-                                                        replacement.getEntry()).trim() + " ");
-                                        displayText.setSpan(replacement, 0,
-                                                displayText.length() - 1,
-                                                Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
-                                        // Replace the old text we found with with the new display
-                                        // text, which now may also contain the display name of the
-                                        // recipient.
-                                        text.replace(start, end, displayText);
-                                        replacement.setOriginalText(displayText.toString());
-                                        replacements.set(i, null);
-
-                                        recipients.set(i, replacement);
-                                    }
-                                }
-                            }
-                            i++;
-                        }
-                        setText(text);
-                    }
-                };
-
-                if (Looper.myLooper() == Looper.getMainLooper()) {
-                    runnable.run();
-                } else {
-                    mHandler.post(runnable);
-                }
-            }
-        }
-    }
-
-    private class IndividualReplacementTask
-            extends AsyncTask<ArrayList<DrawableRecipientChip>, Void, Void> {
-        @Override
-        protected Void doInBackground(ArrayList<DrawableRecipientChip>... params) {
-            // For each chip in the list, look up the matching contact.
-            // If there is a match, replace that chip with the matching
-            // chip.
-            final ArrayList<DrawableRecipientChip> originalRecipients = params[0];
-            ArrayList<String> addresses = new ArrayList<String>();
-            DrawableRecipientChip chip;
-            for (int i = 0; i < originalRecipients.size(); i++) {
-                chip = originalRecipients.get(i);
-                if (chip != null) {
-                    addresses.add(createAddressText(chip.getEntry()));
-                }
-            }
-            final BaseRecipientAdapter adapter = getAdapter();
-            RecipientAlternatesAdapter.getMatchingRecipients(getContext(), adapter, addresses,
-                    adapter.getAccount(),
-                    new RecipientMatchCallback() {
-
-                        @Override
-                        public void matchesFound(Map<String, RecipientEntry> entries) {
-                            for (final DrawableRecipientChip temp : originalRecipients) {
-                                if (RecipientEntry.isCreatedRecipient(temp.getEntry()
-                                        .getContactId())
-                                        && getSpannable().getSpanStart(temp) != -1) {
-                                    // Replace this.
-                                    final RecipientEntry entry = createValidatedEntry(entries
-                                            .get(tokenizeAddress(temp.getEntry().getDestination())
-                                                    .toLowerCase()));
-                                    if (entry != null) {
-                                        mHandler.post(new Runnable() {
-                                            @Override
-                                            public void run() {
-                                                replaceChip(temp, entry);
-                                            }
-                                        });
-                                    }
-                                }
-                            }
-                        }
-
-                        @Override
-                        public void matchesNotFound(final Set<String> unfoundAddresses) {
-                            // No action required
-                        }
-                    });
-            return null;
-        }
-    }
-
-
-    /**
-     * MoreImageSpan is a simple class created for tracking the existence of a
-     * more chip across activity restarts/
-     */
-    private class MoreImageSpan extends ImageSpan {
-        public MoreImageSpan(Drawable b) {
-            super(b);
-        }
-    }
-
-    @Override
-    public boolean onDown(MotionEvent e) {
-        return false;
-    }
-
-    @Override
-    public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
-        // Do nothing.
-        return false;
-    }
-
-    @Override
-    public void onLongPress(MotionEvent event) {
-        if (mSelectedChip != null) {
-            return;
-        }
-        float x = event.getX();
-        float y = event.getY();
-        final int offset = putOffsetInRange(x, y);
-        DrawableRecipientChip currentChip = findChip(offset);
-        if (currentChip != null) {
-            if (mDragEnabled) {
-                // Start drag-and-drop for the selected chip.
-                startDrag(currentChip);
-            } else {
-                // Copy the selected chip email address.
-                showCopyDialog(currentChip.getEntry().getDestination());
-            }
-        }
-    }
-
-    // The following methods are used to provide some functionality on older versions of Android
-    // These methods were copied out of JB MR2's TextView
-    /////////////////////////////////////////////////
-    private int supportGetOffsetForPosition(float x, float y) {
-        if (getLayout() == null) return -1;
-        final int line = supportGetLineAtCoordinate(y);
-        final int offset = supportGetOffsetAtCoordinate(line, x);
-        return offset;
-    }
-
-    private float supportConvertToLocalHorizontalCoordinate(float x) {
-        x -= getTotalPaddingLeft();
-        // Clamp the position to inside of the view.
-        x = Math.max(0.0f, x);
-        x = Math.min(getWidth() - getTotalPaddingRight() - 1, x);
-        x += getScrollX();
-        return x;
-    }
-
-    private int supportGetLineAtCoordinate(float y) {
-        y -= getTotalPaddingLeft();
-        // Clamp the position to inside of the view.
-        y = Math.max(0.0f, y);
-        y = Math.min(getHeight() - getTotalPaddingBottom() - 1, y);
-        y += getScrollY();
-        return getLayout().getLineForVertical((int) y);
-    }
-
-    private int supportGetOffsetAtCoordinate(int line, float x) {
-        x = supportConvertToLocalHorizontalCoordinate(x);
-        return getLayout().getOffsetForHorizontal(line, x);
-    }
-    /////////////////////////////////////////////////
-
-    /**
-     * Enables drag-and-drop for chips.
-     */
-    public void enableDrag() {
-        mDragEnabled = true;
-    }
-
-    /**
-     * Starts drag-and-drop for the selected chip.
-     */
-    private void startDrag(DrawableRecipientChip currentChip) {
-        String address = currentChip.getEntry().getDestination();
-        ClipData data = ClipData.newPlainText(address, address + COMMIT_CHAR_COMMA);
-
-        // Start drag mode.
-        startDrag(data, new RecipientChipShadow(currentChip), null, 0);
-
-        // Remove the current chip, so drag-and-drop will result in a move.
-        // TODO (phamm): consider readd this chip if it's dropped outside a target.
-        removeChip(currentChip);
-    }
-
-    /**
-     * Handles drag event.
-     */
-    @Override
-    public boolean onDragEvent(DragEvent event) {
-        switch (event.getAction()) {
-            case DragEvent.ACTION_DRAG_STARTED:
-                // Only handle plain text drag and drop.
-                return event.getClipDescription().hasMimeType(ClipDescription.MIMETYPE_TEXT_PLAIN);
-            case DragEvent.ACTION_DRAG_ENTERED:
-                requestFocus();
-                return true;
-            case DragEvent.ACTION_DROP:
-                handlePasteClip(event.getClipData());
-                return true;
-        }
-        return false;
-    }
-
-    /**
-     * Drag shadow for a {@link RecipientChip}.
-     */
-    private final class RecipientChipShadow extends DragShadowBuilder {
-        private final DrawableRecipientChip mChip;
-
-        public RecipientChipShadow(DrawableRecipientChip chip) {
-            mChip = chip;
-        }
-
-        @Override
-        public void onProvideShadowMetrics(Point shadowSize, Point shadowTouchPoint) {
-            Rect rect = mChip.getBounds();
-            shadowSize.set(rect.width(), rect.height());
-            shadowTouchPoint.set(rect.centerX(), rect.centerY());
-        }
-
-        @Override
-        public void onDrawShadow(Canvas canvas) {
-            mChip.draw(canvas);
-        }
-    }
-
-    private void showCopyDialog(final String address) {
-        if (!mAttachedToWindow) {
-            return;
-        }
-        mCopyAddress = address;
-        mCopyDialog.setTitle(address);
-        mCopyDialog.setContentView(R.layout.copy_chip_dialog_layout);
-        mCopyDialog.setCancelable(true);
-        mCopyDialog.setCanceledOnTouchOutside(true);
-        Button button = (Button)mCopyDialog.findViewById(android.R.id.button1);
-        button.setOnClickListener(this);
-        int btnTitleId;
-        if (isPhoneQuery()) {
-            btnTitleId = R.string.copy_number;
-        } else {
-            btnTitleId = R.string.copy_email;
-        }
-        String buttonTitle = getContext().getResources().getString(btnTitleId);
-        button.setText(buttonTitle);
-        mCopyDialog.setOnDismissListener(this);
-        mCopyDialog.show();
-    }
-
-    @Override
-    public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
-        // Do nothing.
-        return false;
-    }
-
-    @Override
-    public void onShowPress(MotionEvent e) {
-        // Do nothing.
-    }
-
-    @Override
-    public boolean onSingleTapUp(MotionEvent e) {
-        // Do nothing.
-        return false;
-    }
-
-    @Override
-    public void onDismiss(DialogInterface dialog) {
-        mCopyAddress = null;
-    }
-
-    @Override
-    public void onClick(View v) {
-        // Copy this to the clipboard.
-        ClipboardManager clipboard = (ClipboardManager) getContext().getSystemService(
-                Context.CLIPBOARD_SERVICE);
-        clipboard.setPrimaryClip(ClipData.newPlainText("", mCopyAddress));
-        mCopyDialog.dismiss();
-    }
-
-    protected boolean isPhoneQuery() {
-        return getAdapter() != null
-                && getAdapter().getQueryType() == BaseRecipientAdapter.QUERY_TYPE_PHONE;
-    }
-
-    @Override
-    public BaseRecipientAdapter getAdapter() {
-        return (BaseRecipientAdapter) super.getAdapter();
-    }
-}
diff --git a/chips/src/com/android/ex/chips/RecipientEntry.java b/chips/src/com/android/ex/chips/RecipientEntry.java
deleted file mode 100644
index 7d9b87f..0000000
--- a/chips/src/com/android/ex/chips/RecipientEntry.java
+++ /dev/null
@@ -1,256 +0,0 @@
-/*
- * 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.android.ex.chips;
-
-import android.net.Uri;
-import android.provider.ContactsContract.CommonDataKinds.Email;
-import android.provider.ContactsContract.DisplayNameSources;
-import android.text.util.Rfc822Token;
-import android.text.util.Rfc822Tokenizer;
-
-/**
- * Represents one entry inside recipient auto-complete list.
- */
-public class RecipientEntry {
-    /* package */ static final int INVALID_CONTACT = -1;
-    /**
-     * A GENERATED_CONTACT is one that was created based entirely on
-     * information passed in to the RecipientEntry from an external source
-     * that is not a real contact.
-     */
-    /* package */ static final int GENERATED_CONTACT = -2;
-
-    /** Used when {@link #mDestinationType} is invalid and thus shouldn't be used for display. */
-    /* package */ static final int INVALID_DESTINATION_TYPE = -1;
-
-    public static final int ENTRY_TYPE_PERSON = 0;
-
-    public static final int ENTRY_TYPE_SIZE = 1;
-
-    private final int mEntryType;
-
-    /**
-     * True when this entry is the first entry in a group, which should have a photo and display
-     * name, while the second or later entries won't.
-     */
-    private boolean mIsFirstLevel;
-    private final String mDisplayName;
-
-    /** Destination for this contact entry. Would be an email address or a phone number. */
-    private final String mDestination;
-    /** Type of the destination like {@link Email#TYPE_HOME} */
-    private final int mDestinationType;
-    /**
-     * Label of the destination which will be used when type was {@link Email#TYPE_CUSTOM}.
-     * Can be null when {@link #mDestinationType} is {@link #INVALID_DESTINATION_TYPE}.
-     */
-    private final String mDestinationLabel;
-    /** ID for the person */
-    private final long mContactId;
-    /** ID for the directory this contact came from, or <code>null</code> */
-    private final Long mDirectoryId;
-    /** ID for the destination */
-    private final long mDataId;
-    private final boolean mIsDivider;
-
-    private final Uri mPhotoThumbnailUri;
-
-    private boolean mIsValid;
-    /**
-     * This can be updated after this object being constructed, when the photo is fetched
-     * from remote directories.
-     */
-    private byte[] mPhotoBytes;
-
-    /** See {@link ContactsContract.Contacts#LOOKUP_KEY} */
-    private final String mLookupKey;
-
-    private RecipientEntry(int entryType, String displayName, String destination,
-            int destinationType, String destinationLabel, long contactId, Long directoryId,
-            long dataId, Uri photoThumbnailUri, boolean isFirstLevel, boolean isValid,
-            String lookupKey) {
-        mEntryType = entryType;
-        mIsFirstLevel = isFirstLevel;
-        mDisplayName = displayName;
-        mDestination = destination;
-        mDestinationType = destinationType;
-        mDestinationLabel = destinationLabel;
-        mContactId = contactId;
-        mDirectoryId = directoryId;
-        mDataId = dataId;
-        mPhotoThumbnailUri = photoThumbnailUri;
-        mPhotoBytes = null;
-        mIsDivider = false;
-        mIsValid = isValid;
-        mLookupKey = lookupKey;
-    }
-
-    public boolean isValid() {
-        return mIsValid;
-    }
-
-    /**
-     * Determine if this was a RecipientEntry created from recipient info or
-     * an entry from contacts.
-     */
-    public static boolean isCreatedRecipient(long id) {
-        return id == RecipientEntry.INVALID_CONTACT || id == RecipientEntry.GENERATED_CONTACT;
-    }
-
-    /**
-     * Construct a RecipientEntry from just an address that has been entered.
-     * This address has not been resolved to a contact and therefore does not
-     * have a contact id or photo.
-     */
-    public static RecipientEntry constructFakeEntry(final String address, final boolean isValid) {
-        final Rfc822Token[] tokens = Rfc822Tokenizer.tokenize(address);
-        final String tokenizedAddress = tokens.length > 0 ? tokens[0].getAddress() : address;
-
-        return new RecipientEntry(ENTRY_TYPE_PERSON, tokenizedAddress, tokenizedAddress,
-                INVALID_DESTINATION_TYPE, null, INVALID_CONTACT, null /* directoryId */,
-                INVALID_CONTACT, null, true, isValid, null /* lookupKey */);
-    }
-
-    /**
-     * Construct a RecipientEntry from just a phone number.
-     */
-    public static RecipientEntry constructFakePhoneEntry(final String phoneNumber,
-            final boolean isValid) {
-        return new RecipientEntry(ENTRY_TYPE_PERSON, phoneNumber, phoneNumber,
-                INVALID_DESTINATION_TYPE, null, INVALID_CONTACT, null /* directoryId */,
-                INVALID_CONTACT, null, true, isValid, null /* lookupKey */);
-    }
-
-    /**
-     * @return the display name for the entry.  If the display name source is larger than
-     * {@link DisplayNameSources#PHONE} we use the contact's display name, but if not,
-     * i.e. the display name came from an email address or a phone number, we don't use it
-     * to avoid confusion and just use the destination instead.
-     */
-    private static String pickDisplayName(int displayNameSource, String displayName,
-            String destination) {
-        return (displayNameSource > DisplayNameSources.PHONE) ? displayName : destination;
-    }
-
-    /**
-     * Construct a RecipientEntry from just an address that has been entered
-     * with both an associated display name. This address has not been resolved
-     * to a contact and therefore does not have a contact id or photo.
-     */
-    public static RecipientEntry constructGeneratedEntry(String display, String address,
-            boolean isValid) {
-        return new RecipientEntry(ENTRY_TYPE_PERSON, display, address, INVALID_DESTINATION_TYPE,
-                null, GENERATED_CONTACT, null /* directoryId */, GENERATED_CONTACT, null, true,
-                isValid, null /* lookupKey */);
-    }
-
-    public static RecipientEntry constructTopLevelEntry(String displayName, int displayNameSource,
-            String destination, int destinationType, String destinationLabel, long contactId,
-            Long directoryId, long dataId, Uri photoThumbnailUri, boolean isValid,
-            String lookupKey) {
-        return new RecipientEntry(ENTRY_TYPE_PERSON, pickDisplayName(displayNameSource,
-                displayName, destination), destination, destinationType, destinationLabel,
-                contactId, directoryId, dataId, photoThumbnailUri, true, isValid, lookupKey);
-    }
-
-    public static RecipientEntry constructTopLevelEntry(String displayName, int displayNameSource,
-            String destination, int destinationType, String destinationLabel, long contactId,
-            Long directoryId, long dataId, String thumbnailUriAsString, boolean isValid,
-            String lookupKey) {
-        return new RecipientEntry(ENTRY_TYPE_PERSON, pickDisplayName(displayNameSource,
-                displayName, destination), destination, destinationType, destinationLabel,
-                contactId, directoryId, dataId, (thumbnailUriAsString != null
-                        ? Uri.parse(thumbnailUriAsString) : null), true, isValid, lookupKey);
-    }
-
-    public static RecipientEntry constructSecondLevelEntry(String displayName,
-            int displayNameSource, String destination, int destinationType,
-            String destinationLabel, long contactId, Long directoryId, long dataId,
-            String thumbnailUriAsString, boolean isValid, String lookupKey) {
-        return new RecipientEntry(ENTRY_TYPE_PERSON, pickDisplayName(displayNameSource,
-                displayName, destination), destination, destinationType, destinationLabel,
-                contactId, directoryId, dataId, (thumbnailUriAsString != null
-                        ? Uri.parse(thumbnailUriAsString) : null), false, isValid, lookupKey);
-    }
-
-    public int getEntryType() {
-        return mEntryType;
-    }
-
-    public String getDisplayName() {
-        return mDisplayName;
-    }
-
-    public String getDestination() {
-        return mDestination;
-    }
-
-    public int getDestinationType() {
-        return mDestinationType;
-    }
-
-    public String getDestinationLabel() {
-        return mDestinationLabel;
-    }
-
-    public long getContactId() {
-        return mContactId;
-    }
-
-    public Long getDirectoryId() {
-        return mDirectoryId;
-    }
-
-    public long getDataId() {
-        return mDataId;
-    }
-
-    public boolean isFirstLevel() {
-        return mIsFirstLevel;
-    }
-
-    public Uri getPhotoThumbnailUri() {
-        return mPhotoThumbnailUri;
-    }
-
-    /** This can be called outside main Looper thread. */
-    public synchronized void setPhotoBytes(byte[] photoBytes) {
-        mPhotoBytes = photoBytes;
-    }
-
-    /** This can be called outside main Looper thread. */
-    public synchronized byte[] getPhotoBytes() {
-        return mPhotoBytes;
-    }
-
-    public boolean isSeparator() {
-        return mIsDivider;
-    }
-
-    public boolean isSelectable() {
-        return mEntryType == ENTRY_TYPE_PERSON;
-    }
-
-    public String getLookupKey() {
-        return mLookupKey;
-    }
-
-    @Override
-    public String toString() {
-        return mDisplayName + " <" + mDestination + ">, isValid=" + mIsValid;
-    }
-}
\ No newline at end of file
diff --git a/chips/src/com/android/ex/chips/SingleRecipientArrayAdapter.java b/chips/src/com/android/ex/chips/SingleRecipientArrayAdapter.java
deleted file mode 100644
index 985953f..0000000
--- a/chips/src/com/android/ex/chips/SingleRecipientArrayAdapter.java
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- * 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.android.ex.chips;
-
-import android.content.Context;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.ArrayAdapter;
-
-import com.android.ex.chips.DropdownChipLayouter.AdapterType;
-
-class SingleRecipientArrayAdapter extends ArrayAdapter<RecipientEntry> {
-    private final DropdownChipLayouter mDropdownChipLayouter;
-
-    public SingleRecipientArrayAdapter(Context context, RecipientEntry entry,
-            DropdownChipLayouter dropdownChipLayouter) {
-        super(context, dropdownChipLayouter.getAlternateItemLayoutResId(), new RecipientEntry[] {
-            entry
-        });
-
-        mDropdownChipLayouter = dropdownChipLayouter;
-    }
-
-    @Override
-    public View getView(int position, View convertView, ViewGroup parent) {
-        return mDropdownChipLayouter.bindView(convertView, parent, getItem(position), position,
-                AdapterType.SINGLE_RECIPIENT, null);
-    }
-}
diff --git a/chips/src/com/android/ex/chips/recipientchip/BaseRecipientChip.java b/chips/src/com/android/ex/chips/recipientchip/BaseRecipientChip.java
deleted file mode 100644
index 8012b5c..0000000
--- a/chips/src/com/android/ex/chips/recipientchip/BaseRecipientChip.java
+++ /dev/null
@@ -1,83 +0,0 @@
-/*
- * 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.android.ex.chips.recipientchip;
-
-import com.android.ex.chips.RecipientEntry;
-
-/**
- * BaseRecipientChip defines an object that contains information relevant to a
- * particular recipient.
- */
-interface BaseRecipientChip {
-
-    /**
-     * Set the selected state of the chip.
-     */
-    void setSelected(boolean selected);
-
-    /**
-     * Return true if the chip is selected.
-     */
-    boolean isSelected();
-
-    /**
-     * Get the text displayed in the chip.
-     */
-    CharSequence getDisplay();
-
-    /**
-     * Get the text value this chip represents.
-     */
-    CharSequence getValue();
-
-    /**
-     * Get the id of the contact associated with this chip.
-     */
-    long getContactId();
-
-    /**
-     * Get the directory id of the contact associated with this chip.
-     */
-    Long getDirectoryId();
-
-    /**
-     * Get the directory lookup key associated with this chip, or <code>null</code>.
-     */
-    String getLookupKey();
-
-    /**
-     * Get the id of the data associated with this chip.
-     */
-    long getDataId();
-
-    /**
-     * Get associated RecipientEntry.
-     */
-    RecipientEntry getEntry();
-
-    /**
-     * Set the text in the edittextview originally associated with this chip
-     * before any reverse lookups.
-     */
-    void setOriginalText(String text);
-
-    /**
-     * Set the text in the edittextview originally associated with this chip
-     * before any reverse lookups.
-     */
-    CharSequence getOriginalText();
-}
diff --git a/chips/src/com/android/ex/chips/recipientchip/DrawableRecipientChip.java b/chips/src/com/android/ex/chips/recipientchip/DrawableRecipientChip.java
deleted file mode 100644
index 396a8ac..0000000
--- a/chips/src/com/android/ex/chips/recipientchip/DrawableRecipientChip.java
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * 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.android.ex.chips.recipientchip;
-
-import android.graphics.Canvas;
-import android.graphics.Rect;
-
-/**
- * RecipientChip defines a drawable object that contains information relevant to a
- * particular recipient.
- */
-public interface DrawableRecipientChip extends BaseRecipientChip {
-    /**
-     * Get the bounds of the chip; may be 0,0 if it is not visibly rendered.
-     */
-    Rect getBounds();
-
-    /**
-     * Draw the chip.
-     */
-    void draw(Canvas canvas);
-}
diff --git a/chips/src/com/android/ex/chips/recipientchip/InvisibleRecipientChip.java b/chips/src/com/android/ex/chips/recipientchip/InvisibleRecipientChip.java
deleted file mode 100644
index 455f2cb..0000000
--- a/chips/src/com/android/ex/chips/recipientchip/InvisibleRecipientChip.java
+++ /dev/null
@@ -1,115 +0,0 @@
-/*
- * 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.android.ex.chips.recipientchip;
-
-import android.graphics.Canvas;
-import android.graphics.Paint;
-import android.graphics.Rect;
-import android.text.style.ReplacementSpan;
-
-import com.android.ex.chips.RecipientEntry;
-
-/**
- * RecipientChip defines a span that contains information relevant to a
- * particular recipient.
- */
-public class InvisibleRecipientChip extends ReplacementSpan implements DrawableRecipientChip {
-    private final SimpleRecipientChip mDelegate;
-
-    public InvisibleRecipientChip(final RecipientEntry entry) {
-        super();
-
-        mDelegate = new SimpleRecipientChip(entry);
-    }
-
-    @Override
-    public void setSelected(final boolean selected) {
-        mDelegate.setSelected(selected);
-    }
-
-    @Override
-    public boolean isSelected() {
-        return mDelegate.isSelected();
-    }
-
-    @Override
-    public CharSequence getDisplay() {
-        return mDelegate.getDisplay();
-    }
-
-    @Override
-    public CharSequence getValue() {
-        return mDelegate.getValue();
-    }
-
-    @Override
-    public long getContactId() {
-        return mDelegate.getContactId();
-    }
-
-    @Override
-    public Long getDirectoryId() {
-        return mDelegate.getDirectoryId();
-    }
-
-    @Override
-    public String getLookupKey() {
-        return mDelegate.getLookupKey();
-    }
-
-    @Override
-    public long getDataId() {
-        return mDelegate.getDataId();
-    }
-
-    @Override
-    public RecipientEntry getEntry() {
-        return mDelegate.getEntry();
-    }
-
-    @Override
-    public void setOriginalText(final String text) {
-        mDelegate.setOriginalText(text);
-    }
-
-    @Override
-    public CharSequence getOriginalText() {
-        return mDelegate.getOriginalText();
-    }
-
-    @Override
-    public void draw(final Canvas canvas, final CharSequence text, final int start, final int end,
-            final float x, final int top, final int y, final int bottom, final Paint paint) {
-        // Do nothing.
-    }
-
-    @Override
-    public int getSize(final Paint paint, final CharSequence text, final int start, final int end,
-            final Paint.FontMetricsInt fm) {
-        return 0;
-    }
-
-    @Override
-    public Rect getBounds() {
-        return new Rect(0, 0, 0, 0);
-    }
-
-    @Override
-    public void draw(final Canvas canvas) {
-        // do nothing.
-    }
-}
diff --git a/chips/src/com/android/ex/chips/recipientchip/SimpleRecipientChip.java b/chips/src/com/android/ex/chips/recipientchip/SimpleRecipientChip.java
deleted file mode 100644
index 533f53f..0000000
--- a/chips/src/com/android/ex/chips/recipientchip/SimpleRecipientChip.java
+++ /dev/null
@@ -1,115 +0,0 @@
-/*
- * 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.android.ex.chips.recipientchip;
-
-import com.android.ex.chips.RecipientEntry;
-
-import android.text.TextUtils;
-
-class SimpleRecipientChip implements BaseRecipientChip {
-    private final CharSequence mDisplay;
-
-    private final CharSequence mValue;
-
-    private final long mContactId;
-
-    private final Long mDirectoryId;
-
-    private final String mLookupKey;
-
-    private final long mDataId;
-
-    private final RecipientEntry mEntry;
-
-    private boolean mSelected = false;
-
-    private CharSequence mOriginalText;
-
-    public SimpleRecipientChip(final RecipientEntry entry) {
-        mDisplay = entry.getDisplayName();
-        mValue = entry.getDestination().trim();
-        mContactId = entry.getContactId();
-        mDirectoryId = entry.getDirectoryId();
-        mLookupKey = entry.getLookupKey();
-        mDataId = entry.getDataId();
-        mEntry = entry;
-    }
-
-    @Override
-    public void setSelected(final boolean selected) {
-        mSelected = selected;
-    }
-
-    @Override
-    public boolean isSelected() {
-        return mSelected;
-    }
-
-    @Override
-    public CharSequence getDisplay() {
-        return mDisplay;
-    }
-
-    @Override
-    public CharSequence getValue() {
-        return mValue;
-    }
-
-    @Override
-    public long getContactId() {
-        return mContactId;
-    }
-
-    @Override
-    public Long getDirectoryId() {
-        return mDirectoryId;
-    }
-
-    @Override
-    public String getLookupKey() {
-        return mLookupKey;
-    }
-
-    @Override
-    public long getDataId() {
-        return mDataId;
-    }
-
-    @Override
-    public RecipientEntry getEntry() {
-        return mEntry;
-    }
-
-    @Override
-    public void setOriginalText(final String text) {
-        if (TextUtils.isEmpty(text)) {
-            mOriginalText = text;
-        } else {
-            mOriginalText = text.trim();
-        }
-    }
-
-    @Override
-    public CharSequence getOriginalText() {
-        return !TextUtils.isEmpty(mOriginalText) ? mOriginalText : mEntry.getDestination();
-    }
-
-    @Override
-    public String toString() {
-        return mDisplay + " <" + mValue + ">";
-    }
-}
\ No newline at end of file
diff --git a/chips/src/com/android/ex/chips/recipientchip/VisibleRecipientChip.java b/chips/src/com/android/ex/chips/recipientchip/VisibleRecipientChip.java
deleted file mode 100644
index 6d3d27d..0000000
--- a/chips/src/com/android/ex/chips/recipientchip/VisibleRecipientChip.java
+++ /dev/null
@@ -1,114 +0,0 @@
-/*
- * 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.android.ex.chips.recipientchip;
-
-import android.graphics.Canvas;
-import android.graphics.Rect;
-import android.graphics.drawable.Drawable;
-import android.text.style.DynamicDrawableSpan;
-import android.text.style.ImageSpan;
-
-import com.android.ex.chips.RecipientEntry;
-
-/**
- * VisibleRecipientChip defines an ImageSpan that contains information relevant to a
- * particular recipient and renders a background asset to go with it.
- */
-public class VisibleRecipientChip extends ImageSpan implements DrawableRecipientChip {
-    private final SimpleRecipientChip mDelegate;
-
-    public VisibleRecipientChip(final Drawable drawable, final RecipientEntry entry) {
-        this(drawable, entry, DynamicDrawableSpan.ALIGN_BOTTOM);
-    }
-
-    public VisibleRecipientChip(final Drawable drawable, final RecipientEntry entry,
-            final int verticalAlignment) {
-        super(drawable, verticalAlignment);
-
-        mDelegate = new SimpleRecipientChip(entry);
-    }
-
-    @Override
-    public void setSelected(final boolean selected) {
-        mDelegate.setSelected(selected);
-    }
-
-    @Override
-    public boolean isSelected() {
-        return mDelegate.isSelected();
-    }
-
-    @Override
-    public CharSequence getDisplay() {
-        return mDelegate.getDisplay();
-    }
-
-    @Override
-    public CharSequence getValue() {
-        return mDelegate.getValue();
-    }
-
-    @Override
-    public long getContactId() {
-        return mDelegate.getContactId();
-    }
-
-    @Override
-    public Long getDirectoryId() {
-        return mDelegate.getDirectoryId();
-    }
-
-    @Override
-    public String getLookupKey() {
-        return mDelegate.getLookupKey();
-    }
-
-    @Override
-    public long getDataId() {
-        return mDelegate.getDataId();
-    }
-
-    @Override
-    public RecipientEntry getEntry() {
-        return mDelegate.getEntry();
-    }
-
-    @Override
-    public void setOriginalText(final String text) {
-        mDelegate.setOriginalText(text);
-    }
-
-    @Override
-    public CharSequence getOriginalText() {
-        return mDelegate.getOriginalText();
-    }
-
-    @Override
-    public Rect getBounds() {
-        return getDrawable().getBounds();
-    }
-
-    @Override
-    public void draw(final Canvas canvas) {
-        getDrawable().draw(canvas);
-    }
-
-    @Override
-    public String toString() {
-        return mDelegate.toString();
-    }
-}
diff --git a/chips/tests/AndroidManifest.xml b/chips/tests/AndroidManifest.xml
deleted file mode 100644
index b2b307e..0000000
--- a/chips/tests/AndroidManifest.xml
+++ /dev/null
@@ -1,30 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
-
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
-        package="com.android.ex.chips.tests"
-        android:sharedUserId="com.android.uid.test">
-
-    <application>
-        <uses-library android:name="android.test.runner" />
-    </application>
-
-    <!-- Run tests with "runtest android-common" -->
-    <instrumentation android:name="android.test.InstrumentationTestRunner"
-            android:targetPackage="com.android.ex.chips.tests"
-            android:label="Chips Tests" />
-
-</manifest>
diff --git a/chips/tests/src/com/android/ex/chips/ChipsTest.java b/chips/tests/src/com/android/ex/chips/ChipsTest.java
deleted file mode 100644
index 9116e26..0000000
--- a/chips/tests/src/com/android/ex/chips/ChipsTest.java
+++ /dev/null
@@ -1,1028 +0,0 @@
-/*
- * 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.android.ex.chips;
-
-import android.content.Context;
-import android.graphics.Bitmap;
-import android.graphics.drawable.BitmapDrawable;
-import android.graphics.drawable.Drawable;
-import android.test.AndroidTestCase;
-import android.test.suitebuilder.annotation.SmallTest;
-import android.text.Editable;
-import android.text.SpannableStringBuilder;
-import android.text.style.ImageSpan;
-import android.text.util.Rfc822Tokenizer;
-import android.widget.TextView;
-
-import com.android.ex.chips.BaseRecipientAdapter;
-import com.android.ex.chips.RecipientEditTextView;
-import com.android.ex.chips.RecipientEntry;
-import com.android.ex.chips.recipientchip.DrawableRecipientChip;
-import com.android.ex.chips.recipientchip.VisibleRecipientChip;
-
-import java.util.regex.Pattern;
-
-@SmallTest
-public class ChipsTest extends AndroidTestCase {
-    private DrawableRecipientChip[] mMockRecips;
-
-    private RecipientEntry[] mMockEntries;
-
-    private Rfc822Tokenizer mTokenizer;
-
-    private Editable mEditable;
-
-    class BaseMockRecipientEditTextView extends RecipientEditTextView {
-
-        public BaseMockRecipientEditTextView(Context context) {
-            super(context, null);
-            mTokenizer = new Rfc822Tokenizer();
-            setTokenizer(mTokenizer);
-        }
-
-        @Override
-        public DrawableRecipientChip[] getSortedRecipients() {
-            return mMockRecips;
-        }
-
-        @Override
-        public int getLineHeight() {
-            return 48;
-        }
-
-        @Override
-        Drawable getChipBackground(RecipientEntry contact) {
-            return createChipBackground();
-        }
-
-        @Override
-        public int getViewWidth() {
-            return 100;
-        }
-    }
-
-    class MockRecipientEditTextView extends BaseMockRecipientEditTextView {
-
-        public MockRecipientEditTextView(Context context) {
-            super(context);
-            mTokenizer = new Rfc822Tokenizer();
-            setTokenizer(mTokenizer);
-        }
-
-        @Override
-        public DrawableRecipientChip[] getSortedRecipients() {
-            return mMockRecips;
-        }
-
-        @Override
-        public Editable getText() {
-            return mEditable;
-        }
-
-        @Override
-        public Editable getSpannable() {
-            return mEditable;
-        }
-
-        @Override
-        public int getLineHeight() {
-            return 48;
-        }
-
-        @Override
-        Drawable getChipBackground(RecipientEntry contact) {
-            return createChipBackground();
-        }
-
-        @Override
-        public int length() {
-            return mEditable != null ? mEditable.length() : 0;
-        }
-
-        @Override
-        public String toString() {
-            return mEditable != null ? mEditable.toString() : "";
-        }
-
-        @Override
-        public int getViewWidth() {
-            return 100;
-        }
-    }
-
-    private class TestBaseRecipientAdapter extends BaseRecipientAdapter {
-        public TestBaseRecipientAdapter(final Context context) {
-            super(context);
-        }
-
-        public TestBaseRecipientAdapter(final Context context, final int preferredMaxResultCount,
-                final int queryMode) {
-            super(context, preferredMaxResultCount, queryMode);
-        }
-    }
-
-    private MockRecipientEditTextView createViewForTesting() {
-        mEditable = new SpannableStringBuilder();
-        MockRecipientEditTextView view = new MockRecipientEditTextView(getContext());
-        view.setAdapter(new TestBaseRecipientAdapter(getContext()));
-        return view;
-    }
-
-    public void testCreateDisplayText() {
-        RecipientEditTextView view = createViewForTesting();
-        RecipientEntry entry = RecipientEntry.constructGeneratedEntry("User Name, Jr",
-                "user@username.com", true);
-        String testAddress = view.createAddressText(entry);
-        String testDisplay = view.createChipDisplayText(entry);
-        assertEquals("Expected a properly formatted RFC email address",
-                "\"User Name, Jr\" <user@username.com>, ", testAddress);
-        assertEquals("Expected a displayable name", "User Name, Jr", testDisplay);
-
-        RecipientEntry alreadyFormatted =
-                RecipientEntry.constructFakeEntry("user@username.com, ", true);
-        testAddress = view.createAddressText(alreadyFormatted);
-        testDisplay = view.createChipDisplayText(alreadyFormatted);
-        assertEquals("Expected a properly formatted RFC email address", "<user@username.com>, ",
-                testAddress);
-        assertEquals("Expected a displayable name", "user@username.com", testDisplay);
-
-        RecipientEntry alreadyFormattedNoSpace = RecipientEntry
-                .constructFakeEntry("user@username.com,", true);
-        testAddress = view.createAddressText(alreadyFormattedNoSpace);
-        assertEquals("Expected a properly formatted RFC email address", "<user@username.com>, ",
-                testAddress);
-
-        RecipientEntry alreadyNamed = RecipientEntry.constructGeneratedEntry("User Name",
-                "\"User Name, Jr\" <user@username.com>", true);
-        testAddress = view.createAddressText(alreadyNamed);
-        testDisplay = view.createChipDisplayText(alreadyNamed);
-        assertEquals(
-                "Expected address that used the name not the excess address name",
-                "User Name <user@username.com>, ", testAddress);
-        assertEquals("Expected a displayable name", "User Name", testDisplay);
-    }
-
-    public void testSanitizeBetween() {
-        // First, add 2 chips and then make sure we remove
-        // the extra content between them correctly.
-        populateMocks(2);
-        MockRecipientEditTextView view = createViewForTesting();
-        String first = (String) mTokenizer.terminateToken("FIRST");
-        String second = (String) mTokenizer.terminateToken("SECOND");
-        String extra = "EXTRA";
-        mEditable = new SpannableStringBuilder();
-        mEditable.append(first + extra + second);
-        int firstStart = mEditable.toString().indexOf(first);
-        int firstEnd = firstStart + first.trim().length();
-        int secondStart = mEditable.toString().indexOf(second);
-        int secondEnd = secondStart + second.trim().length();
-        mEditable.setSpan(mMockRecips[mMockRecips.length - 2], firstStart, firstEnd, 0);
-        mEditable.setSpan(mMockRecips[mMockRecips.length - 1], secondStart, secondEnd, 0);
-        view.sanitizeBetween();
-        String editableString = mEditable.toString();
-        assertEquals(editableString.indexOf(extra), -1);
-        assertEquals(editableString.indexOf(first), firstStart);
-        assertEquals(editableString.indexOf(second), secondStart - extra.length());
-        assertEquals(editableString, (first + second));
-
-        // Add 1 chip and make sure that we remove the extra stuff before it correctly.
-        mEditable = new SpannableStringBuilder();
-        populateMocks(1);
-        mEditable.append(extra);
-        mEditable.append(first);
-        firstStart = mEditable.toString().indexOf(first);
-        firstEnd = firstStart + first.length();
-        mEditable.setSpan(mMockRecips[mMockRecips.length - 1], firstStart, firstEnd, 0);
-        view.sanitizeBetween();
-        assertEquals(mEditable.toString(), first);
-        assertEquals(mEditable.getSpanStart(mMockRecips[mMockRecips.length - 1]), firstStart
-                - extra.length());
-    }
-
-    public void testSanitizeEnd() {
-        // First, add 2 chips and then make sure we remove
-        // the extra content between them correctly.
-        populateMocks(2);
-        MockRecipientEditTextView view = createViewForTesting();
-        String first = (String) mTokenizer.terminateToken("FIRST");
-        String second = (String) mTokenizer.terminateToken("SECOND");
-        String extra = "EXTRA";
-        mEditable = new SpannableStringBuilder();
-        mEditable.append(first + second);
-        int firstStart = mEditable.toString().indexOf(first);
-        int firstEnd = firstStart + first.trim().length();
-        int secondStart = mEditable.toString().indexOf(second);
-        int secondEnd = secondStart + second.trim().length();
-        mEditable.setSpan(mMockRecips[mMockRecips.length - 2], firstStart, firstEnd, 0);
-        mEditable.setSpan(mMockRecips[mMockRecips.length - 1], secondStart, secondEnd, 0);
-        view.sanitizeEnd();
-        String editableString = mEditable.toString();
-        assertEquals(editableString.indexOf(extra), -1);
-        assertEquals(editableString.indexOf(first), firstStart);
-        assertEquals(editableString.indexOf(second), secondStart);
-        assertEquals(editableString, (first + second));
-        mEditable.append(extra);
-        editableString = mEditable.toString();
-        assertEquals(mEditable.toString(), (first + second + extra));
-        view.sanitizeEnd();
-        assertEquals(mEditable.toString(), (first + second));
-    }
-
-    public void testMoreChipPlainText() {
-        MockRecipientEditTextView view = createViewForTesting();
-        view.setMoreItem(createTestMoreItem());
-        String first = (String) mTokenizer.terminateToken("FIRST");
-        String second = (String) mTokenizer.terminateToken("SECOND");
-        String third = (String) mTokenizer.terminateToken("THIRD");
-        mEditable = new SpannableStringBuilder();
-        mEditable.append(first+second+third);
-        int thirdStart = mEditable.toString().indexOf(third);
-        int thirdEnd = thirdStart + third.trim().length();
-        view.createMoreChipPlainText();
-        ImageSpan moreChip = view.getMoreChip();
-        assertEquals(mEditable.getSpanStart(moreChip), thirdStart);
-        assertEquals(mEditable.getSpanEnd(moreChip), thirdEnd + 1);
-    }
-
-    public void testCountTokens() {
-        MockRecipientEditTextView view = createViewForTesting();
-        view.setMoreItem(createTestMoreItem());
-        String first = (String) mTokenizer.terminateToken("FIRST");
-        String second = (String) mTokenizer.terminateToken("SECOND");
-        String third = (String) mTokenizer.terminateToken("THIRD");
-        String fourth = "FOURTH,";
-        String fifth = "FIFTH,";
-        mEditable = new SpannableStringBuilder();
-        mEditable.append(first+second+third+fourth+fifth);
-        assertEquals(view.countTokens(mEditable), 5);
-    }
-
-    public void testTooManyRecips() {
-        BaseMockRecipientEditTextView view = new BaseMockRecipientEditTextView(getContext());
-        view.setMoreItem(createTestMoreItem());
-        for (int i = 0; i < 100; i++) {
-            view.append(mTokenizer.terminateToken(i + ""));
-        }
-        assertEquals(view.countTokens(view.getText()), 100);
-        view.handlePendingChips();
-        view.createMoreChip();
-        ImageSpan moreChip = view.getMoreChip();
-        // We show 2 chips then place a more chip.
-        int secondStart = view.getText().toString().indexOf(
-                (String) mTokenizer.terminateToken(RecipientEditTextView.CHIP_LIMIT + ""));
-        assertEquals(view.getText().getSpanStart(moreChip), secondStart);
-        assertEquals(view.getText().getSpanEnd(moreChip), view.length());
-        assertEquals(view.getSortedRecipients(), null);
-    }
-
-    public void testMoreChip() {
-        // Add 3 chips: this is the trigger point at which the more chip will be created.
-        // Test that adding the chips and then creating and removing the more chip, as if
-        // the user were focusing/ removing focus from the chips field.
-        populateMocks(3);
-        MockRecipientEditTextView view = createViewForTesting();
-        view.setMoreItem(createTestMoreItem());
-        String first = (String) mTokenizer.terminateToken("FIRST");
-        String second = (String) mTokenizer.terminateToken("SECOND");
-        String third = (String) mTokenizer.terminateToken("THIRD");
-        mEditable = new SpannableStringBuilder();
-        mEditable.append(first+second+third);
-
-        int firstStart = mEditable.toString().indexOf(first);
-        int firstEnd = firstStart + first.trim().length();
-        int secondStart = mEditable.toString().indexOf(second);
-        int secondEnd = secondStart + second.trim().length();
-        int thirdStart = mEditable.toString().indexOf(third);
-        int thirdEnd = thirdStart + third.trim().length();
-        mEditable.setSpan(mMockRecips[mMockRecips.length - 3], firstStart, firstEnd, 0);
-        mEditable.setSpan(mMockRecips[mMockRecips.length - 2], secondStart, secondEnd, 0);
-        mEditable.setSpan(mMockRecips[mMockRecips.length - 1], thirdStart, thirdEnd, 0);
-
-        view.createMoreChip();
-        assertEquals(mEditable.toString(), first+second+third);
-        assertEquals(mEditable.getSpanStart(mMockRecips[mMockRecips.length - 3]), firstStart);
-        assertEquals(mEditable.getSpanStart(mMockRecips[mMockRecips.length - 2]), secondStart);
-        // Find the more chip.
-        ImageSpan moreChip = view.getMoreChip();
-        assertEquals(mEditable.getSpanStart(moreChip), thirdStart);
-        assertEquals(mEditable.getSpanEnd(moreChip), thirdEnd + 1);
-
-        view.removeMoreChip();
-        assertEquals(mEditable.toString(), first+second+third);
-        assertEquals(mEditable.getSpanStart(mMockRecips[mMockRecips.length - 3]), firstStart);
-        assertEquals(mEditable.getSpanEnd(mMockRecips[mMockRecips.length - 3]), firstEnd);
-        assertEquals(mEditable.getSpanStart(mMockRecips[mMockRecips.length - 2]), secondStart);
-        assertEquals(mEditable.getSpanStart(mMockRecips[mMockRecips.length - 1]), thirdStart);
-        assertEquals(mEditable.getSpanEnd(mMockRecips[mMockRecips.length - 1]), thirdEnd);
-        moreChip = view.getMoreChip();
-        assertEquals(mEditable.getSpanStart(moreChip), -1);
-
-        // Rinse and repeat, just in case!
-        view.createMoreChip();
-        assertEquals(mEditable.toString(), first+second+third);
-        assertEquals(mEditable.getSpanStart(mMockRecips[mMockRecips.length - 3]), firstStart);
-        assertEquals(mEditable.getSpanStart(mMockRecips[mMockRecips.length - 2]), secondStart);
-        // Find the more chip.
-        moreChip = view.getMoreChip();
-        assertEquals(mEditable.getSpanStart(moreChip), thirdStart);
-        assertEquals(mEditable.getSpanEnd(moreChip), thirdEnd + 1);
-
-        view.removeMoreChip();
-        assertEquals(mEditable.toString(), first+second+third);
-        assertEquals(mEditable.getSpanStart(mMockRecips[mMockRecips.length - 3]), firstStart);
-        assertEquals(mEditable.getSpanEnd(mMockRecips[mMockRecips.length - 3]), firstEnd);
-        assertEquals(mEditable.getSpanStart(mMockRecips[mMockRecips.length - 2]), secondStart);
-        assertEquals(mEditable.getSpanStart(mMockRecips[mMockRecips.length - 1]), thirdStart);
-        assertEquals(mEditable.getSpanEnd(mMockRecips[mMockRecips.length - 1]), thirdEnd);
-        moreChip = view.getMoreChip();
-        assertEquals(mEditable.getSpanStart(moreChip), -1);
-    }
-
-    public void testMoreChipLotsOfUsers() {
-        // Test adding and removing the more chip in the case where we have a lot of users.
-        populateMocks(10);
-        MockRecipientEditTextView view = createViewForTesting();
-        view.setMoreItem(createTestMoreItem());
-        String first = (String) mTokenizer.terminateToken("FIRST");
-        String second = (String) mTokenizer.terminateToken("SECOND");
-        String third = (String) mTokenizer.terminateToken("THIRD");
-        String fourth = (String) mTokenizer.terminateToken("FOURTH");
-        String fifth = (String) mTokenizer.terminateToken("FIFTH");
-        String sixth = (String) mTokenizer.terminateToken("SIXTH");
-        String seventh = (String) mTokenizer.terminateToken("SEVENTH");
-        String eigth = (String) mTokenizer.terminateToken("EIGHTH");
-        String ninth = (String) mTokenizer.terminateToken("NINTH");
-        String tenth = (String) mTokenizer.terminateToken("TENTH");
-        mEditable = new SpannableStringBuilder();
-        mEditable.append(first+second+third+fourth+fifth+sixth+seventh+eigth+ninth+tenth);
-
-        int firstStart = mEditable.toString().indexOf(first);
-        int firstEnd = firstStart + first.trim().length();
-        int secondStart = mEditable.toString().indexOf(second);
-        int secondEnd = secondStart + second.trim().length();
-        int thirdStart = mEditable.toString().indexOf(third);
-        int thirdEnd = thirdStart + third.trim().length();
-        int fourthStart = mEditable.toString().indexOf(fourth);
-        int fourthEnd = fourthStart + fourth.trim().length();
-        int fifthStart = mEditable.toString().indexOf(fifth);
-        int fifthEnd = fifthStart + fifth.trim().length();
-        int sixthStart = mEditable.toString().indexOf(sixth);
-        int sixthEnd = sixthStart + sixth.trim().length();
-        int seventhStart = mEditable.toString().indexOf(seventh);
-        int seventhEnd = seventhStart + seventh.trim().length();
-        int eighthStart = mEditable.toString().indexOf(eigth);
-        int eighthEnd = eighthStart + eigth.trim().length();
-        int ninthStart = mEditable.toString().indexOf(ninth);
-        int ninthEnd = ninthStart + ninth.trim().length();
-        int tenthStart = mEditable.toString().indexOf(tenth);
-        int tenthEnd = tenthStart + tenth.trim().length();
-        mEditable.setSpan(mMockRecips[mMockRecips.length - 10], firstStart, firstEnd, 0);
-        mEditable.setSpan(mMockRecips[mMockRecips.length - 9], secondStart, secondEnd, 0);
-        mEditable.setSpan(mMockRecips[mMockRecips.length - 8], thirdStart, thirdEnd, 0);
-        mEditable.setSpan(mMockRecips[mMockRecips.length - 7], fourthStart, fourthEnd, 0);
-        mEditable.setSpan(mMockRecips[mMockRecips.length - 6], fifthStart, fifthEnd, 0);
-        mEditable.setSpan(mMockRecips[mMockRecips.length - 5], sixthStart, sixthEnd, 0);
-        mEditable.setSpan(mMockRecips[mMockRecips.length - 4], seventhStart, seventhEnd, 0);
-        mEditable.setSpan(mMockRecips[mMockRecips.length - 3], eighthStart, eighthEnd, 0);
-        mEditable.setSpan(mMockRecips[mMockRecips.length - 2], ninthStart, ninthEnd, 0);
-        mEditable.setSpan(mMockRecips[mMockRecips.length - 1], tenthStart, tenthEnd, 0);
-
-        view.createMoreChip();
-        assertEquals(mEditable.toString(), first + second + third + fourth + fifth + sixth
-                + seventh + eigth + ninth + tenth);
-        assertEquals(mEditable.getSpanStart(mMockRecips[mMockRecips.length - 10]), firstStart);
-        assertEquals(mEditable.getSpanStart(mMockRecips[mMockRecips.length - 9]), secondStart);
-        // Find the more chip.
-        ImageSpan moreChip = view.getMoreChip();
-        assertEquals(mEditable.getSpanStart(moreChip), thirdStart);
-        assertEquals(mEditable.getSpanEnd(moreChip), tenthEnd + 1);
-
-        view.removeMoreChip();
-        assertEquals(mEditable.toString(), first + second + third + fourth + fifth + sixth
-                + seventh + eigth + ninth + tenth);
-        assertEquals(mEditable.getSpanStart(mMockRecips[mMockRecips.length - 10]), firstStart);
-        assertEquals(mEditable.getSpanStart(mMockRecips[mMockRecips.length - 9]), secondStart);
-
-        assertEquals(mEditable.getSpanStart(mMockRecips[mMockRecips.length - 8]), thirdStart);
-        assertEquals(mEditable.getSpanStart(mMockRecips[mMockRecips.length - 7]), fourthStart);
-        assertEquals(mEditable.getSpanStart(mMockRecips[mMockRecips.length - 6]), fifthStart);
-        assertEquals(mEditable.getSpanStart(mMockRecips[mMockRecips.length - 5]), sixthStart);
-        assertEquals(mEditable.getSpanStart(mMockRecips[mMockRecips.length - 4]), seventhStart);
-        assertEquals(mEditable.getSpanStart(mMockRecips[mMockRecips.length - 3]), eighthStart);
-        assertEquals(mEditable.getSpanStart(mMockRecips[mMockRecips.length - 2]), ninthStart);
-        assertEquals(mEditable.getSpanStart(mMockRecips[mMockRecips.length - 1]), tenthStart);
-        moreChip = view.getMoreChip();
-        assertEquals(mEditable.getSpanStart(moreChip), -1);
-
-    }
-
-    public void testMoreChipSpecialChars() {
-        // Make sure the more chip correctly handles extra tokenizer characters in the middle
-        // of chip text.
-        populateMocks(3);
-        MockRecipientEditTextView view = createViewForTesting();
-        view.setMoreItem(createTestMoreItem());
-        String first = (String) mTokenizer.terminateToken("FI,RST");
-        String second = (String) mTokenizer.terminateToken("SE,COND");
-        String third = (String) mTokenizer.terminateToken("THI,RD");
-        mEditable = new SpannableStringBuilder();
-        mEditable.append(first+second+third);
-
-        int firstStart = mEditable.toString().indexOf(first);
-        int firstEnd = firstStart + first.trim().length();
-        int secondStart = mEditable.toString().indexOf(second);
-        int secondEnd = secondStart + second.trim().length();
-        int thirdStart = mEditable.toString().indexOf(third);
-        int thirdEnd = thirdStart + third.trim().length();
-        mEditable.setSpan(mMockRecips[mMockRecips.length - 3], firstStart, firstEnd, 0);
-        mEditable.setSpan(mMockRecips[mMockRecips.length - 2], secondStart, secondEnd, 0);
-        mEditable.setSpan(mMockRecips[mMockRecips.length - 1], thirdStart, thirdEnd, 0);
-
-        view.createMoreChip();
-        assertEquals(mEditable.toString(), first+second+third);
-        assertEquals(mEditable.getSpanStart(mMockRecips[mMockRecips.length - 3]), firstStart);
-        assertEquals(mEditable.getSpanStart(mMockRecips[mMockRecips.length - 2]), secondStart);
-        // Find the more chip.
-        ImageSpan moreChip = view.getMoreChip();
-        assertEquals(mEditable.getSpanStart(moreChip), thirdStart);
-        assertEquals(mEditable.getSpanEnd(moreChip), thirdEnd + 1);
-
-        view.removeMoreChip();
-        assertEquals(mEditable.toString(), first+second+third);
-        assertEquals(mEditable.getSpanStart(mMockRecips[mMockRecips.length - 3]), firstStart);
-        assertEquals(mEditable.getSpanEnd(mMockRecips[mMockRecips.length - 3]), firstEnd);
-        assertEquals(mEditable.getSpanStart(mMockRecips[mMockRecips.length - 2]), secondStart);
-        assertEquals(mEditable.getSpanStart(mMockRecips[mMockRecips.length - 1]), thirdStart);
-        assertEquals(mEditable.getSpanEnd(mMockRecips[mMockRecips.length - 1]), thirdEnd);
-        moreChip = view.getMoreChip();
-        assertEquals(mEditable.getSpanStart(moreChip), -1);
-    }
-
-    public void testMoreChipDupes() {
-        // Make sure the more chip is correctly added and removed when we have duplicate chips.
-        populateMocks(4);
-        MockRecipientEditTextView view = createViewForTesting();
-        view.setMoreItem(createTestMoreItem());
-        String first = (String) mTokenizer.terminateToken("FIRST");
-        String second = (String) mTokenizer.terminateToken("SECOND");
-        String third = (String) mTokenizer.terminateToken("THIRD");
-        mEditable = new SpannableStringBuilder();
-        mEditable.append(first+second+third+third);
-
-        int firstStart = mEditable.toString().indexOf(first);
-        int firstEnd = firstStart + first.trim().length();
-        int secondStart = mEditable.toString().indexOf(second);
-        int secondEnd = secondStart + second.trim().length();
-        int thirdStart = mEditable.toString().indexOf(third);
-        int thirdEnd = thirdStart + third.trim().length();
-        int thirdNextStart = mEditable.toString().indexOf(third, thirdEnd);
-        int thirdNextEnd = thirdNextStart + third.trim().length();
-        mEditable.setSpan(mMockRecips[mMockRecips.length - 4], firstStart, firstEnd, 0);
-        mEditable.setSpan(mMockRecips[mMockRecips.length - 3], secondStart, secondEnd, 0);
-        mEditable.setSpan(mMockRecips[mMockRecips.length - 2], thirdStart, thirdEnd, 0);
-        mEditable.setSpan(mMockRecips[mMockRecips.length - 1], thirdNextStart, thirdNextEnd, 0);
-
-        view.createMoreChip();
-        assertEquals(mEditable.toString(), first+second+third+third);
-        assertEquals(mEditable.getSpanStart(mMockRecips[mMockRecips.length - 4]), firstStart);
-        assertEquals(mEditable.getSpanStart(mMockRecips[mMockRecips.length - 3]), secondStart);
-        // Find the more chip.
-        ImageSpan moreChip = view.getMoreChip();
-        assertEquals(mEditable.getSpanStart(moreChip), thirdStart);
-        assertEquals(mEditable.getSpanEnd(moreChip), thirdNextEnd + 1);
-
-        view.removeMoreChip();
-        assertEquals(mEditable.toString(), first+second+third+third);
-        assertEquals(mEditable.getSpanStart(mMockRecips[mMockRecips.length - 4]), firstStart);
-        assertEquals(mEditable.getSpanEnd(mMockRecips[mMockRecips.length - 4]), firstEnd);
-        assertEquals(mEditable.getSpanStart(mMockRecips[mMockRecips.length - 3]), secondStart);
-        assertEquals(mEditable.getSpanStart(mMockRecips[mMockRecips.length - 2]), thirdStart);
-        assertEquals(mEditable.getSpanEnd(mMockRecips[mMockRecips.length - 2]), thirdEnd);
-        assertEquals(mEditable.getSpanStart(mMockRecips[mMockRecips.length - 1]), thirdNextStart);
-        assertEquals(mEditable.getSpanEnd(mMockRecips[mMockRecips.length - 1]), thirdNextEnd);
-        moreChip = view.getMoreChip();
-        assertEquals(mEditable.getSpanStart(moreChip), -1);
-    }
-
-    public void testRemoveChip() {
-        // Create 3 chips to start and test removing chips in various postions.
-        populateMocks(3);
-        MockRecipientEditTextView view = createViewForTesting();
-        view.setMoreItem(createTestMoreItem());
-        String first = (String) mTokenizer.terminateToken("FIRST");
-        String second = (String) mTokenizer.terminateToken("SECOND");
-        String third = (String) mTokenizer.terminateToken("THIRD");
-        mEditable = new SpannableStringBuilder();
-        mEditable.append(first + second + third);
-
-        int firstStart = mEditable.toString().indexOf(first);
-        int firstEnd = firstStart + first.length();
-        int secondStart = mEditable.toString().indexOf(second);
-        int secondEnd = secondStart + second.length();
-        int thirdStart = mEditable.toString().indexOf(third);
-        int thirdEnd = thirdStart + third.length();
-        mEditable.setSpan(mMockRecips[mMockRecips.length - 3], firstStart, firstEnd, 0);
-        mEditable.setSpan(mMockRecips[mMockRecips.length - 2], secondStart, secondEnd, 0);
-        mEditable.setSpan(mMockRecips[mMockRecips.length - 1], thirdStart, thirdEnd, 0);
-        assertEquals(mEditable.toString(), first + second + third);
-        // Test removing the middle chip.
-        view.removeChip(mMockRecips[mMockRecips.length - 2]);
-        assertEquals(mEditable.toString(), first + third);
-        assertEquals(mEditable.getSpanStart(mMockRecips[mMockRecips.length - 3]), firstStart);
-        assertEquals(mEditable.getSpanEnd(mMockRecips[mMockRecips.length - 3]), firstEnd);
-        assertEquals(mEditable.getSpanStart(mMockRecips[mMockRecips.length - 2]), -1);
-        assertEquals(mEditable.getSpanEnd(mMockRecips[mMockRecips.length - 2]), -1);
-        int newThirdStart = mEditable.toString().indexOf(third);
-        assertEquals(mEditable.getSpanStart(mMockRecips[mMockRecips.length - 1]), newThirdStart);
-        assertEquals(mEditable.getSpanEnd(mMockRecips[mMockRecips.length - 1]), newThirdStart
-                + third.length());
-
-        // Test removing the first chip.
-        populateMocks(3);
-        view = createViewForTesting();
-        view.setMoreItem(createTestMoreItem());
-        mEditable = new SpannableStringBuilder();
-        mEditable.append(first + second + third);
-
-        firstStart = mEditable.toString().indexOf(first);
-        firstEnd = firstStart + first.length();
-        secondStart = mEditable.toString().indexOf(second);
-        secondEnd = secondStart + second.length();
-        thirdStart = mEditable.toString().indexOf(third);
-        thirdEnd = thirdStart + third.length();
-        mEditable.setSpan(mMockRecips[mMockRecips.length - 3], firstStart, firstEnd, 0);
-        mEditable.setSpan(mMockRecips[mMockRecips.length - 2], secondStart, secondEnd, 0);
-        mEditable.setSpan(mMockRecips[mMockRecips.length - 1], thirdStart, thirdEnd, 0);
-        assertEquals(mEditable.toString(), first + second + third);
-        view.removeChip(mMockRecips[mMockRecips.length - 3]);
-        assertEquals(mEditable.toString(), second + third);
-        assertEquals(mEditable.getSpanStart(mMockRecips[mMockRecips.length - 3]), -1);
-        assertEquals(mEditable.getSpanEnd(mMockRecips[mMockRecips.length - 3]), -1);
-        assertEquals(mEditable.getSpanStart(mMockRecips[mMockRecips.length - 2]), 0);
-        assertEquals(mEditable.getSpanEnd(mMockRecips[mMockRecips.length - 2]), second.length());
-        newThirdStart = mEditable.toString().indexOf(third);
-        assertEquals(mEditable.getSpanStart(mMockRecips[mMockRecips.length - 1]), newThirdStart);
-        assertEquals(mEditable.getSpanEnd(mMockRecips[mMockRecips.length - 1]), newThirdStart
-                + third.length());
-
-        // Test removing the last chip.
-        populateMocks(3);
-        view = createViewForTesting();
-        view.setMoreItem(createTestMoreItem());
-        mEditable = new SpannableStringBuilder();
-        mEditable.append(first + second + third);
-
-        firstStart = mEditable.toString().indexOf(first);
-        firstEnd = firstStart + first.length();
-        secondStart = mEditable.toString().indexOf(second);
-        secondEnd = secondStart + second.length();
-        thirdStart = mEditable.toString().indexOf(third);
-        thirdEnd = thirdStart + third.length();
-        mEditable.setSpan(mMockRecips[mMockRecips.length - 3], firstStart, firstEnd, 0);
-        mEditable.setSpan(mMockRecips[mMockRecips.length - 2], secondStart, secondEnd, 0);
-        mEditable.setSpan(mMockRecips[mMockRecips.length - 1], thirdStart, thirdEnd, 0);
-        assertEquals(mEditable.toString(), first + second + third);
-        view.removeChip(mMockRecips[mMockRecips.length - 1]);
-        assertEquals(mEditable.toString(), first + second);
-        assertEquals(mEditable.getSpanStart(mMockRecips[mMockRecips.length - 3]), firstStart);
-        assertEquals(mEditable.getSpanEnd(mMockRecips[mMockRecips.length - 3]), firstEnd);
-        assertEquals(mEditable.getSpanStart(mMockRecips[mMockRecips.length - 2]), secondStart);
-        assertEquals(mEditable.getSpanEnd(mMockRecips[mMockRecips.length - 2]), secondEnd);
-        assertEquals(mEditable.getSpanStart(mMockRecips[mMockRecips.length - 1]), -1);
-        assertEquals(mEditable.getSpanEnd(mMockRecips[mMockRecips.length - 1]), -1);
-    }
-
-    public void testReplaceChip() {
-        populateMocks(3);
-        MockRecipientEditTextView view = createViewForTesting();
-        view.setMoreItem(createTestMoreItem());
-        view.setChipBackground(createChipBackground());
-        view.setChipHeight(48);
-        String first = (String) mTokenizer.terminateToken("FIRST");
-        String second = (String) mTokenizer.terminateToken("SECOND");
-        String third = (String) mTokenizer.terminateToken("THIRD");
-        mEditable = new SpannableStringBuilder();
-        mEditable.append(first + second + third);
-
-        // Test replacing the first chip with a new chip.
-        int firstStart = mEditable.toString().indexOf(first);
-        int firstEnd = firstStart + first.trim().length();
-        int secondStart = mEditable.toString().indexOf(second);
-        int secondEnd = secondStart + second.trim().length();
-        int thirdStart = mEditable.toString().indexOf(third);
-        int thirdEnd = thirdStart + third.trim().length();
-        mEditable.setSpan(mMockRecips[mMockRecips.length - 3], firstStart, firstEnd, 0);
-        mEditable.setSpan(mMockRecips[mMockRecips.length - 2], secondStart, secondEnd, 0);
-        mEditable.setSpan(mMockRecips[mMockRecips.length - 1], thirdStart, thirdEnd, 0);
-        assertEquals(mEditable.toString(), first + second + third);
-        view.replaceChip(mMockRecips[mMockRecips.length - 3], RecipientEntry
-                .constructGeneratedEntry("replacement", "replacement@replacement.com", true));
-        assertEquals(mEditable.toString(), mTokenizer
-                .terminateToken("replacement <replacement@replacement.com>")
-                + second + third);
-        assertEquals(mEditable.getSpanStart(mMockRecips[mMockRecips.length - 3]), -1);
-        assertEquals(mEditable.getSpanEnd(mMockRecips[mMockRecips.length - 3]), -1);
-        assertEquals(mEditable.getSpanStart(mMockRecips[mMockRecips.length - 2]), mEditable
-                .toString().indexOf(second));
-        assertEquals(mEditable.getSpanEnd(mMockRecips[mMockRecips.length - 2]), mEditable
-                .toString().indexOf(second)
-                + second.trim().length());
-        assertEquals(mEditable.getSpanStart(mMockRecips[mMockRecips.length - 1]), mEditable
-                .toString().indexOf(third));
-        assertEquals(mEditable.getSpanEnd(mMockRecips[mMockRecips.length - 1]), mEditable
-                .toString().indexOf(third)
-                + third.trim().length());
-        DrawableRecipientChip[] spans =
-                mEditable.getSpans(0, mEditable.length(), DrawableRecipientChip.class);
-        assertEquals(spans.length, 3);
-        spans = mEditable
-                .getSpans(0, mEditable.toString().indexOf(second) - 1, DrawableRecipientChip.class);
-        assertEquals((String) spans[0].getDisplay(), "replacement");
-
-
-        // Test replacing the middle chip with a new chip.
-        mEditable = new SpannableStringBuilder();
-        mEditable.append(first + second + third);
-        firstStart = mEditable.toString().indexOf(first);
-        firstEnd = firstStart + first.trim().length();
-        secondStart = mEditable.toString().indexOf(second);
-        secondEnd = secondStart + second.trim().length();
-        thirdStart = mEditable.toString().indexOf(third);
-        thirdEnd = thirdStart + third.trim().length();
-        mEditable.setSpan(mMockRecips[mMockRecips.length - 3], firstStart, firstEnd, 0);
-        mEditable.setSpan(mMockRecips[mMockRecips.length - 2], secondStart, secondEnd, 0);
-        mEditable.setSpan(mMockRecips[mMockRecips.length - 1], thirdStart, thirdEnd, 0);
-        assertEquals(mEditable.toString(), first + second + third);
-        view.replaceChip(mMockRecips[mMockRecips.length - 2], RecipientEntry
-                .constructGeneratedEntry("replacement", "replacement@replacement.com", true));
-        assertEquals(mEditable.toString(), first + mTokenizer
-                .terminateToken("replacement <replacement@replacement.com>") + third);
-        assertEquals(mEditable.getSpanStart(mMockRecips[mMockRecips.length - 3]), firstStart);
-        assertEquals(mEditable.getSpanEnd(mMockRecips[mMockRecips.length - 3]), firstEnd);
-        assertEquals(mEditable.getSpanStart(mMockRecips[mMockRecips.length - 2]), -1);
-        assertEquals(mEditable.getSpanEnd(mMockRecips[mMockRecips.length - 2]), -1);
-        assertEquals(mEditable.getSpanStart(mMockRecips[mMockRecips.length - 1]), mEditable
-                .toString().indexOf(third));
-        assertEquals(mEditable.getSpanEnd(mMockRecips[mMockRecips.length - 1]), mEditable
-                .toString().indexOf(third)
-                + third.trim().length());
-        spans = mEditable.getSpans(0, mEditable.length(), DrawableRecipientChip.class);
-        assertEquals(spans.length, 3);
-        spans = mEditable.getSpans(firstEnd, mEditable.toString().indexOf(third) - 1,
-                DrawableRecipientChip.class);
-        assertEquals((String) spans[0].getDisplay(), "replacement");
-
-
-        // Test replacing the last chip with a new chip.
-        mEditable = new SpannableStringBuilder();
-        mEditable.append(first + second + third);
-        firstStart = mEditable.toString().indexOf(first);
-        firstEnd = firstStart + first.trim().length();
-        secondStart = mEditable.toString().indexOf(second);
-        secondEnd = secondStart + second.trim().length();
-        thirdStart = mEditable.toString().indexOf(third);
-        thirdEnd = thirdStart + third.trim().length();
-        mEditable.setSpan(mMockRecips[mMockRecips.length - 3], firstStart, firstEnd, 0);
-        mEditable.setSpan(mMockRecips[mMockRecips.length - 2], secondStart, secondEnd, 0);
-        mEditable.setSpan(mMockRecips[mMockRecips.length - 1], thirdStart, thirdEnd, 0);
-        assertEquals(mEditable.toString(), first + second + third);
-        view.replaceChip(mMockRecips[mMockRecips.length - 1], RecipientEntry
-                .constructGeneratedEntry("replacement", "replacement@replacement.com", true));
-        assertEquals(mEditable.toString(), first + second + mTokenizer
-                .terminateToken("replacement <replacement@replacement.com>"));
-        assertEquals(mEditable.getSpanStart(mMockRecips[mMockRecips.length - 3]), firstStart);
-        assertEquals(mEditable.getSpanEnd(mMockRecips[mMockRecips.length - 3]), firstEnd);
-        assertEquals(mEditable.getSpanStart(mMockRecips[mMockRecips.length - 2]), secondStart);
-        assertEquals(mEditable.getSpanEnd(mMockRecips[mMockRecips.length - 2]), secondEnd);
-        assertEquals(mEditable.getSpanStart(mMockRecips[mMockRecips.length - 1]), -1);
-        assertEquals(mEditable.getSpanEnd(mMockRecips[mMockRecips.length - 1]), -1);
-        spans = mEditable.getSpans(0, mEditable.length(), DrawableRecipientChip.class);
-        assertEquals(spans.length, 3);
-        spans = mEditable
-                .getSpans(secondEnd, mEditable.length(), DrawableRecipientChip.class);
-        assertEquals((String) spans[0].getDisplay(), "replacement");
-    }
-
-    public void testHandlePaste() {
-        // Start with an empty edit field.
-        // Add an address; the text should be left as is.
-        MockRecipientEditTextView view = createViewForTesting();
-        view.setMoreItem(createTestMoreItem());
-        view.setChipBackground(createChipBackground());
-        view.setChipHeight(48);
-        mEditable = new SpannableStringBuilder();
-        mEditable.append("user@user.com");
-        view.setSelection(mEditable.length());
-        view.handlePaste();
-        assertEquals(mEditable.getSpans(0, mEditable.length(), DrawableRecipientChip.class).length, 0);
-        assertEquals(mEditable.toString(), "user@user.com");
-
-        // Test adding a single address to an empty chips field with a space at
-        // the end of it. The address should stay as text.
-        mEditable = new SpannableStringBuilder();
-        String tokenizedUser = "user@user.com" + " ";
-        mEditable.append(tokenizedUser);
-        view.setSelection(mEditable.length());
-        view.handlePaste();
-        assertEquals(mEditable.getSpans(0, mEditable.length(), DrawableRecipientChip.class).length, 0);
-        assertEquals(mEditable.toString(), tokenizedUser);
-
-        // Test adding a single address to an empty chips field with a semicolon at
-        // the end of it. The address should become a chip
-        mEditable = new SpannableStringBuilder();
-        tokenizedUser = "user@user.com;";
-        mEditable.append(tokenizedUser);
-        view.setSelection(mEditable.length());
-        view.handlePaste();
-        assertEquals(mEditable.getSpans(0, mEditable.length(), DrawableRecipientChip.class).length, 1);
-
-        // Test adding 2 address to an empty chips field. The second to last
-        // address should become a chip and the last address should stay as
-        // text.
-        mEditable = new SpannableStringBuilder();
-        mEditable.append("user1,user2@user.com");
-        view.setSelection(mEditable.length());
-        view.handlePaste();
-        assertEquals(mEditable.getSpans(0, mEditable.length(), DrawableRecipientChip.class).length, 1);
-        assertEquals(mEditable.getSpans(0, mEditable.toString().indexOf("user2@user.com"),
-                DrawableRecipientChip.class).length, 1);
-        assertEquals(mEditable.toString(), "<user1>, user2@user.com");
-
-        // Test adding a single address to the end of existing chips. The existing
-        // chips should remain, and the last address should stay as text.
-        populateMocks(3);
-        String first = (String) mTokenizer.terminateToken("FIRST");
-        String second = (String) mTokenizer.terminateToken("SECOND");
-        String third = (String) mTokenizer.terminateToken("THIRD");
-        mEditable = new SpannableStringBuilder();
-        mEditable.append(first + second + third);
-        view.setSelection(mEditable.length());
-        int firstStart = mEditable.toString().indexOf(first);
-        int firstEnd = firstStart + first.trim().length();
-        int secondStart = mEditable.toString().indexOf(second);
-        int secondEnd = secondStart + second.trim().length();
-        int thirdStart = mEditable.toString().indexOf(third);
-        int thirdEnd = thirdStart + third.trim().length();
-        mEditable.setSpan(mMockRecips[mMockRecips.length - 3], firstStart, firstEnd, 0);
-        mEditable.setSpan(mMockRecips[mMockRecips.length - 2], secondStart, secondEnd, 0);
-        mEditable.setSpan(mMockRecips[mMockRecips.length - 1], thirdStart, thirdEnd, 0);
-
-        mEditable.append("user@user.com");
-        view.setSelection(mEditable.length());
-        view.handlePaste();
-        assertEquals(mEditable.getSpans(0, mEditable.length(), DrawableRecipientChip.class).length,
-                mMockRecips.length);
-        assertEquals(mEditable.toString(), first + second + third + "user@user.com");
-
-        // Paste 2 addresses after existing chips. We expect the first address to be turned into
-        // a chip and the second to be left as text.
-        populateMocks(3);
-        mEditable = new SpannableStringBuilder();
-        mEditable.append(first + second + third);
-
-        mEditable.setSpan(mMockRecips[mMockRecips.length - 3], firstStart, firstEnd, 0);
-        mEditable.setSpan(mMockRecips[mMockRecips.length - 2], secondStart, secondEnd, 0);
-        mEditable.setSpan(mMockRecips[mMockRecips.length - 1], thirdStart, thirdEnd, 0);
-
-        mEditable.append("user1, user2@user.com");
-        view.setSelection(mEditable.length());
-        view.handlePaste();
-        assertEquals(mEditable.getSpans(0, mEditable.length(), DrawableRecipientChip.class).length,
-                mMockRecips.length + 1);
-        assertEquals(mEditable.getSpans(mEditable.toString().indexOf("<user1>"), mEditable
-                .toString().indexOf("user2@user.com") - 1, DrawableRecipientChip.class).length, 1);
-        assertEquals(mEditable.getSpans(mEditable.toString().indexOf("user2@user.com"), mEditable
-                .length(), DrawableRecipientChip.class).length, 0);
-        assertEquals(mEditable.toString(), first + second + third + "<user1>, user2@user.com");
-
-        // Paste 2 addresses after existing chips. We expect the first address to be turned into
-        // a chip and the second to be left as text. This removes the space seperator char between
-        // addresses.
-        populateMocks(3);
-        mEditable = new SpannableStringBuilder();
-        mEditable.append(first + second + third);
-
-        mEditable.setSpan(mMockRecips[mMockRecips.length - 3], firstStart, firstEnd, 0);
-        mEditable.setSpan(mMockRecips[mMockRecips.length - 2], secondStart, secondEnd, 0);
-        mEditable.setSpan(mMockRecips[mMockRecips.length - 1], thirdStart, thirdEnd, 0);
-
-        mEditable.append("user1,user2@user.com");
-        view.setSelection(mEditable.length());
-        view.handlePaste();
-        assertEquals(mEditable.getSpans(0, mEditable.length(), DrawableRecipientChip.class).length,
-                mMockRecips.length + 1);
-        assertEquals(mEditable.getSpans(mEditable.toString().indexOf("<user1>"), mEditable
-                .toString().indexOf("user2@user.com") - 1, DrawableRecipientChip.class).length, 1);
-        assertEquals(mEditable.getSpans(mEditable.toString().indexOf("user2@user.com"), mEditable
-                .length(), DrawableRecipientChip.class).length, 0);
-        assertEquals(mEditable.toString(), first + second + third + "<user1>, user2@user.com");
-
-        // Test a complete token pasted in at the end. It should be turned into a chip.
-        mEditable = new SpannableStringBuilder();
-        mEditable.append("user1, user2@user.com,");
-        view.setSelection(mEditable.length());
-        view.handlePaste();
-        assertEquals(mEditable.getSpans(0, mEditable.length(), DrawableRecipientChip.class).length, 2);
-        assertEquals(mEditable.getSpans(mEditable.toString().indexOf("<user1>"), mEditable
-                .toString().indexOf("user2@user.com") - 1, DrawableRecipientChip.class).length, 1);
-        assertEquals(mEditable.getSpans(mEditable.toString().indexOf("user2@user.com"), mEditable
-                .length(), DrawableRecipientChip.class).length, 1);
-        assertEquals(mEditable.toString(), "<user1>, <user2@user.com>, ");
-    }
-
-    public void testGetPastTerminators() {
-        MockRecipientEditTextView view = createViewForTesting();
-        view.setMoreItem(createTestMoreItem());
-        view.setChipBackground(createChipBackground());
-        view.setChipHeight(48);
-        String test = "test";
-        mEditable = new SpannableStringBuilder();
-        mEditable.append(test);
-        assertEquals(view.movePastTerminators(mTokenizer.findTokenEnd(mEditable.toString(), 0)),
-                test.length());
-
-        test = "test,";
-        mEditable = new SpannableStringBuilder();
-        mEditable.append(test);
-        assertEquals(view.movePastTerminators(mTokenizer.findTokenEnd(mEditable.toString(), 0)),
-                test.length());
-
-        test = "test, ";
-        mEditable = new SpannableStringBuilder();
-        mEditable.append(test);
-        assertEquals(view.movePastTerminators(mTokenizer.findTokenEnd(mEditable.toString(), 0)),
-                test.length());
-
-        test = "test;";
-        mEditable = new SpannableStringBuilder();
-        mEditable.append(test);
-        assertEquals(view.movePastTerminators(mTokenizer.findTokenEnd(mEditable.toString(), 0)),
-                test.length());
-
-        test = "test; ";
-        mEditable = new SpannableStringBuilder();
-        mEditable.append(test);
-        assertEquals(view.movePastTerminators(mTokenizer.findTokenEnd(mEditable.toString(), 0)),
-                test.length());
-    }
-
-    public void testIsCompletedToken() {
-        MockRecipientEditTextView view = createViewForTesting();
-        view.setMoreItem(createTestMoreItem());
-        view.setChipBackground(createChipBackground());
-        view.setChipHeight(48);
-        assertTrue(view.isCompletedToken("test;"));
-        assertTrue(view.isCompletedToken("test,"));
-        assertFalse(view.isCompletedToken("test"));
-        assertFalse(view.isCompletedToken("test "));
-    }
-
-    public void testGetLastChip() {
-        populateMocks(3);
-        MockRecipientEditTextView view = createViewForTesting();
-        view.setMoreItem(createTestMoreItem());
-        view.setChipBackground(createChipBackground());
-        view.setChipHeight(48);
-        String first = (String) mTokenizer.terminateToken("FIRST");
-        String second = (String) mTokenizer.terminateToken("SECOND");
-        String third = (String) mTokenizer.terminateToken("THIRD");
-        mEditable = new SpannableStringBuilder();
-        mEditable.append(first + second + third);
-
-        // Test replacing the first chip with a new chip.
-        int firstStart = mEditable.toString().indexOf(first);
-        int firstEnd = firstStart + first.trim().length();
-        int secondStart = mEditable.toString().indexOf(second);
-        int secondEnd = secondStart + second.trim().length();
-        int thirdStart = mEditable.toString().indexOf(third);
-        int thirdEnd = thirdStart + third.trim().length();
-        mEditable.setSpan(mMockRecips[mMockRecips.length - 3], firstStart, firstEnd, 0);
-        mEditable.setSpan(mMockRecips[mMockRecips.length - 2], secondStart, secondEnd, 0);
-        mEditable.setSpan(mMockRecips[mMockRecips.length - 1], thirdStart, thirdEnd, 0);
-        assertEquals(view.getLastChip(), mMockRecips[mMockRecips.length - 1]);
-        mEditable.append("extra");
-        assertEquals(view.getLastChip(), mMockRecips[mMockRecips.length - 1]);
-    }
-
-    private Drawable createChipBackground() {
-        Bitmap drawable = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888);
-        return new BitmapDrawable(getContext().getResources(), drawable);
-    }
-
-    private TextView createTestMoreItem() {
-        TextView view = new TextView(getContext());
-        view.setText("<xliff:g id='count'>%1$s</xliff:g> more...");
-        return view;
-    }
-
-    private void populateMocks(int size) {
-        mMockEntries = new RecipientEntry[size];
-        for (int i = 0; i < size; i++) {
-            mMockEntries[i] = RecipientEntry.constructGeneratedEntry("user",
-                    "user@username.com", true);
-        }
-        mMockRecips = new DrawableRecipientChip[size];
-        for (int i = 0; i < size; i++) {
-            mMockRecips[i] = new VisibleRecipientChip(null, mMockEntries[i]);
-        }
-    }
-
-    /**
-     * <p>
-     * Ensure the original text is always accurate, regardless of the type of email. The original
-     * text is used to determine where to display the chip span. If this test fails, it means some
-     * text that should be turned into one whole chip may behave unexpectedly.
-     * </p>
-     * <p>
-     * For example, a bug was seen where
-     *
-     * <pre>
-     * "Android User" <android@example.com>
-     * </pre>
-     *
-     * was converted to
-     *
-     * <pre>
-     * Android User [android@example.com]
-     * </pre>
-     *
-     * where text inside [] is a chip.
-     * </p>
-     */
-    public void testCreateReplacementChipOriginalText() {
-        // Name in quotes + email address
-        testCreateReplacementChipOriginalText("\"Android User\" <android@example.com>,");
-        // Name in quotes + email address without brackets
-        testCreateReplacementChipOriginalText("\"Android User\" android@example.com,");
-        // Name in quotes
-        testCreateReplacementChipOriginalText("\"Android User\",");
-        // Name without quotes + email address
-        testCreateReplacementChipOriginalText("Android User <android@example.com>,");
-        // Name without quotes
-        testCreateReplacementChipOriginalText("Android User,");
-        // Email address
-        testCreateReplacementChipOriginalText("<android@example.com>,");
-        // Email address without brackets
-        testCreateReplacementChipOriginalText("android@example.com,");
-    }
-
-    private void testCreateReplacementChipOriginalText(final String email) {
-        // No trailing space
-        attemptCreateReplacementChipOriginalText(email.trim());
-        // Trailing space
-        attemptCreateReplacementChipOriginalText(email.trim() + " ");
-    }
-
-    private void attemptCreateReplacementChipOriginalText(final String email) {
-        final RecipientEditTextView view = new RecipientEditTextView(getContext(), null);
-
-        view.setText(email);
-        view.mPendingChips.add(email);
-
-        view.createReplacementChip(0, email.length(), view.getText(), true);
-        // The "original text" should be the email without the comma or space(s)
-        assertEquals(email.replaceAll(",\\s*$", ""),
-                view.mTemporaryRecipients.get(0).getOriginalText().toString().trim());
-    }
-
-    public void testCreateTokenizedEntryForPhone() {
-        final String phonePattern = "[^\\d]*888[^\\d]*555[^\\d]*1234[^\\d]*";
-        final String phone1 = "8885551234";
-        final String phone2 = "888-555-1234";
-        final String phone3 = "(888) 555-1234";
-
-        final RecipientEditTextView view = new RecipientEditTextView(getContext(), null);
-        final BaseRecipientAdapter adapter = new TestBaseRecipientAdapter(getContext(), 10,
-                BaseRecipientAdapter.QUERY_TYPE_PHONE);
-        view.setAdapter(adapter);
-
-        final RecipientEntry entry1 = view.createTokenizedEntry(phone1);
-        final String destination1 = entry1.getDestination();
-        assertTrue(phone1 + " failed with " + destination1,
-                Pattern.matches(phonePattern, destination1));
-
-        final RecipientEntry entry2 = view.createTokenizedEntry(phone2);
-        final String destination2 = entry2.getDestination();
-        assertTrue(phone2 + " failed with " + destination2,
-                Pattern.matches(phonePattern, destination2));
-
-        final RecipientEntry entry3 = view.createTokenizedEntry(phone3);
-        final String destination3 = entry3.getDestination();
-        assertTrue(phone3 + " failed with " + destination3,
-                Pattern.matches(phonePattern, destination3));
-    }
-}
diff --git a/chips/tests/src/com/android/ex/chips/RecipientAlternatesAdapterTest.java b/chips/tests/src/com/android/ex/chips/RecipientAlternatesAdapterTest.java
deleted file mode 100644
index afb6a00..0000000
--- a/chips/tests/src/com/android/ex/chips/RecipientAlternatesAdapterTest.java
+++ /dev/null
@@ -1,167 +0,0 @@
-/*
- * Copyright (C) 2012 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.android.ex.chips;
-
-import android.database.Cursor;
-import android.database.MatrixCursor;
-import android.net.Uri;
-import android.provider.ContactsContract.DisplayNameSources;
-import android.test.AndroidTestCase;
-
-import com.android.ex.chips.RecipientAlternatesAdapter;
-import com.android.ex.chips.RecipientEntry;
-
-public class RecipientAlternatesAdapterTest extends AndroidTestCase {
-
-    public void testRemoveUndesiredDestinations() {
-        MatrixCursor c = new MatrixCursor(Queries.EMAIL.getProjection());
-        Cursor result;
-
-        // Test: Empty input
-        assertEquals(0, RecipientAlternatesAdapter.removeUndesiredDestinations(c,
-                null /* desiredMimeType */, null /* lookupKey */).getCount());
-
-
-        // Test: One row
-        addRow(c, "a", "1@android.com", 1, "home", 1000, 2000, "x", 0);
-
-        result = RecipientAlternatesAdapter.removeUndesiredDestinations(c,
-                null /* desiredMimeType */, null /* lookupKey */);
-        assertEquals(1, result.getCount());
-        assertRow(result, 0, "a", "1@android.com", 1, "home", 1000, 2000, "x", 0);
-
-        // Test: two unique rows, different destinations
-        addRow(c, "a", "2@android.com", 1, "home", 1000, 2000, "x", 0);
-
-        result = RecipientAlternatesAdapter.removeUndesiredDestinations(c,
-                null /* desiredMimeType */, null /* lookupKey */);
-        assertEquals(2, result.getCount());
-        assertRow(result, 0, "a", "1@android.com", 1, "home", 1000, 2000, "x", 0);
-        assertRow(result, 1, "a", "2@android.com", 1, "home", 1000, 2000, "x", 0);
-
-        // Test: add a third row with a non-unique destination.
-        addRow(c, "ax", "1@android.com", 11, "homex", 10001, 2000, "xx", 1);
-
-        // Third row should be removed.
-        result = RecipientAlternatesAdapter.removeUndesiredDestinations(c,
-                null /* desiredMimeType */, null /* lookupKey */);
-        assertEquals(2, result.getCount());
-        assertRow(result, 0, "a", "1@android.com", 1, "home", 1000, 2000, "x", 0);
-        assertRow(result, 1, "a", "2@android.com", 1, "home", 1000, 2000, "x", 0);
-
-        // Test: add a forth row with a non-unique destination again.
-        addRow(c, "ax", "2@android.com", 11, "homex", 10001, 2000, "xx", 1);
-
-        // Forth row should also be removed.
-        result = RecipientAlternatesAdapter.removeUndesiredDestinations(c,
-                null /* desiredMimeType */, null /* lookupKey */);
-        assertEquals(2, result.getCount());
-        assertRow(result, 0, "a", "1@android.com", 1, "home", 1000, 2000, "x", 0);
-        assertRow(result, 1, "a", "2@android.com", 1, "home", 1000, 2000, "x", 0);
-    }
-
-    private static MatrixCursor addRow(MatrixCursor c,
-            String displayName,
-            String destination,
-            int destinationType,
-            String destinationLabel,
-            long contactId,
-            long dataId,
-            String photoUri,
-            int displayNameSource
-            ) {
-        c.addRow(new Object[] {displayName, destination, destinationType, destinationLabel,
-                contactId, dataId, photoUri, displayNameSource});
-        return c;
-    }
-
-    private static void assertRow(Cursor c, int position,
-            String displayName,
-            String destination,
-            int destinationType,
-            String destinationLabel,
-            long contactId,
-            long dataId,
-            String photoUri,
-            int displayNameSource
-            ) {
-        assertTrue(c.moveToPosition(position));
-        assertEquals(displayName, c.getString(0));
-        assertEquals(destination, c.getString(1));
-        assertEquals(destinationType, c.getInt(2));
-        assertEquals(destinationLabel, c.getString(3));
-        assertEquals(contactId, c.getLong(4));
-        assertEquals(dataId, c.getLong(5));
-        assertEquals(photoUri, c.getString(6));
-        assertEquals(displayNameSource, c.getInt(7));
-    }
-
-    public void testGetBetterRecipient() {
-        // Ensure that if either (but not both) parameters are null, the other is returned
-        {
-            final RecipientEntry entry1 =
-                    RecipientEntry.constructFakeEntry("1@android.com", true);
-            final RecipientEntry entry2 = null;
-
-            assertEquals(RecipientAlternatesAdapter.getBetterRecipient(entry1, entry2), entry1);
-            assertEquals(RecipientAlternatesAdapter.getBetterRecipient(entry2, entry1), entry1);
-        }
-
-        // Ensure that if only one has a display name, it is used
-        {
-            final RecipientEntry entry1 =
-                    RecipientEntry.constructTopLevelEntry("Android", DisplayNameSources.NICKNAME,
-                            "1@android.com", 0, null, 0, null /* directoryId */, 0, (Uri) null,
-                            true, null /* lookupKey */);
-            final RecipientEntry entry2 = RecipientEntry.constructFakeEntry("1@android.com", true);
-
-            assertEquals(RecipientAlternatesAdapter.getBetterRecipient(entry1, entry2), entry1);
-            assertEquals(RecipientAlternatesAdapter.getBetterRecipient(entry2, entry1), entry1);
-        }
-
-        // Ensure that if one has a display name different from its destination, and the other's
-        // is equal to its destination, we use the unique one
-        {
-            final RecipientEntry entry1 =
-                    RecipientEntry.constructTopLevelEntry("Android", DisplayNameSources.NICKNAME,
-                            "1@android.com", 0, null, 0, null /* directoryId */, 0, (Uri) null,
-                            true, null /* lookupKey */);
-            final RecipientEntry entry2 =
-                    RecipientEntry.constructTopLevelEntry("2@android.com", DisplayNameSources.EMAIL,
-                            "2@android.com", 0, null, 0, null /* directoryId */, 0, (Uri) null,
-                            true, null /* lookupKey */);
-
-            assertEquals(RecipientAlternatesAdapter.getBetterRecipient(entry1, entry2), entry1);
-            assertEquals(RecipientAlternatesAdapter.getBetterRecipient(entry2, entry1), entry1);
-        }
-
-        // Ensure that if only one has a photo, it is used
-        {
-            final RecipientEntry entry1 =
-                    RecipientEntry.constructTopLevelEntry("Android", DisplayNameSources.NICKNAME,
-                            "1@android.com", 0, null, 0, null /* directoryId */, 0,
-                            Uri.parse("http://www.android.com"), true, null /* lookupKey */);
-            final RecipientEntry entry2 =
-                    RecipientEntry.constructTopLevelEntry("Android", DisplayNameSources.EMAIL,
-                            "2@android.com", 0, null, 0, null /* directoryId */,
-                            0, (Uri) null, true, null /* lookupKey */);
-
-            assertEquals(RecipientAlternatesAdapter.getBetterRecipient(entry1, entry2), entry1);
-            assertEquals(RecipientAlternatesAdapter.getBetterRecipient(entry2, entry1), entry1);
-        }
-    }
-}
diff --git a/common/build.gradle b/common/build.gradle
index ed3dba9..258bee6 100644
--- a/common/build.gradle
+++ b/common/build.gradle
@@ -1,38 +1,17 @@
 apply plugin: 'android-library'
+apply plugin: 'logtags'
 
 android {
-    compileSdkVersion 17
-    buildToolsVersion = '19.0.1'
-
-    logtags {
-        srcDirs = ['java']
-        genDir = "$buildDir/source/generated"
-    }
-
     sourceSets {
         main {
-            manifest {
-                srcFile "AndroidManifest.xml"
-            }
-            java {
-                srcDirs = [
-                    'java',
-                    "$buildDir/source/generated"
-                ]
-            }
-            resources.srcDirs = ['src']
-            aidl.srcDirs = ['src']
-            renderscript.srcDirs = ['src']
-            res.srcDirs = ['res']
-            assets.srcDirs = ['assets']
+            manifest.srcFile 'AndroidManifest.xml'
+            java.srcDirs = ['java']
+            logtags.srcDirs = ['java']
         }
     }
+
     lintOptions {
         // TODO: fix errors and reenable.
         abortOnError false
     }
 }
-
-android.libraryVariants.each { variant ->
-    variant.packageLibrary.baseName = "android-common"
-}
\ No newline at end of file
diff --git a/common/java/com/android/common/OperationScheduler.java b/common/java/com/android/common/OperationScheduler.java
index 81371ef..22dfe41 100644
--- a/common/java/com/android/common/OperationScheduler.java
+++ b/common/java/com/android/common/OperationScheduler.java
@@ -20,7 +20,8 @@
 import android.net.http.AndroidHttpClient;
 import android.text.format.Time;
 
-import java.util.TreeSet;
+import java.util.Map;
+import java.util.TreeMap;
 
 /**
  * Tracks the success/failure history of a particular network operation in
@@ -355,16 +356,23 @@
      */
     public String toString() {
         StringBuilder out = new StringBuilder("[OperationScheduler:");
-        for (String key : new TreeSet<String>(mStorage.getAll().keySet())) {  // Sort keys
+        TreeMap<String, Object> copy = new TreeMap<String, Object>(mStorage.getAll());  // Sort keys
+        for (Map.Entry<String, Object> e : copy.entrySet()) {
+            String key = e.getKey();
             if (key.startsWith(PREFIX)) {
                 if (key.endsWith("TimeMillis")) {
                     Time time = new Time();
-                    time.set(mStorage.getLong(key, 0));
+                    time.set((Long) e.getValue());
                     out.append(" ").append(key.substring(PREFIX.length(), key.length() - 10));
                     out.append("=").append(time.format("%Y-%m-%d/%H:%M:%S"));
                 } else {
                     out.append(" ").append(key.substring(PREFIX.length()));
-                    out.append("=").append(mStorage.getAll().get(key).toString());
+                    Object v = e.getValue();
+                    if (v == null) {
+                        out.append("=(null)");
+                    } else {
+                        out.append("=").append(v.toString());
+                    }
                 }
             }
         }
diff --git a/framesequence/jni/Android.mk b/framesequence/jni/Android.mk
index ee86fc1..e9d0ec5 100644
--- a/framesequence/jni/Android.mk
+++ b/framesequence/jni/Android.mk
@@ -19,12 +19,13 @@
 
 ## Main library
 
-LOCAL_STATIC_LIBRARIES += libgif
+LOCAL_STATIC_LIBRARIES += libgif libwebp-decode
 
 LOCAL_LDFLAGS := -llog -ljnigraphics
 
 LOCAL_C_INCLUDES := \
-	external/giflib
+	external/giflib \
+	external/webp/include
 
 LOCAL_MODULE    := libframesequence
 LOCAL_SRC_FILES := \
@@ -32,6 +33,7 @@
 	FrameSequence.cpp \
 	FrameSequenceJNI.cpp \
 	FrameSequence_gif.cpp \
+	FrameSequence_webp.cpp \
 	JNIHelpers.cpp \
 	Registry.cpp \
 	Stream.cpp
diff --git a/framesequence/jni/FrameSequence_gif.cpp b/framesequence/jni/FrameSequence_gif.cpp
index daa097b..f3e94df 100644
--- a/framesequence/jni/FrameSequence_gif.cpp
+++ b/framesequence/jni/FrameSequence_gif.cpp
@@ -89,7 +89,7 @@
                     && eb2->Function == CONTINUE_EXT_FUNC_CODE
                     && eb2->ByteCount == 3
                     && eb2->Bytes[0] == 1) {
-                mLoopCount = (int)(eb2->Bytes[2] & 0xff) + (int)(eb2->Bytes[1] & 0xff);
+                mLoopCount = (int)(eb2->Bytes[2] << 8) + (int)(eb2->Bytes[1]);
             }
         }
 
diff --git a/framesequence/jni/FrameSequence_webp.cpp b/framesequence/jni/FrameSequence_webp.cpp
new file mode 100644
index 0000000..602feb7
--- /dev/null
+++ b/framesequence/jni/FrameSequence_webp.cpp
@@ -0,0 +1,380 @@
+/*
+ * 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.
+ */
+
+#include <string.h>
+#include "JNIHelpers.h"
+#include "utils/log.h"
+#include "utils/math.h"
+#include "webp/format_constants.h"
+
+#include "FrameSequence_webp.h"
+
+#define WEBP_DEBUG 0
+
+////////////////////////////////////////////////////////////////////////////////
+// Frame sequence
+////////////////////////////////////////////////////////////////////////////////
+
+static uint32_t GetLE32(const uint8_t* const data) {
+    return MKFOURCC(data[0], data[1], data[2], data[3]);
+}
+
+// Returns true if the frame covers full canvas.
+static bool isFullFrame(const WebPIterator& frame, int canvasWidth, int canvasHeight) {
+    return (frame.width == canvasWidth && frame.height == canvasHeight);
+}
+
+// Returns true if the rectangle defined by 'frame' contains pixel (x, y).
+static bool FrameContainsPixel(const WebPIterator& frame, int x, int y) {
+    const int left = frame.x_offset;
+    const int right = left + frame.width;
+    const int top = frame.y_offset;
+    const int bottom = top + frame.height;
+    return x >= left && x < right && y >= top && y < bottom;
+}
+
+// Construct mIsKeyFrame array.
+void FrameSequence_webp::constructDependencyChain() {
+    const size_t frameCount = getFrameCount();
+    mIsKeyFrame = new bool[frameCount];
+    const int canvasWidth = getWidth();
+    const int canvasHeight = getHeight();
+
+    WebPIterator prev;
+    WebPIterator curr;
+
+    // Note: WebPDemuxGetFrame() uses base-1 counting.
+    int ok = WebPDemuxGetFrame(mDemux, 1, &curr);
+    ALOG_ASSERT(ok, "Could not retrieve frame# 0");
+    mIsKeyFrame[0] = true;  // 0th frame is always a key frame.
+    for (size_t i = 1; i < frameCount; i++) {
+        prev = curr;
+        ok = WebPDemuxGetFrame(mDemux, i + 1, &curr);  // Get ith frame.
+        ALOG_ASSERT(ok, "Could not retrieve frame# %d", i);
+
+        if ((!curr.has_alpha || curr.blend_method == WEBP_MUX_NO_BLEND) &&
+                isFullFrame(curr, canvasWidth, canvasHeight)) {
+            mIsKeyFrame[i] = true;
+        } else {
+            mIsKeyFrame[i] = (prev.dispose_method == WEBP_MUX_DISPOSE_BACKGROUND) &&
+                    (isFullFrame(prev, canvasWidth, canvasHeight) || mIsKeyFrame[i - 1]);
+        }
+    }
+    WebPDemuxReleaseIterator(&prev);
+    WebPDemuxReleaseIterator(&curr);
+
+#if WEBP_DEBUG
+    ALOGD("Dependency chain:");
+    for (size_t i = 0; i < frameCount; i++) {
+        ALOGD("Frame# %zu: %s", i, mIsKeyFrame[i] ? "Key frame" : "NOT a key frame");
+    }
+#endif
+}
+
+FrameSequence_webp::FrameSequence_webp(Stream* stream) {
+    // Read RIFF header to get file size.
+    uint8_t riff_header[RIFF_HEADER_SIZE];
+    if (stream->read(riff_header, RIFF_HEADER_SIZE) != RIFF_HEADER_SIZE) {
+        ALOGE("WebP header load failed");
+        return;
+    }
+    mData.size = CHUNK_HEADER_SIZE + GetLE32(riff_header + TAG_SIZE);
+    mData.bytes = new uint8_t[mData.size];
+    memcpy((void*)mData.bytes, riff_header, RIFF_HEADER_SIZE);
+
+    // Read rest of the bytes.
+    void* remaining_bytes = (void*)(mData.bytes + RIFF_HEADER_SIZE);
+    size_t remaining_size = mData.size - RIFF_HEADER_SIZE;
+    if (stream->read(remaining_bytes, remaining_size) != remaining_size) {
+        ALOGE("WebP full load failed");
+        return;
+    }
+
+    // Construct demux.
+    mDemux = WebPDemux(&mData);
+    if (!mDemux) {
+        ALOGE("Parsing of WebP container file failed");
+        return;
+    }
+    mLoopCount = WebPDemuxGetI(mDemux, WEBP_FF_LOOP_COUNT);
+    mFormatFlags = WebPDemuxGetI(mDemux, WEBP_FF_FORMAT_FLAGS);
+#if WEBP_DEBUG
+    ALOGD("FrameSequence_webp created with size = %d x %d, number of frames = %d, flags = 0x%X",
+          getWidth(), getHeight(), getFrameCount(), mFormatFlags);
+#endif
+    constructDependencyChain();
+}
+
+FrameSequence_webp::~FrameSequence_webp() {
+    WebPDemuxDelete(mDemux);
+    delete[] mData.bytes;
+    delete[] mIsKeyFrame;
+}
+
+FrameSequenceState* FrameSequence_webp::createState() const {
+    return new FrameSequenceState_webp(*this);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// draw helpers
+////////////////////////////////////////////////////////////////////////////////
+
+static bool willBeCleared(const WebPIterator& iter) {
+    return iter.dispose_method == WEBP_MUX_DISPOSE_BACKGROUND;
+}
+
+// return true if area of 'target' completely covers area of 'covered'
+static bool checkIfCover(const WebPIterator& target, const WebPIterator& covered) {
+    const int covered_x_max = covered.x_offset + covered.width;
+    const int target_x_max = target.x_offset + target.width;
+    const int covered_y_max = covered.y_offset + covered.height;
+    const int target_y_max = target.y_offset + target.height;
+    return target.x_offset <= covered.x_offset
+           && covered_x_max <= target_x_max
+           && target.y_offset <= covered.y_offset
+           && covered_y_max <= target_y_max;
+}
+
+// Clear all pixels in a line to transparent.
+static void clearLine(Color8888* dst, int width) {
+    memset(dst, 0, width * sizeof(*dst));  // Note: Assumes TRANSPARENT == 0x0.
+}
+
+// Copy all pixels from 'src' to 'dst'.
+static void copyFrame(const Color8888* src, int srcStride, Color8888* dst, int dstStride,
+        int width, int height) {
+    for (int y = 0; y < height; y++) {
+        memcpy(dst, src, width * sizeof(*dst));
+        src += srcStride;
+        dst += dstStride;
+    }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Frame sequence state
+////////////////////////////////////////////////////////////////////////////////
+
+FrameSequenceState_webp::FrameSequenceState_webp(const FrameSequence_webp& frameSequence) :
+        mFrameSequence(frameSequence) {
+    WebPInitDecoderConfig(&mDecoderConfig);
+    mDecoderConfig.output.is_external_memory = 1;
+    mDecoderConfig.output.colorspace = MODE_rgbA;  // Pre-multiplied alpha mode.
+    const int canvasWidth = mFrameSequence.getWidth();
+    const int canvasHeight = mFrameSequence.getHeight();
+    mPreservedBuffer = new Color8888[canvasWidth * canvasHeight];
+}
+
+FrameSequenceState_webp::~FrameSequenceState_webp() {
+    delete[] mPreservedBuffer;
+}
+
+void FrameSequenceState_webp::initializeFrame(const WebPIterator& currIter, Color8888* currBuffer,
+        int currStride, const WebPIterator& prevIter, const Color8888* prevBuffer, int prevStride) {
+    const int canvasWidth = mFrameSequence.getWidth();
+    const int canvasHeight = mFrameSequence.getHeight();
+    const bool currFrameIsKeyFrame = mFrameSequence.isKeyFrame(currIter.frame_num - 1);
+
+    if (currFrameIsKeyFrame) {  // Clear canvas.
+        for (int y = 0; y < canvasHeight; y++) {
+            Color8888* dst = currBuffer + y * currStride;
+            clearLine(dst, canvasWidth);
+        }
+    } else {
+        // Preserve previous frame as starting state of current frame.
+        copyFrame(prevBuffer, prevStride, currBuffer, currStride, canvasWidth, canvasHeight);
+
+        // Dispose previous frame rectangle to Background if needed.
+        bool prevFrameCompletelyCovered =
+                (!currIter.has_alpha || currIter.blend_method == WEBP_MUX_NO_BLEND) &&
+                checkIfCover(currIter, prevIter);
+        if ((prevIter.dispose_method == WEBP_MUX_DISPOSE_BACKGROUND) &&
+                !prevFrameCompletelyCovered) {
+            Color8888* dst = currBuffer + prevIter.x_offset + prevIter.y_offset * currStride;
+            for (int j = 0; j < prevIter.height; j++) {
+                clearLine(dst, prevIter.width);
+                dst += currStride;
+            }
+        }
+    }
+}
+
+bool FrameSequenceState_webp::decodeFrame(const WebPIterator& currIter, Color8888* currBuffer,
+        int currStride, const WebPIterator& prevIter, const Color8888* prevBuffer, int prevStride) {
+    Color8888* dst = currBuffer + currIter.x_offset + currIter.y_offset * currStride;
+    mDecoderConfig.output.u.RGBA.rgba = (uint8_t*)dst;
+    mDecoderConfig.output.u.RGBA.stride = currStride * 4;
+    mDecoderConfig.output.u.RGBA.size = mDecoderConfig.output.u.RGBA.stride * currIter.height;
+
+    const WebPData& currFrame = currIter.fragment;
+    if (WebPDecode(currFrame.bytes, currFrame.size, &mDecoderConfig) != VP8_STATUS_OK) {
+        return false;
+    }
+
+    const int canvasWidth = mFrameSequence.getWidth();
+    const int canvasHeight = mFrameSequence.getHeight();
+    const bool currFrameIsKeyFrame = mFrameSequence.isKeyFrame(currIter.frame_num - 1);
+    // During the decoding of current frame, we may have set some pixels to be transparent
+    // (i.e. alpha < 255). However, the value of each of these pixels should have been determined
+    // by blending it against the value of that pixel in the previous frame if WEBP_MUX_BLEND was
+    // specified. So, we correct these pixels based on disposal method of the previous frame and
+    // the previous frame buffer.
+    if (currIter.blend_method == WEBP_MUX_BLEND && !currFrameIsKeyFrame) {
+        if (prevIter.dispose_method == WEBP_MUX_DISPOSE_NONE) {
+            for (int y = 0; y < currIter.height; y++) {
+                const int canvasY = currIter.y_offset + y;
+                for (int x = 0; x < currIter.width; x++) {
+                    const int canvasX = currIter.x_offset + x;
+                    Color8888& currPixel = currBuffer[canvasY * currStride + canvasX];
+                    // FIXME: Use alpha-blending when alpha is between 0 and 255.
+                    if (!(currPixel & COLOR_8888_ALPHA_MASK)) {
+                        const Color8888 prevPixel = prevBuffer[canvasY * prevStride + canvasX];
+                        currPixel = prevPixel;
+                    }
+                }
+            }
+        } else {  // prevIter.dispose_method == WEBP_MUX_DISPOSE_BACKGROUND
+            // Need to restore transparent pixels to as they were just after frame initialization.
+            // That is:
+            //   * Transparent if it belongs to previous frame rectangle <-- This is a no-op.
+            //   * Pixel in the previous canvas otherwise <-- Need to restore.
+            for (int y = 0; y < currIter.height; y++) {
+                const int canvasY = currIter.y_offset + y;
+                for (int x = 0; x < currIter.width; x++) {
+                    const int canvasX = currIter.x_offset + x;
+                    Color8888& currPixel = currBuffer[canvasY * currStride + canvasX];
+                    // FIXME: Use alpha-blending when alpha is between 0 and 255.
+                    if (!(currPixel & COLOR_8888_ALPHA_MASK)
+                            && !FrameContainsPixel(prevIter, canvasX, canvasY)) {
+                        const Color8888 prevPixel = prevBuffer[canvasY * prevStride + canvasX];
+                        currPixel = prevPixel;
+                    }
+                }
+            }
+        }
+    }
+    return true;
+}
+
+long FrameSequenceState_webp::drawFrame(int frameNr,
+        Color8888* outputPtr, int outputPixelStride, int previousFrameNr) {
+    WebPDemuxer* demux = mFrameSequence.getDemuxer();
+    ALOG_ASSERT(demux, "Cannot drawFrame, mDemux is NULL");
+
+#if WEBP_DEBUG
+    ALOGD("  drawFrame called for frame# %d, previous frame# %d", frameNr, previousFrameNr);
+#endif
+
+    const int canvasWidth = mFrameSequence.getWidth();
+    const int canvasHeight = mFrameSequence.getHeight();
+
+    // Find the first frame to be decoded.
+    int start = max(previousFrameNr + 1, 0);
+    int earliestRequired = frameNr;
+    while (earliestRequired > start) {
+        if (mFrameSequence.isKeyFrame(earliestRequired)) {
+            start = earliestRequired;
+            break;
+        }
+        earliestRequired--;
+    }
+
+    WebPIterator currIter;
+    WebPIterator prevIter;
+    int ok = WebPDemuxGetFrame(demux, start, &currIter);  // Get frame number 'start - 1'.
+    ALOG_ASSERT(ok, "Could not retrieve frame# %d", start - 1);
+
+    // Use preserve buffer only if needed.
+    Color8888* prevBuffer = (frameNr == 0) ? outputPtr : mPreservedBuffer;
+    int prevStride = (frameNr == 0) ? outputPixelStride : canvasWidth;
+    Color8888* currBuffer = outputPtr;
+    int currStride = outputPixelStride;
+
+    for (int i = start; i <= frameNr; i++) {
+        prevIter = currIter;
+        ok = WebPDemuxGetFrame(demux, i + 1, &currIter);  // Get ith frame.
+        ALOG_ASSERT(ok, "Could not retrieve frame# %d", i);
+#if WEBP_DEBUG
+        ALOGD("      producing frame %d (has_alpha = %d, dispose = %s, blend = %s, duration = %d)",
+              i, currIter.has_alpha,
+              (currIter.dispose_method == WEBP_MUX_DISPOSE_NONE) ? "none" : "background",
+              (currIter.blend_method == WEBP_MUX_BLEND) ? "yes" : "no", currIter.duration);
+#endif
+        // We swap the prev/curr buffers as we go.
+        Color8888* tmpBuffer = prevBuffer;
+        prevBuffer = currBuffer;
+        currBuffer = tmpBuffer;
+
+        int tmpStride = prevStride;
+        prevStride = currStride;
+        currStride = tmpStride;
+
+#if WEBP_DEBUG
+        ALOGD("            prev = %p, curr = %p, out = %p, tmp = %p",
+              prevBuffer, currBuffer, outputPtr, mPreservedBuffer);
+#endif
+        // Process this frame.
+        initializeFrame(currIter, currBuffer, currStride, prevIter, prevBuffer, prevStride);
+
+        if (i == frameNr || !willBeCleared(currIter)) {
+            if (!decodeFrame(currIter, currBuffer, currStride, prevIter, prevBuffer, prevStride)) {
+                ALOGE("Error decoding frame# %d", i);
+                return -1;
+            }
+        }
+    }
+
+    if (outputPtr != currBuffer) {
+        copyFrame(currBuffer, currStride, outputPtr, outputPixelStride, canvasWidth, canvasHeight);
+    }
+
+    // Return last frame's delay.
+    const int frameCount = mFrameSequence.getFrameCount();
+    const int lastFrame = (frameNr + frameCount - 1) % frameCount;
+    ok = WebPDemuxGetFrame(demux, lastFrame, &currIter);
+    ALOG_ASSERT(ok, "Could not retrieve frame# %d", lastFrame - 1);
+    const int lastFrameDelay = currIter.duration;
+
+    WebPDemuxReleaseIterator(&currIter);
+    WebPDemuxReleaseIterator(&prevIter);
+
+    return lastFrameDelay;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Registry
+////////////////////////////////////////////////////////////////////////////////
+
+#include "Registry.h"
+
+static bool isWebP(void* header, int header_size) {
+    const uint8_t* const header_str = (const uint8_t*)header;
+    return (header_size >= RIFF_HEADER_SIZE) &&
+            !memcmp("RIFF", header_str, 4) &&
+            !memcmp("WEBP", header_str + 8, 4);
+}
+
+static FrameSequence* createFramesequence(Stream* stream) {
+    return new FrameSequence_webp(stream);
+}
+
+static RegistryEntry gEntry = {
+        RIFF_HEADER_SIZE,
+        isWebP,
+        createFramesequence,
+        NULL,
+};
+static Registry gRegister(gEntry);
+
diff --git a/framesequence/jni/FrameSequence_webp.h b/framesequence/jni/FrameSequence_webp.h
new file mode 100644
index 0000000..f4fbec0
--- /dev/null
+++ b/framesequence/jni/FrameSequence_webp.h
@@ -0,0 +1,92 @@
+/*
+ * 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.
+ */
+
+#ifndef RASTERMILL_FRAMESQUENCE_WEBP_H
+#define RASTERMILL_FRAMESQUENCE_WEBP_H
+
+#include "config.h"
+#include "webp/decode.h"
+#include "webp/demux.h"
+
+#include "Stream.h"
+#include "Color.h"
+#include "FrameSequence.h"
+
+// Parser for a possibly-animated WebP bitstream.
+class FrameSequence_webp : public FrameSequence {
+public:
+    FrameSequence_webp(Stream* stream);
+    virtual ~FrameSequence_webp();
+
+    virtual int getWidth() const {
+        return WebPDemuxGetI(mDemux, WEBP_FF_CANVAS_WIDTH);
+    }
+
+    virtual int getHeight() const {
+        return WebPDemuxGetI(mDemux, WEBP_FF_CANVAS_HEIGHT);
+    }
+
+    virtual bool isOpaque() const {
+        return !(mFormatFlags & ALPHA_FLAG);
+    }
+
+    virtual int getFrameCount() const {
+        return WebPDemuxGetI(mDemux, WEBP_FF_FRAME_COUNT);
+    }
+
+    virtual int getDefaultLoopCount() const {
+        return mLoopCount;
+    }
+
+    virtual FrameSequenceState* createState() const;
+
+    WebPDemuxer* getDemuxer() const { return mDemux; }
+
+    bool isKeyFrame(size_t frameNr) const { return mIsKeyFrame[frameNr]; }
+
+private:
+    void constructDependencyChain();
+
+    WebPData mData;
+    WebPDemuxer* mDemux;
+    int mLoopCount;
+    uint32_t mFormatFlags;
+    // mIsKeyFrame[i] is true if ith canvas can be constructed without decoding any prior frames.
+    bool* mIsKeyFrame;
+};
+
+// Produces frames of a possibly-animated WebP file for display.
+class FrameSequenceState_webp : public FrameSequenceState {
+public:
+    FrameSequenceState_webp(const FrameSequence_webp& frameSequence);
+    virtual ~FrameSequenceState_webp();
+
+    // Returns frame's delay time in milliseconds.
+    virtual long drawFrame(int frameNr,
+            Color8888* outputPtr, int outputPixelStride, int previousFrameNr);
+
+private:
+    void initializeFrame(const WebPIterator& currIter, Color8888* currBuffer, int currStride,
+            const WebPIterator& prevIter, const Color8888* prevBuffer, int prevStride);
+    bool decodeFrame(const WebPIterator& iter, Color8888* currBuffer, int currStride,
+            const WebPIterator& prevIter, const Color8888* prevBuffer, int prevStride);
+
+    const FrameSequence_webp& mFrameSequence;
+    WebPDecoderConfig mDecoderConfig;
+    Color8888* mPreservedBuffer;
+};
+
+#endif //RASTERMILL_FRAMESQUENCE_WEBP_H
diff --git a/framesequence/jni/utils/log.h b/framesequence/jni/utils/log.h
index 5e15f30..d8441dc 100644
--- a/framesequence/jni/utils/log.h
+++ b/framesequence/jni/utils/log.h
@@ -267,6 +267,20 @@
     if (__android_log_assert(ANDROID_##priority, tag))
 #endif
 
+/* Returns 2nd arg.  Used to substitute default value if caller's vararg list
+ * is empty.
+ */
+#define __android_second(dummy, second, ...)     second
+
+/* If passed multiple args, returns ',' followed by all but 1st arg, otherwise
+ * returns nothing.
+ */
+#define __android_rest(first, ...)               , ## __VA_ARGS__
+
+#define android_printAssert(cond, tag, fmt...) \
+    __android_log_assert(cond, tag, \
+        __android_second(0, ## fmt, NULL) __android_rest(fmt))
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/variablespeed/jni/jni_entry.cc b/variablespeed/jni/jni_entry.cc
index 93c12ba..20bcf3c 100644
--- a/variablespeed/jni/jni_entry.cc
+++ b/variablespeed/jni/jni_entry.cc
@@ -81,9 +81,12 @@
     jfloat initialRate, jint decodeInitialSize, jint decodeMaxSize,
     jint startPositionMillis, jint audioStreamType) {
   MethodLog _("initializeEngine");
-  AudioEngine::SetEngine(new AudioEngine(targetFrames,
+  AudioEngine *engine = new AudioEngine(targetFrames,
       windowDuration, windowOverlapDuration, maxPlayBufferCount, initialRate,
-      decodeInitialSize, decodeMaxSize, startPositionMillis, audioStreamType));
+      decodeInitialSize, decodeMaxSize, startPositionMillis, audioStreamType);
+  if (!AudioEngine::CompareAndSetEngine(NULL, engine)) {
+    delete engine;
+  }
 }
 
 JNI_METHOD(shutdownEngine, void) (JNIEnv*, jclass) {
diff --git a/variablespeed/jni/variablespeed.cc b/variablespeed/jni/variablespeed.cc
index b5d9067..8e161fc 100644
--- a/variablespeed/jni/variablespeed.cc
+++ b/variablespeed/jni/variablespeed.cc
@@ -129,14 +129,21 @@
   audioEngine_ = engine;
 }
 
-void AudioEngine::DeleteEngine() {
-  if (audioEngine_ == NULL) {
-    LOGE("you haven't initialized the audio engine");
-    CHECK(false);
-    return;
+bool AudioEngine::CompareAndSetEngine(AudioEngine* expect, AudioEngine* update) {
+  android::Mutex::Autolock autoLock(publishEngineLock_);
+  if (audioEngine_ == expect) {
+    DeleteEngine();
+    audioEngine_ = update;
+    return true;
   }
-  delete audioEngine_;
-  audioEngine_ = NULL;
+  return false;
+}
+
+void AudioEngine::DeleteEngine() {
+  if (audioEngine_ != NULL) {
+    delete audioEngine_;
+    audioEngine_ = NULL;
+  }
 }
 
 // ****************************************************************************
diff --git a/variablespeed/jni/variablespeed.h b/variablespeed/jni/variablespeed.h
index cf856da..74710e5 100644
--- a/variablespeed/jni/variablespeed.h
+++ b/variablespeed/jni/variablespeed.h
@@ -64,6 +64,7 @@
 
   static AudioEngine* GetEngine();
   static void SetEngine(AudioEngine* engine);
+  static bool CompareAndSetEngine(AudioEngine* expect, AudioEngine* update);
   static void DeleteEngine();
 
  private:
diff --git a/variablespeed/src/com/android/ex/variablespeed/MediaPlayerProxy.java b/variablespeed/src/com/android/ex/variablespeed/MediaPlayerProxy.java
index 8489dc1..3b7b576 100644
--- a/variablespeed/src/com/android/ex/variablespeed/MediaPlayerProxy.java
+++ b/variablespeed/src/com/android/ex/variablespeed/MediaPlayerProxy.java
@@ -42,6 +42,7 @@
     int getDuration();
     void seekTo(int startPosition);
     void start();
+    boolean isReadyToPlay();
     boolean isPlaying();
     int getCurrentPosition();
     void pause();
diff --git a/variablespeed/src/com/android/ex/variablespeed/SingleThreadedMediaPlayerProxy.java b/variablespeed/src/com/android/ex/variablespeed/SingleThreadedMediaPlayerProxy.java
index 17692f7..c9a9741 100644
--- a/variablespeed/src/com/android/ex/variablespeed/SingleThreadedMediaPlayerProxy.java
+++ b/variablespeed/src/com/android/ex/variablespeed/SingleThreadedMediaPlayerProxy.java
@@ -85,6 +85,11 @@
     }
 
     @Override
+    public synchronized boolean isReadyToPlay() {
+        return mDelegate.isReadyToPlay();
+    }
+
+    @Override
     public synchronized boolean isPlaying() {
         return mDelegate.isPlaying();
     }
diff --git a/variablespeed/src/com/android/ex/variablespeed/VariableSpeed.java b/variablespeed/src/com/android/ex/variablespeed/VariableSpeed.java
index 5c93d26..e44a375 100644
--- a/variablespeed/src/com/android/ex/variablespeed/VariableSpeed.java
+++ b/variablespeed/src/com/android/ex/variablespeed/VariableSpeed.java
@@ -337,9 +337,16 @@
     }
 
     @Override
+    public boolean isReadyToPlay() {
+        synchronized (lock) {
+            return !mHasBeenReleased && mHasDuration;
+        }
+    }
+
+    @Override
     public boolean isPlaying() {
         synchronized (lock) {
-            return mHasStartedPlayback && !hasPlaybackFinished();
+            return isReadyToPlay() && mHasStartedPlayback && !hasPlaybackFinished();
         }
     }