From b50b5cbfbc0a67db6fc43373363b10381c9c61a3 Mon Sep 17 00:00:00 2001 From: Angus Kong Date: Fri, 9 Aug 2013 14:55:20 -0700 Subject: Clean up Gallery codes. bug:10263696 Change-Id: I3a16dba3aabe52b8103a2c591cfa39907265a263 --- AndroidManifest.xml | 4 +- .../com/android/gallery3d/common/ApiHelper.java | 234 ----- .../android/gallery3d/common/AsyncTaskUtil.java | 66 -- .../com/android/gallery3d/common/BitmapUtils.java | 260 ------ .../com/android/gallery3d/common/BlobCache.java | 668 -------------- .../src/com/android/gallery3d/common/Entry.java | 58 -- .../com/android/gallery3d/common/EntrySchema.java | 542 ------------ .../com/android/gallery3d/common/FileCache.java | 312 ------- .../com/android/gallery3d/common/Fingerprint.java | 187 ---- .../gallery3d/common/HttpClientFactory.java | 133 --- .../src/com/android/gallery3d/common/LruCache.java | 90 -- .../com/android/gallery3d/common/OverScroller.java | 958 --------------------- .../src/com/android/gallery3d/common/Scroller.java | 507 ----------- .../src/com/android/gallery3d/common/Utils.java | 340 -------- .../gallery3d/jpegstream/JPEGInputStream.java | 193 ----- .../gallery3d/jpegstream/JPEGOutputStream.java | 144 ---- .../android/gallery3d/jpegstream/JpegConfig.java | 32 - .../android/gallery3d/jpegstream/StreamUtils.java | 80 -- .../src/com/android/gallery3d/util/Future.java | 35 - .../com/android/gallery3d/util/FutureListener.java | 21 - .../gallery3d/util/PriorityThreadFactory.java | 49 -- .../src/com/android/gallery3d/util/ThreadPool.java | 268 ------ proguard.flags | 29 - .../android/camera/AndroidCameraManagerImpl.java | 4 +- src/com/android/camera/CameraActivity.java | 7 +- src/com/android/camera/CameraHolder.java | 2 +- src/com/android/camera/CameraManager.java | 2 +- src/com/android/camera/CameraScreenNail.java | 526 ----------- src/com/android/camera/CameraSettings.java | 11 +- src/com/android/camera/CaptureAnimManager.java | 228 ----- src/com/android/camera/EffectsRecorder.java | 7 +- src/com/android/camera/FocusOverlayManager.java | 27 +- src/com/android/camera/ListPreference.java | 5 +- src/com/android/camera/PhotoModule.java | 80 +- src/com/android/camera/PhotoUI.java | 21 +- src/com/android/camera/PreviewFrameLayout.java | 5 +- src/com/android/camera/RotateDialogController.java | 169 ---- src/com/android/camera/SoundClips.java | 2 +- src/com/android/camera/StaticBitmapScreenNail.java | 32 - src/com/android/camera/Storage.java | 2 +- src/com/android/camera/SwitchAnimManager.java | 146 ---- src/com/android/camera/Util.java | 818 ------------------ src/com/android/camera/VideoModule.java | 69 +- src/com/android/camera/VideoUI.java | 20 +- src/com/android/camera/app/CameraApp.java | 19 +- src/com/android/camera/app/OrientationManager.java | 151 ++++ src/com/android/camera/app/OrientationSource.java | 6 + src/com/android/camera/data/LocalMediaData.java | 4 +- .../camera/support/app/OrientationManager.java | 152 ---- .../support/app/StitchingChangeListener.java | 11 - .../android/camera/support/common/ApiHelper.java | 219 ----- src/com/android/camera/support/common/Utils.java | 325 ------- .../camera/support/filtershow/crop/CropExtras.java | 106 --- .../camera/support/glrenderer/BasicTexture.java | 197 ----- .../camera/support/glrenderer/BitmapTexture.java | 38 - .../camera/support/glrenderer/ExtTexture.java | 45 - .../camera/support/glrenderer/GLCanvas.java | 201 ----- .../android/camera/support/glrenderer/GLId.java | 17 - .../android/camera/support/glrenderer/GLPaint.java | 26 - .../camera/support/glrenderer/NinePatchChunk.java | 66 -- .../support/glrenderer/NinePatchTexture.java | 408 --------- .../camera/support/glrenderer/RawTexture.java | 58 -- .../camera/support/glrenderer/ResourceTexture.java | 37 - .../android/camera/support/glrenderer/Texture.java | 27 - .../camera/support/glrenderer/UploadedTexture.java | 283 ------ .../camera/support/ui/BitmapScreenNail.java | 45 - .../camera/support/ui/OrientationSource.java | 6 - src/com/android/camera/support/ui/ScreenNail.java | 21 - .../support/ui/SurfaceTextureScreenNail.java | 128 --- .../camera/support/util/AccessibilityUtils.java | 38 - .../camera/support/util/MotionEventHelper.java | 105 --- src/com/android/camera/ui/CameraRootView.java | 6 +- src/com/android/camera/ui/CameraSwitcher.java | 6 +- src/com/android/camera/ui/EffectSettingPopup.java | 2 +- src/com/android/camera/ui/FaceView.java | 10 +- src/com/android/camera/ui/PreviewSurfaceView.java | 50 -- src/com/android/camera/ui/RotatableLayout.java | 10 +- src/com/android/camera/ui/RotateLayout.java | 4 +- src/com/android/camera/ui/RotateTextToast.java | 4 +- src/com/android/camera/ui/Switch.java | 2 +- .../android/camera/util/AccessibilityUtils.java | 36 + src/com/android/camera/util/ApiHelper.java | 234 +++++ src/com/android/camera/util/CameraUtil.java | 825 ++++++++++++++++++ src/com/android/camera/util/MotionEventHelper.java | 103 +++ .../camera/app/StitchingProgressManager.java | 21 - .../com/android/camera/util/PhotoSphereHelper.java | 10 - .../android/camera/activity/CameraTestCase.java | 4 +- .../android/camera/unittest/CameraUnitTest.java | 92 +- 88 files changed, 1569 insertions(+), 9982 deletions(-) delete mode 100644 gallerycommon/src/com/android/gallery3d/common/ApiHelper.java delete mode 100644 gallerycommon/src/com/android/gallery3d/common/AsyncTaskUtil.java delete mode 100644 gallerycommon/src/com/android/gallery3d/common/BitmapUtils.java delete mode 100644 gallerycommon/src/com/android/gallery3d/common/BlobCache.java delete mode 100644 gallerycommon/src/com/android/gallery3d/common/Entry.java delete mode 100644 gallerycommon/src/com/android/gallery3d/common/EntrySchema.java delete mode 100644 gallerycommon/src/com/android/gallery3d/common/FileCache.java delete mode 100644 gallerycommon/src/com/android/gallery3d/common/Fingerprint.java delete mode 100644 gallerycommon/src/com/android/gallery3d/common/HttpClientFactory.java delete mode 100644 gallerycommon/src/com/android/gallery3d/common/LruCache.java delete mode 100644 gallerycommon/src/com/android/gallery3d/common/OverScroller.java delete mode 100644 gallerycommon/src/com/android/gallery3d/common/Scroller.java delete mode 100644 gallerycommon/src/com/android/gallery3d/common/Utils.java delete mode 100644 gallerycommon/src/com/android/gallery3d/jpegstream/JPEGInputStream.java delete mode 100644 gallerycommon/src/com/android/gallery3d/jpegstream/JPEGOutputStream.java delete mode 100644 gallerycommon/src/com/android/gallery3d/jpegstream/JpegConfig.java delete mode 100644 gallerycommon/src/com/android/gallery3d/jpegstream/StreamUtils.java delete mode 100644 gallerycommon/src/com/android/gallery3d/util/Future.java delete mode 100644 gallerycommon/src/com/android/gallery3d/util/FutureListener.java delete mode 100644 gallerycommon/src/com/android/gallery3d/util/PriorityThreadFactory.java delete mode 100644 gallerycommon/src/com/android/gallery3d/util/ThreadPool.java delete mode 100644 src/com/android/camera/CameraScreenNail.java delete mode 100644 src/com/android/camera/CaptureAnimManager.java delete mode 100644 src/com/android/camera/RotateDialogController.java delete mode 100644 src/com/android/camera/StaticBitmapScreenNail.java delete mode 100644 src/com/android/camera/SwitchAnimManager.java delete mode 100644 src/com/android/camera/Util.java create mode 100644 src/com/android/camera/app/OrientationManager.java create mode 100644 src/com/android/camera/app/OrientationSource.java delete mode 100644 src/com/android/camera/support/app/OrientationManager.java delete mode 100644 src/com/android/camera/support/app/StitchingChangeListener.java delete mode 100644 src/com/android/camera/support/common/ApiHelper.java delete mode 100644 src/com/android/camera/support/common/Utils.java delete mode 100644 src/com/android/camera/support/filtershow/crop/CropExtras.java delete mode 100644 src/com/android/camera/support/glrenderer/BasicTexture.java delete mode 100644 src/com/android/camera/support/glrenderer/BitmapTexture.java delete mode 100644 src/com/android/camera/support/glrenderer/ExtTexture.java delete mode 100644 src/com/android/camera/support/glrenderer/GLCanvas.java delete mode 100644 src/com/android/camera/support/glrenderer/GLId.java delete mode 100644 src/com/android/camera/support/glrenderer/GLPaint.java delete mode 100644 src/com/android/camera/support/glrenderer/NinePatchChunk.java delete mode 100644 src/com/android/camera/support/glrenderer/NinePatchTexture.java delete mode 100644 src/com/android/camera/support/glrenderer/RawTexture.java delete mode 100644 src/com/android/camera/support/glrenderer/ResourceTexture.java delete mode 100644 src/com/android/camera/support/glrenderer/Texture.java delete mode 100644 src/com/android/camera/support/glrenderer/UploadedTexture.java delete mode 100644 src/com/android/camera/support/ui/BitmapScreenNail.java delete mode 100644 src/com/android/camera/support/ui/OrientationSource.java delete mode 100644 src/com/android/camera/support/ui/ScreenNail.java delete mode 100644 src/com/android/camera/support/ui/SurfaceTextureScreenNail.java delete mode 100644 src/com/android/camera/support/util/AccessibilityUtils.java delete mode 100644 src/com/android/camera/support/util/MotionEventHelper.java delete mode 100644 src/com/android/camera/ui/PreviewSurfaceView.java create mode 100644 src/com/android/camera/util/AccessibilityUtils.java create mode 100644 src/com/android/camera/util/ApiHelper.java create mode 100644 src/com/android/camera/util/CameraUtil.java create mode 100644 src/com/android/camera/util/MotionEventHelper.java delete mode 100644 src_pd/com/android/camera/app/StitchingProgressManager.java diff --git a/AndroidManifest.xml b/AndroidManifest.xml index 57209ee90..cd18989b4 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -1,3 +1,5 @@ + + - \ No newline at end of file + diff --git a/gallerycommon/src/com/android/gallery3d/common/ApiHelper.java b/gallerycommon/src/com/android/gallery3d/common/ApiHelper.java deleted file mode 100644 index f4de5c9ff..000000000 --- a/gallerycommon/src/com/android/gallery3d/common/ApiHelper.java +++ /dev/null @@ -1,234 +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.gallery3d.common; - -import android.app.admin.DevicePolicyManager; -import android.content.ComponentName; -import android.hardware.Camera; -import android.os.Build; -import android.provider.MediaStore.MediaColumns; -import android.view.View; -import android.view.WindowManager; - -import java.lang.reflect.Field; - -public class ApiHelper { - public static interface VERSION_CODES { - // These value are copied from Build.VERSION_CODES - public static final int GINGERBREAD_MR1 = 10; - public static final int HONEYCOMB = 11; - public static final int HONEYCOMB_MR1 = 12; - public static final int HONEYCOMB_MR2 = 13; - public static final int ICE_CREAM_SANDWICH = 14; - public static final int ICE_CREAM_SANDWICH_MR1 = 15; - public static final int JELLY_BEAN = 16; - public static final int JELLY_BEAN_MR1 = 17; - public static final int JELLY_BEAN_MR2 = 18; - } - - public static final boolean AT_LEAST_16 = Build.VERSION.SDK_INT >= 16; - - public static final boolean USE_888_PIXEL_FORMAT = - Build.VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN; - - public static final boolean ENABLE_PHOTO_EDITOR = - Build.VERSION.SDK_INT >= VERSION_CODES.ICE_CREAM_SANDWICH; - - public static final boolean HAS_VIEW_SYSTEM_UI_FLAG_LAYOUT_STABLE = - hasField(View.class, "SYSTEM_UI_FLAG_LAYOUT_STABLE"); - - public static final boolean HAS_VIEW_SYSTEM_UI_FLAG_HIDE_NAVIGATION = - hasField(View.class, "SYSTEM_UI_FLAG_HIDE_NAVIGATION"); - - public static final boolean HAS_MEDIA_COLUMNS_WIDTH_AND_HEIGHT = - hasField(MediaColumns.class, "WIDTH"); - - public static final boolean HAS_REUSING_BITMAP_IN_BITMAP_REGION_DECODER = - Build.VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN; - - public static final boolean HAS_REUSING_BITMAP_IN_BITMAP_FACTORY = - Build.VERSION.SDK_INT >= VERSION_CODES.HONEYCOMB; - - public static final boolean HAS_SET_BEAM_PUSH_URIS = - Build.VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN; - - public static final boolean HAS_SET_DEFALT_BUFFER_SIZE = hasMethod( - "android.graphics.SurfaceTexture", "setDefaultBufferSize", - int.class, int.class); - - public static final boolean HAS_RELEASE_SURFACE_TEXTURE = hasMethod( - "android.graphics.SurfaceTexture", "release"); - - public static final boolean HAS_SURFACE_TEXTURE = - Build.VERSION.SDK_INT >= VERSION_CODES.HONEYCOMB; - - public static final boolean HAS_MTP = - Build.VERSION.SDK_INT >= VERSION_CODES.HONEYCOMB_MR1; - - public static final boolean HAS_AUTO_FOCUS_MOVE_CALLBACK = - Build.VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN; - - public static final boolean HAS_REMOTE_VIEWS_SERVICE = - Build.VERSION.SDK_INT >= VERSION_CODES.HONEYCOMB; - - public static final boolean HAS_INTENT_EXTRA_LOCAL_ONLY = - Build.VERSION.SDK_INT >= VERSION_CODES.HONEYCOMB; - - public static final boolean HAS_SET_SYSTEM_UI_VISIBILITY = - hasMethod(View.class, "setSystemUiVisibility", int.class); - - public static final boolean HAS_FACE_DETECTION; - static { - boolean hasFaceDetection = false; - try { - Class listenerClass = Class.forName( - "android.hardware.Camera$FaceDetectionListener"); - hasFaceDetection = - hasMethod(Camera.class, "setFaceDetectionListener", listenerClass) && - hasMethod(Camera.class, "startFaceDetection") && - hasMethod(Camera.class, "stopFaceDetection") && - hasMethod(Camera.Parameters.class, "getMaxNumDetectedFaces"); - } catch (Throwable t) { - } - HAS_FACE_DETECTION = hasFaceDetection; - } - - public static final boolean HAS_GET_CAMERA_DISABLED = - hasMethod(DevicePolicyManager.class, "getCameraDisabled", ComponentName.class); - - public static final boolean HAS_MEDIA_ACTION_SOUND = - Build.VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN; - - public static final boolean HAS_TIME_LAPSE_RECORDING = - Build.VERSION.SDK_INT >= VERSION_CODES.HONEYCOMB; - - public static final boolean HAS_ZOOM_WHEN_RECORDING = - Build.VERSION.SDK_INT >= VERSION_CODES.ICE_CREAM_SANDWICH; - - public static final boolean HAS_CAMERA_FOCUS_AREA = - Build.VERSION.SDK_INT >= VERSION_CODES.ICE_CREAM_SANDWICH; - - public static final boolean HAS_CAMERA_METERING_AREA = - Build.VERSION.SDK_INT >= VERSION_CODES.ICE_CREAM_SANDWICH; - - public static final boolean HAS_MOTION_EVENT_TRANSFORM = - Build.VERSION.SDK_INT >= VERSION_CODES.HONEYCOMB; - - public static final boolean HAS_EFFECTS_RECORDING = false; - - // "Background" filter does not have "context" input port in jelly bean. - public static final boolean HAS_EFFECTS_RECORDING_CONTEXT_INPUT = - Build.VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN_MR1; - - public static final boolean HAS_GET_SUPPORTED_VIDEO_SIZE = - Build.VERSION.SDK_INT >= VERSION_CODES.HONEYCOMB; - - public static final boolean HAS_SET_ICON_ATTRIBUTE = - Build.VERSION.SDK_INT >= VERSION_CODES.HONEYCOMB; - - public static final boolean HAS_MEDIA_PROVIDER_FILES_TABLE = - Build.VERSION.SDK_INT >= VERSION_CODES.HONEYCOMB; - - public static final boolean HAS_SURFACE_TEXTURE_RECORDING = - Build.VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN; - - public static final boolean HAS_ACTION_BAR = - Build.VERSION.SDK_INT >= VERSION_CODES.HONEYCOMB; - - // Ex: View.setTranslationX. - public static final boolean HAS_VIEW_TRANSFORM_PROPERTIES = - Build.VERSION.SDK_INT >= VERSION_CODES.HONEYCOMB; - - public static final boolean HAS_CAMERA_HDR = - Build.VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN_MR1; - - public static final boolean HAS_OPTIONS_IN_MUTABLE = - Build.VERSION.SDK_INT >= VERSION_CODES.HONEYCOMB; - - public static final boolean CAN_START_PREVIEW_IN_JPEG_CALLBACK = - Build.VERSION.SDK_INT >= VERSION_CODES.ICE_CREAM_SANDWICH; - - public static final boolean HAS_VIEW_PROPERTY_ANIMATOR = - Build.VERSION.SDK_INT >= VERSION_CODES.HONEYCOMB_MR1; - - public static final boolean HAS_POST_ON_ANIMATION = - Build.VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN; - - public static final boolean HAS_ANNOUNCE_FOR_ACCESSIBILITY = - Build.VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN; - - public static final boolean HAS_OBJECT_ANIMATION = - Build.VERSION.SDK_INT >= VERSION_CODES.HONEYCOMB; - - public static final boolean HAS_GLES20_REQUIRED = - Build.VERSION.SDK_INT >= VERSION_CODES.HONEYCOMB; - - public static final boolean HAS_ROTATION_ANIMATION = - hasField(WindowManager.LayoutParams.class, "rotationAnimation"); - - public static final boolean HAS_ORIENTATION_LOCK = - Build.VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN_MR2; - - public static final boolean HAS_CANCELLATION_SIGNAL = - Build.VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN; - - public static final boolean HAS_MEDIA_MUXER = - Build.VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN_MR2; - - public static final boolean HAS_DISPLAY_LISTENER = - Build.VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN_MR1; - - public static int getIntFieldIfExists(Class klass, String fieldName, - Class obj, int defaultVal) { - try { - Field f = klass.getDeclaredField(fieldName); - return f.getInt(obj); - } catch (Exception e) { - return defaultVal; - } - } - - private static boolean hasField(Class klass, String fieldName) { - try { - klass.getDeclaredField(fieldName); - return true; - } catch (NoSuchFieldException e) { - return false; - } - } - - private static boolean hasMethod(String className, String methodName, - Class... parameterTypes) { - try { - Class klass = Class.forName(className); - klass.getDeclaredMethod(methodName, parameterTypes); - return true; - } catch (Throwable th) { - return false; - } - } - - private static boolean hasMethod( - Class klass, String methodName, Class ... paramTypes) { - try { - klass.getDeclaredMethod(methodName, paramTypes); - return true; - } catch (NoSuchMethodException e) { - return false; - } - } -} diff --git a/gallerycommon/src/com/android/gallery3d/common/AsyncTaskUtil.java b/gallerycommon/src/com/android/gallery3d/common/AsyncTaskUtil.java deleted file mode 100644 index b70c4d365..000000000 --- a/gallerycommon/src/com/android/gallery3d/common/AsyncTaskUtil.java +++ /dev/null @@ -1,66 +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.gallery3d.common; - -import android.os.AsyncTask; -import android.os.Build; - -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; -import java.util.concurrent.Executor; - -/** - * Helper class to execute an AsyncTask in parallel if SDK version is 11 or newer. - */ -public class AsyncTaskUtil { - private static Method sMethodExecuteOnExecutor; - private static Executor sExecutor; - static { - if (Build.VERSION.SDK_INT >= 11) { - try { - sExecutor = (Executor) AsyncTask.class.getField("THREAD_POOL_EXECUTOR") - .get(null); - sMethodExecuteOnExecutor = AsyncTask.class.getMethod( - "executeOnExecutor", Executor.class, Object[].class); - } catch (IllegalAccessException e) { - throw new RuntimeException(e); - } catch (NoSuchFieldException e) { - throw new RuntimeException(e); - } catch (NoSuchMethodException e) { - throw new RuntimeException(e); - } - } - } - - public static void executeInParallel(AsyncTask task, Param... params) { - if (Build.VERSION.SDK_INT < 11) { - task.execute(params); - } else { - try { - sMethodExecuteOnExecutor.invoke(task, sExecutor, params); - } catch (IllegalAccessException e) { - throw new RuntimeException(e); - } catch (InvocationTargetException e) { - throw new RuntimeException(e); - } - } - } - - private AsyncTaskUtil() { - } -} - diff --git a/gallerycommon/src/com/android/gallery3d/common/BitmapUtils.java b/gallerycommon/src/com/android/gallery3d/common/BitmapUtils.java deleted file mode 100644 index a671ed2b9..000000000 --- a/gallerycommon/src/com/android/gallery3d/common/BitmapUtils.java +++ /dev/null @@ -1,260 +0,0 @@ -/* - * Copyright (C) 2010 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.gallery3d.common; - -import android.graphics.Bitmap; -import android.graphics.Bitmap.CompressFormat; -import android.graphics.BitmapFactory; -import android.graphics.Canvas; -import android.graphics.Matrix; -import android.graphics.Paint; -import android.os.Build; -import android.util.FloatMath; -import android.util.Log; - -import java.io.ByteArrayOutputStream; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; - -public class BitmapUtils { - private static final String TAG = "BitmapUtils"; - private static final int DEFAULT_JPEG_QUALITY = 90; - public static final int UNCONSTRAINED = -1; - - private BitmapUtils(){} - - /* - * Compute the sample size as a function of minSideLength - * and maxNumOfPixels. - * minSideLength is used to specify that minimal width or height of a - * bitmap. - * maxNumOfPixels is used to specify the maximal size in pixels that is - * tolerable in terms of memory usage. - * - * The function returns a sample size based on the constraints. - * Both size and minSideLength can be passed in as UNCONSTRAINED, - * which indicates no care of the corresponding constraint. - * The functions prefers returning a sample size that - * generates a smaller bitmap, unless minSideLength = UNCONSTRAINED. - * - * Also, the function rounds up the sample size to a power of 2 or multiple - * of 8 because BitmapFactory only honors sample size this way. - * For example, BitmapFactory downsamples an image by 2 even though the - * request is 3. So we round up the sample size to avoid OOM. - */ - public static int computeSampleSize(int width, int height, - int minSideLength, int maxNumOfPixels) { - int initialSize = computeInitialSampleSize( - width, height, minSideLength, maxNumOfPixels); - - return initialSize <= 8 - ? Utils.nextPowerOf2(initialSize) - : (initialSize + 7) / 8 * 8; - } - - private static int computeInitialSampleSize(int w, int h, - int minSideLength, int maxNumOfPixels) { - if (maxNumOfPixels == UNCONSTRAINED - && minSideLength == UNCONSTRAINED) return 1; - - int lowerBound = (maxNumOfPixels == UNCONSTRAINED) ? 1 : - (int) FloatMath.ceil(FloatMath.sqrt((float) (w * h) / maxNumOfPixels)); - - if (minSideLength == UNCONSTRAINED) { - return lowerBound; - } else { - int sampleSize = Math.min(w / minSideLength, h / minSideLength); - return Math.max(sampleSize, lowerBound); - } - } - - // This computes a sample size which makes the longer side at least - // minSideLength long. If that's not possible, return 1. - public static int computeSampleSizeLarger(int w, int h, - int minSideLength) { - int initialSize = Math.max(w / minSideLength, h / minSideLength); - if (initialSize <= 1) return 1; - - return initialSize <= 8 - ? Utils.prevPowerOf2(initialSize) - : initialSize / 8 * 8; - } - - // Find the min x that 1 / x >= scale - public static int computeSampleSizeLarger(float scale) { - int initialSize = (int) FloatMath.floor(1f / scale); - if (initialSize <= 1) return 1; - - return initialSize <= 8 - ? Utils.prevPowerOf2(initialSize) - : initialSize / 8 * 8; - } - - // Find the max x that 1 / x <= scale. - public static int computeSampleSize(float scale) { - Utils.assertTrue(scale > 0); - int initialSize = Math.max(1, (int) FloatMath.ceil(1 / scale)); - return initialSize <= 8 - ? Utils.nextPowerOf2(initialSize) - : (initialSize + 7) / 8 * 8; - } - - public static Bitmap resizeBitmapByScale( - Bitmap bitmap, float scale, boolean recycle) { - int width = Math.round(bitmap.getWidth() * scale); - int height = Math.round(bitmap.getHeight() * scale); - if (width == bitmap.getWidth() - && height == bitmap.getHeight()) return bitmap; - Bitmap target = Bitmap.createBitmap(width, height, getConfig(bitmap)); - Canvas canvas = new Canvas(target); - canvas.scale(scale, scale); - Paint paint = new Paint(Paint.FILTER_BITMAP_FLAG | Paint.DITHER_FLAG); - canvas.drawBitmap(bitmap, 0, 0, paint); - if (recycle) bitmap.recycle(); - return target; - } - - private static Bitmap.Config getConfig(Bitmap bitmap) { - Bitmap.Config config = bitmap.getConfig(); - if (config == null) { - config = Bitmap.Config.ARGB_8888; - } - return config; - } - - public static Bitmap resizeDownBySideLength( - Bitmap bitmap, int maxLength, boolean recycle) { - int srcWidth = bitmap.getWidth(); - int srcHeight = bitmap.getHeight(); - float scale = Math.min( - (float) maxLength / srcWidth, (float) maxLength / srcHeight); - if (scale >= 1.0f) return bitmap; - return resizeBitmapByScale(bitmap, scale, recycle); - } - - public static Bitmap resizeAndCropCenter(Bitmap bitmap, int size, boolean recycle) { - int w = bitmap.getWidth(); - int h = bitmap.getHeight(); - if (w == size && h == size) return bitmap; - - // scale the image so that the shorter side equals to the target; - // the longer side will be center-cropped. - float scale = (float) size / Math.min(w, h); - - Bitmap target = Bitmap.createBitmap(size, size, getConfig(bitmap)); - int width = Math.round(scale * bitmap.getWidth()); - int height = Math.round(scale * bitmap.getHeight()); - Canvas canvas = new Canvas(target); - canvas.translate((size - width) / 2f, (size - height) / 2f); - canvas.scale(scale, scale); - Paint paint = new Paint(Paint.FILTER_BITMAP_FLAG | Paint.DITHER_FLAG); - canvas.drawBitmap(bitmap, 0, 0, paint); - if (recycle) bitmap.recycle(); - return target; - } - - public static void recycleSilently(Bitmap bitmap) { - if (bitmap == null) return; - try { - bitmap.recycle(); - } catch (Throwable t) { - Log.w(TAG, "unable recycle bitmap", t); - } - } - - public static Bitmap rotateBitmap(Bitmap source, int rotation, boolean recycle) { - if (rotation == 0) return source; - int w = source.getWidth(); - int h = source.getHeight(); - Matrix m = new Matrix(); - m.postRotate(rotation); - Bitmap bitmap = Bitmap.createBitmap(source, 0, 0, w, h, m, true); - if (recycle) source.recycle(); - return bitmap; - } - - public static Bitmap createVideoThumbnail(String filePath) { - // MediaMetadataRetriever is available on API Level 8 - // but is hidden until API Level 10 - Class clazz = null; - Object instance = null; - try { - clazz = Class.forName("android.media.MediaMetadataRetriever"); - instance = clazz.newInstance(); - - Method method = clazz.getMethod("setDataSource", String.class); - method.invoke(instance, filePath); - - // The method name changes between API Level 9 and 10. - if (Build.VERSION.SDK_INT <= 9) { - return (Bitmap) clazz.getMethod("captureFrame").invoke(instance); - } else { - byte[] data = (byte[]) clazz.getMethod("getEmbeddedPicture").invoke(instance); - if (data != null) { - Bitmap bitmap = BitmapFactory.decodeByteArray(data, 0, data.length); - if (bitmap != null) return bitmap; - } - return (Bitmap) clazz.getMethod("getFrameAtTime").invoke(instance); - } - } catch (IllegalArgumentException ex) { - // Assume this is a corrupt video file - } catch (RuntimeException ex) { - // Assume this is a corrupt video file. - } catch (InstantiationException e) { - Log.e(TAG, "createVideoThumbnail", e); - } catch (InvocationTargetException e) { - Log.e(TAG, "createVideoThumbnail", e); - } catch (ClassNotFoundException e) { - Log.e(TAG, "createVideoThumbnail", e); - } catch (NoSuchMethodException e) { - Log.e(TAG, "createVideoThumbnail", e); - } catch (IllegalAccessException e) { - Log.e(TAG, "createVideoThumbnail", e); - } finally { - try { - if (instance != null) { - clazz.getMethod("release").invoke(instance); - } - } catch (Exception ignored) { - } - } - return null; - } - - public static byte[] compressToBytes(Bitmap bitmap) { - return compressToBytes(bitmap, DEFAULT_JPEG_QUALITY); - } - - public static byte[] compressToBytes(Bitmap bitmap, int quality) { - ByteArrayOutputStream baos = new ByteArrayOutputStream(65536); - bitmap.compress(CompressFormat.JPEG, quality, baos); - return baos.toByteArray(); - } - - public static boolean isSupportedByRegionDecoder(String mimeType) { - if (mimeType == null) return false; - mimeType = mimeType.toLowerCase(); - return mimeType.startsWith("image/") && - (!mimeType.equals("image/gif") && !mimeType.endsWith("bmp")); - } - - public static boolean isRotationSupported(String mimeType) { - if (mimeType == null) return false; - mimeType = mimeType.toLowerCase(); - return mimeType.equals("image/jpeg"); - } -} diff --git a/gallerycommon/src/com/android/gallery3d/common/BlobCache.java b/gallerycommon/src/com/android/gallery3d/common/BlobCache.java deleted file mode 100644 index 3c131e591..000000000 --- a/gallerycommon/src/com/android/gallery3d/common/BlobCache.java +++ /dev/null @@ -1,668 +0,0 @@ -/* - * Copyright (C) 2010 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. - */ - -// This is an on-disk cache which maps a 64-bits key to a byte array. -// -// It consists of three files: one index file and two data files. One of the -// data files is "active", and the other is "inactive". New entries are -// appended into the active region until it reaches the size limit. At that -// point the active file and the inactive file are swapped, and the new active -// file is truncated to empty (and the index for that file is also cleared). -// The index is a hash table with linear probing. When the load factor reaches -// 0.5, it does the same thing like when the size limit is reached. -// -// The index file format: (all numbers are stored in little-endian) -// [0] Magic number: 0xB3273030 -// [4] MaxEntries: Max number of hash entries per region. -// [8] MaxBytes: Max number of data bytes per region (including header). -// [12] ActiveRegion: The active growing region: 0 or 1. -// [16] ActiveEntries: The number of hash entries used in the active region. -// [20] ActiveBytes: The number of data bytes used in the active region. -// [24] Version number. -// [28] Checksum of [0..28). -// [32] Hash entries for region 0. The size is X = (12 * MaxEntries bytes). -// [32 + X] Hash entries for region 1. The size is also X. -// -// Each hash entry is 12 bytes: 8 bytes key and 4 bytes offset into the data -// file. The offset is 0 when the slot is free. Note that 0 is a valid value -// for key. The keys are used directly as index into a hash table, so they -// should be suitably distributed. -// -// Each data file stores data for one region. The data file is concatenated -// blobs followed by the magic number 0xBD248510. -// -// The blob format: -// [0] Key of this blob -// [8] Checksum of this blob -// [12] Offset of this blob -// [16] Length of this blob (not including header) -// [20] Blob -// -// Below are the interface for BlobCache. The instance of this class does not -// support concurrent use by multiple threads. -// -// public BlobCache(String path, int maxEntries, int maxBytes, boolean reset) throws IOException; -// public void insert(long key, byte[] data) throws IOException; -// public byte[] lookup(long key) throws IOException; -// public void lookup(LookupRequest req) throws IOException; -// public void close(); -// public void syncIndex(); -// public void syncAll(); -// public static void deleteFiles(String path); -// -package com.android.gallery3d.common; - -import android.util.Log; - -import java.io.Closeable; -import java.io.File; -import java.io.IOException; -import java.io.RandomAccessFile; -import java.nio.ByteOrder; -import java.nio.MappedByteBuffer; -import java.nio.channels.FileChannel; -import java.util.Arrays; -import java.util.zip.Adler32; - -public class BlobCache implements Closeable { - private static final String TAG = "BlobCache"; - - private static final int MAGIC_INDEX_FILE = 0xB3273030; - private static final int MAGIC_DATA_FILE = 0xBD248510; - - // index header offset - private static final int IH_MAGIC = 0; - private static final int IH_MAX_ENTRIES = 4; - private static final int IH_MAX_BYTES = 8; - private static final int IH_ACTIVE_REGION = 12; - private static final int IH_ACTIVE_ENTRIES = 16; - private static final int IH_ACTIVE_BYTES = 20; - private static final int IH_VERSION = 24; - private static final int IH_CHECKSUM = 28; - private static final int INDEX_HEADER_SIZE = 32; - - private static final int DATA_HEADER_SIZE = 4; - - // blob header offset - private static final int BH_KEY = 0; - private static final int BH_CHECKSUM = 8; - private static final int BH_OFFSET = 12; - private static final int BH_LENGTH = 16; - private static final int BLOB_HEADER_SIZE = 20; - - private RandomAccessFile mIndexFile; - private RandomAccessFile mDataFile0; - private RandomAccessFile mDataFile1; - private FileChannel mIndexChannel; - private MappedByteBuffer mIndexBuffer; - - private int mMaxEntries; - private int mMaxBytes; - private int mActiveRegion; - private int mActiveEntries; - private int mActiveBytes; - private int mVersion; - - private RandomAccessFile mActiveDataFile; - private RandomAccessFile mInactiveDataFile; - private int mActiveHashStart; - private int mInactiveHashStart; - private byte[] mIndexHeader = new byte[INDEX_HEADER_SIZE]; - private byte[] mBlobHeader = new byte[BLOB_HEADER_SIZE]; - private Adler32 mAdler32 = new Adler32(); - - // Creates the cache. Three files will be created: - // path + ".idx", path + ".0", and path + ".1" - // The ".0" file and the ".1" file each stores data for a region. Each of - // them can grow to the size specified by maxBytes. The maxEntries parameter - // specifies the maximum number of entries each region can have. If the - // "reset" parameter is true, the cache will be cleared before use. - public BlobCache(String path, int maxEntries, int maxBytes, boolean reset) - throws IOException { - this(path, maxEntries, maxBytes, reset, 0); - } - - public BlobCache(String path, int maxEntries, int maxBytes, boolean reset, - int version) throws IOException { - mIndexFile = new RandomAccessFile(path + ".idx", "rw"); - mDataFile0 = new RandomAccessFile(path + ".0", "rw"); - mDataFile1 = new RandomAccessFile(path + ".1", "rw"); - mVersion = version; - - if (!reset && loadIndex()) { - return; - } - - resetCache(maxEntries, maxBytes); - - if (!loadIndex()) { - closeAll(); - throw new IOException("unable to load index"); - } - } - - // Delete the files associated with the given path previously created - // by the BlobCache constructor. - public static void deleteFiles(String path) { - deleteFileSilently(path + ".idx"); - deleteFileSilently(path + ".0"); - deleteFileSilently(path + ".1"); - } - - private static void deleteFileSilently(String path) { - try { - new File(path).delete(); - } catch (Throwable t) { - // ignore; - } - } - - // Close the cache. All resources are released. No other method should be - // called after this is called. - @Override - public void close() { - syncAll(); - closeAll(); - } - - private void closeAll() { - closeSilently(mIndexChannel); - closeSilently(mIndexFile); - closeSilently(mDataFile0); - closeSilently(mDataFile1); - } - - // Returns true if loading index is successful. After this method is called, - // mIndexHeader and index header in file should be kept sync. - private boolean loadIndex() { - try { - mIndexFile.seek(0); - mDataFile0.seek(0); - mDataFile1.seek(0); - - byte[] buf = mIndexHeader; - if (mIndexFile.read(buf) != INDEX_HEADER_SIZE) { - Log.w(TAG, "cannot read header"); - return false; - } - - if (readInt(buf, IH_MAGIC) != MAGIC_INDEX_FILE) { - Log.w(TAG, "cannot read header magic"); - return false; - } - - if (readInt(buf, IH_VERSION) != mVersion) { - Log.w(TAG, "version mismatch"); - return false; - } - - mMaxEntries = readInt(buf, IH_MAX_ENTRIES); - mMaxBytes = readInt(buf, IH_MAX_BYTES); - mActiveRegion = readInt(buf, IH_ACTIVE_REGION); - mActiveEntries = readInt(buf, IH_ACTIVE_ENTRIES); - mActiveBytes = readInt(buf, IH_ACTIVE_BYTES); - - int sum = readInt(buf, IH_CHECKSUM); - if (checkSum(buf, 0, IH_CHECKSUM) != sum) { - Log.w(TAG, "header checksum does not match"); - return false; - } - - // Sanity check - if (mMaxEntries <= 0) { - Log.w(TAG, "invalid max entries"); - return false; - } - if (mMaxBytes <= 0) { - Log.w(TAG, "invalid max bytes"); - return false; - } - if (mActiveRegion != 0 && mActiveRegion != 1) { - Log.w(TAG, "invalid active region"); - return false; - } - if (mActiveEntries < 0 || mActiveEntries > mMaxEntries) { - Log.w(TAG, "invalid active entries"); - return false; - } - if (mActiveBytes < DATA_HEADER_SIZE || mActiveBytes > mMaxBytes) { - Log.w(TAG, "invalid active bytes"); - return false; - } - if (mIndexFile.length() != - INDEX_HEADER_SIZE + mMaxEntries * 12 * 2) { - Log.w(TAG, "invalid index file length"); - return false; - } - - // Make sure data file has magic - byte[] magic = new byte[4]; - if (mDataFile0.read(magic) != 4) { - Log.w(TAG, "cannot read data file magic"); - return false; - } - if (readInt(magic, 0) != MAGIC_DATA_FILE) { - Log.w(TAG, "invalid data file magic"); - return false; - } - if (mDataFile1.read(magic) != 4) { - Log.w(TAG, "cannot read data file magic"); - return false; - } - if (readInt(magic, 0) != MAGIC_DATA_FILE) { - Log.w(TAG, "invalid data file magic"); - return false; - } - - // Map index file to memory - mIndexChannel = mIndexFile.getChannel(); - mIndexBuffer = mIndexChannel.map(FileChannel.MapMode.READ_WRITE, - 0, mIndexFile.length()); - mIndexBuffer.order(ByteOrder.LITTLE_ENDIAN); - - setActiveVariables(); - return true; - } catch (IOException ex) { - Log.e(TAG, "loadIndex failed.", ex); - return false; - } - } - - private void setActiveVariables() throws IOException { - mActiveDataFile = (mActiveRegion == 0) ? mDataFile0 : mDataFile1; - mInactiveDataFile = (mActiveRegion == 1) ? mDataFile0 : mDataFile1; - mActiveDataFile.setLength(mActiveBytes); - mActiveDataFile.seek(mActiveBytes); - - mActiveHashStart = INDEX_HEADER_SIZE; - mInactiveHashStart = INDEX_HEADER_SIZE; - - if (mActiveRegion == 0) { - mInactiveHashStart += mMaxEntries * 12; - } else { - mActiveHashStart += mMaxEntries * 12; - } - } - - private void resetCache(int maxEntries, int maxBytes) throws IOException { - mIndexFile.setLength(0); // truncate to zero the index - mIndexFile.setLength(INDEX_HEADER_SIZE + maxEntries * 12 * 2); - mIndexFile.seek(0); - byte[] buf = mIndexHeader; - writeInt(buf, IH_MAGIC, MAGIC_INDEX_FILE); - writeInt(buf, IH_MAX_ENTRIES, maxEntries); - writeInt(buf, IH_MAX_BYTES, maxBytes); - writeInt(buf, IH_ACTIVE_REGION, 0); - writeInt(buf, IH_ACTIVE_ENTRIES, 0); - writeInt(buf, IH_ACTIVE_BYTES, DATA_HEADER_SIZE); - writeInt(buf, IH_VERSION, mVersion); - writeInt(buf, IH_CHECKSUM, checkSum(buf, 0, IH_CHECKSUM)); - mIndexFile.write(buf); - // This is only needed if setLength does not zero the extended part. - // writeZero(mIndexFile, maxEntries * 12 * 2); - - mDataFile0.setLength(0); - mDataFile1.setLength(0); - mDataFile0.seek(0); - mDataFile1.seek(0); - writeInt(buf, 0, MAGIC_DATA_FILE); - mDataFile0.write(buf, 0, 4); - mDataFile1.write(buf, 0, 4); - } - - // Flip the active region and the inactive region. - private void flipRegion() throws IOException { - mActiveRegion = 1 - mActiveRegion; - mActiveEntries = 0; - mActiveBytes = DATA_HEADER_SIZE; - - writeInt(mIndexHeader, IH_ACTIVE_REGION, mActiveRegion); - writeInt(mIndexHeader, IH_ACTIVE_ENTRIES, mActiveEntries); - writeInt(mIndexHeader, IH_ACTIVE_BYTES, mActiveBytes); - updateIndexHeader(); - - setActiveVariables(); - clearHash(mActiveHashStart); - syncIndex(); - } - - // Sync mIndexHeader to the index file. - private void updateIndexHeader() { - writeInt(mIndexHeader, IH_CHECKSUM, - checkSum(mIndexHeader, 0, IH_CHECKSUM)); - mIndexBuffer.position(0); - mIndexBuffer.put(mIndexHeader); - } - - // Clear the hash table starting from the specified offset. - private void clearHash(int hashStart) { - byte[] zero = new byte[1024]; - mIndexBuffer.position(hashStart); - for (int count = mMaxEntries * 12; count > 0;) { - int todo = Math.min(count, 1024); - mIndexBuffer.put(zero, 0, todo); - count -= todo; - } - } - - // Inserts a (key, data) pair into the cache. - public void insert(long key, byte[] data) throws IOException { - if (DATA_HEADER_SIZE + BLOB_HEADER_SIZE + data.length > mMaxBytes) { - throw new RuntimeException("blob is too large!"); - } - - if (mActiveBytes + BLOB_HEADER_SIZE + data.length > mMaxBytes - || mActiveEntries * 2 >= mMaxEntries) { - flipRegion(); - } - - if (!lookupInternal(key, mActiveHashStart)) { - // If we don't have an existing entry with the same key, increase - // the entry count. - mActiveEntries++; - writeInt(mIndexHeader, IH_ACTIVE_ENTRIES, mActiveEntries); - } - - insertInternal(key, data, data.length); - updateIndexHeader(); - } - - public void clearEntry(long key) throws IOException { - if (!lookupInternal(key, mActiveHashStart)) { - return; // Nothing to clear - } - byte[] header = mBlobHeader; - Arrays.fill(header, (byte) 0); - mActiveDataFile.seek(mFileOffset); - mActiveDataFile.write(header); - } - - // Appends the data to the active file. It also updates the hash entry. - // The proper hash entry (suitable for insertion or replacement) must be - // pointed by mSlotOffset. - private void insertInternal(long key, byte[] data, int length) - throws IOException { - byte[] header = mBlobHeader; - int sum = checkSum(data); - writeLong(header, BH_KEY, key); - writeInt(header, BH_CHECKSUM, sum); - writeInt(header, BH_OFFSET, mActiveBytes); - writeInt(header, BH_LENGTH, length); - mActiveDataFile.write(header); - mActiveDataFile.write(data, 0, length); - - mIndexBuffer.putLong(mSlotOffset, key); - mIndexBuffer.putInt(mSlotOffset + 8, mActiveBytes); - mActiveBytes += BLOB_HEADER_SIZE + length; - writeInt(mIndexHeader, IH_ACTIVE_BYTES, mActiveBytes); - } - - public static class LookupRequest { - public long key; // input: the key to find - public byte[] buffer; // input/output: the buffer to store the blob - public int length; // output: the length of the blob - } - - // This method is for one-off lookup. For repeated lookup, use the version - // accepting LookupRequest to avoid repeated memory allocation. - private LookupRequest mLookupRequest = new LookupRequest(); - public byte[] lookup(long key) throws IOException { - mLookupRequest.key = key; - mLookupRequest.buffer = null; - if (lookup(mLookupRequest)) { - return mLookupRequest.buffer; - } else { - return null; - } - } - - // Returns true if the associated blob for the given key is available. - // The blob is stored in the buffer pointed by req.buffer, and the length - // is in stored in the req.length variable. - // - // The user can input a non-null value in req.buffer, and this method will - // try to use that buffer. If that buffer is not large enough, this method - // will allocate a new buffer and assign it to req.buffer. - // - // This method tries not to throw IOException even if the data file is - // corrupted, but it can still throw IOException if things get strange. - public boolean lookup(LookupRequest req) throws IOException { - // Look up in the active region first. - if (lookupInternal(req.key, mActiveHashStart)) { - if (getBlob(mActiveDataFile, mFileOffset, req)) { - return true; - } - } - - // We want to copy the data from the inactive file to the active file - // if it's available. So we keep the offset of the hash entry so we can - // avoid looking it up again. - int insertOffset = mSlotOffset; - - // Look up in the inactive region. - if (lookupInternal(req.key, mInactiveHashStart)) { - if (getBlob(mInactiveDataFile, mFileOffset, req)) { - // If we don't have enough space to insert this blob into - // the active file, just return it. - if (mActiveBytes + BLOB_HEADER_SIZE + req.length > mMaxBytes - || mActiveEntries * 2 >= mMaxEntries) { - return true; - } - // Otherwise copy it over. - mSlotOffset = insertOffset; - try { - insertInternal(req.key, req.buffer, req.length); - mActiveEntries++; - writeInt(mIndexHeader, IH_ACTIVE_ENTRIES, mActiveEntries); - updateIndexHeader(); - } catch (Throwable t) { - Log.e(TAG, "cannot copy over"); - } - return true; - } - } - - return false; - } - - - // Copies the blob for the specified offset in the specified file to - // req.buffer. If req.buffer is null or too small, allocate a buffer and - // assign it to req.buffer. - // Returns false if the blob is not available (either the index file is - // not sync with the data file, or one of them is corrupted). The length - // of the blob is stored in the req.length variable. - private boolean getBlob(RandomAccessFile file, int offset, - LookupRequest req) throws IOException { - byte[] header = mBlobHeader; - long oldPosition = file.getFilePointer(); - try { - file.seek(offset); - if (file.read(header) != BLOB_HEADER_SIZE) { - Log.w(TAG, "cannot read blob header"); - return false; - } - long blobKey = readLong(header, BH_KEY); - if (blobKey == 0) { - return false; // This entry has been cleared. - } - if (blobKey != req.key) { - Log.w(TAG, "blob key does not match: " + blobKey); - return false; - } - int sum = readInt(header, BH_CHECKSUM); - int blobOffset = readInt(header, BH_OFFSET); - if (blobOffset != offset) { - Log.w(TAG, "blob offset does not match: " + blobOffset); - return false; - } - int length = readInt(header, BH_LENGTH); - if (length < 0 || length > mMaxBytes - offset - BLOB_HEADER_SIZE) { - Log.w(TAG, "invalid blob length: " + length); - return false; - } - if (req.buffer == null || req.buffer.length < length) { - req.buffer = new byte[length]; - } - - byte[] blob = req.buffer; - req.length = length; - - if (file.read(blob, 0, length) != length) { - Log.w(TAG, "cannot read blob data"); - return false; - } - if (checkSum(blob, 0, length) != sum) { - Log.w(TAG, "blob checksum does not match: " + sum); - return false; - } - return true; - } catch (Throwable t) { - Log.e(TAG, "getBlob failed.", t); - return false; - } finally { - file.seek(oldPosition); - } - } - - // Tries to look up a key in the specified hash region. - // Returns true if the lookup is successful. - // The slot offset in the index file is saved in mSlotOffset. If the lookup - // is successful, it's the slot found. Otherwise it's the slot suitable for - // insertion. - // If the lookup is successful, the file offset is also saved in - // mFileOffset. - private int mSlotOffset; - private int mFileOffset; - private boolean lookupInternal(long key, int hashStart) { - int slot = (int) (key % mMaxEntries); - if (slot < 0) slot += mMaxEntries; - int slotBegin = slot; - while (true) { - int offset = hashStart + slot * 12; - long candidateKey = mIndexBuffer.getLong(offset); - int candidateOffset = mIndexBuffer.getInt(offset + 8); - if (candidateOffset == 0) { - mSlotOffset = offset; - return false; - } else if (candidateKey == key) { - mSlotOffset = offset; - mFileOffset = candidateOffset; - return true; - } else { - if (++slot >= mMaxEntries) { - slot = 0; - } - if (slot == slotBegin) { - Log.w(TAG, "corrupted index: clear the slot."); - mIndexBuffer.putInt(hashStart + slot * 12 + 8, 0); - } - } - } - } - - public void syncIndex() { - try { - mIndexBuffer.force(); - } catch (Throwable t) { - Log.w(TAG, "sync index failed", t); - } - } - - public void syncAll() { - syncIndex(); - try { - mDataFile0.getFD().sync(); - } catch (Throwable t) { - Log.w(TAG, "sync data file 0 failed", t); - } - try { - mDataFile1.getFD().sync(); - } catch (Throwable t) { - Log.w(TAG, "sync data file 1 failed", t); - } - } - - // This is for testing only. - // - // Returns the active count (mActiveEntries). This also verifies that - // the active count matches matches what's inside the hash region. - int getActiveCount() { - int count = 0; - for (int i = 0; i < mMaxEntries; i++) { - int offset = mActiveHashStart + i * 12; - long candidateKey = mIndexBuffer.getLong(offset); - int candidateOffset = mIndexBuffer.getInt(offset + 8); - if (candidateOffset != 0) ++count; - } - if (count == mActiveEntries) { - return count; - } else { - Log.e(TAG, "wrong active count: " + mActiveEntries + " vs " + count); - return -1; // signal failure. - } - } - - int checkSum(byte[] data) { - mAdler32.reset(); - mAdler32.update(data); - return (int) mAdler32.getValue(); - } - - int checkSum(byte[] data, int offset, int nbytes) { - mAdler32.reset(); - mAdler32.update(data, offset, nbytes); - return (int) mAdler32.getValue(); - } - - static void closeSilently(Closeable c) { - if (c == null) return; - try { - c.close(); - } catch (Throwable t) { - // do nothing - } - } - - static int readInt(byte[] buf, int offset) { - return (buf[offset] & 0xff) - | ((buf[offset + 1] & 0xff) << 8) - | ((buf[offset + 2] & 0xff) << 16) - | ((buf[offset + 3] & 0xff) << 24); - } - - static long readLong(byte[] buf, int offset) { - long result = buf[offset + 7] & 0xff; - for (int i = 6; i >= 0; i--) { - result = (result << 8) | (buf[offset + i] & 0xff); - } - return result; - } - - static void writeInt(byte[] buf, int offset, int value) { - for (int i = 0; i < 4; i++) { - buf[offset + i] = (byte) (value & 0xff); - value >>= 8; - } - } - - static void writeLong(byte[] buf, int offset, long value) { - for (int i = 0; i < 8; i++) { - buf[offset + i] = (byte) (value & 0xff); - value >>= 8; - } - } -} diff --git a/gallerycommon/src/com/android/gallery3d/common/Entry.java b/gallerycommon/src/com/android/gallery3d/common/Entry.java deleted file mode 100644 index 3f1644e65..000000000 --- a/gallerycommon/src/com/android/gallery3d/common/Entry.java +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright (C) 2009 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.gallery3d.common; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -public abstract class Entry { - public static final String[] ID_PROJECTION = { "_id" }; - - public static interface Columns { - public static final String ID = "_id"; - } - - // The primary key of the entry. - @Column("_id") - public long id = 0; - - @Retention(RetentionPolicy.RUNTIME) - @Target(ElementType.TYPE) - public @interface Table { - String value(); - } - - @Retention(RetentionPolicy.RUNTIME) - @Target(ElementType.FIELD) - public @interface Column { - String value(); - - boolean indexed() default false; - - boolean fullText() default false; - - String defaultValue() default ""; - - boolean unique() default false; - } - - public void clear() { - id = 0; - } -} diff --git a/gallerycommon/src/com/android/gallery3d/common/EntrySchema.java b/gallerycommon/src/com/android/gallery3d/common/EntrySchema.java deleted file mode 100644 index 7bf7e431c..000000000 --- a/gallerycommon/src/com/android/gallery3d/common/EntrySchema.java +++ /dev/null @@ -1,542 +0,0 @@ -/* - * Copyright (C) 2009 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.gallery3d.common; - -import android.content.ContentValues; -import android.database.Cursor; -import android.database.sqlite.SQLiteDatabase; -import android.text.TextUtils; - -import java.lang.reflect.AnnotatedElement; -import java.lang.reflect.Field; -import java.util.ArrayList; - -public final class EntrySchema { - @SuppressWarnings("unused") - private static final String TAG = "EntrySchema"; - - public static final int TYPE_STRING = 0; - public static final int TYPE_BOOLEAN = 1; - public static final int TYPE_SHORT = 2; - public static final int TYPE_INT = 3; - public static final int TYPE_LONG = 4; - public static final int TYPE_FLOAT = 5; - public static final int TYPE_DOUBLE = 6; - public static final int TYPE_BLOB = 7; - private static final String SQLITE_TYPES[] = { - "TEXT", "INTEGER", "INTEGER", "INTEGER", "INTEGER", "REAL", "REAL", "NONE" }; - - private static final String FULL_TEXT_INDEX_SUFFIX = "_fulltext"; - - private final String mTableName; - private final ColumnInfo[] mColumnInfo; - private final String[] mProjection; - private final boolean mHasFullTextIndex; - - public EntrySchema(Class clazz) { - // Get table and column metadata from reflection. - ColumnInfo[] columns = parseColumnInfo(clazz); - mTableName = parseTableName(clazz); - mColumnInfo = columns; - - // Cache the list of projection columns and check for full-text columns. - String[] projection = {}; - boolean hasFullTextIndex = false; - if (columns != null) { - projection = new String[columns.length]; - for (int i = 0; i != columns.length; ++i) { - ColumnInfo column = columns[i]; - projection[i] = column.name; - if (column.fullText) { - hasFullTextIndex = true; - } - } - } - mProjection = projection; - mHasFullTextIndex = hasFullTextIndex; - } - - public String getTableName() { - return mTableName; - } - - public ColumnInfo[] getColumnInfo() { - return mColumnInfo; - } - - public String[] getProjection() { - return mProjection; - } - - public int getColumnIndex(String columnName) { - for (ColumnInfo column : mColumnInfo) { - if (column.name.equals(columnName)) { - return column.projectionIndex; - } - } - return -1; - } - - public ColumnInfo getColumn(String columnName) { - int index = getColumnIndex(columnName); - return (index < 0) ? null : mColumnInfo[index]; - } - - private void logExecSql(SQLiteDatabase db, String sql) { - db.execSQL(sql); - } - - public T cursorToObject(Cursor cursor, T object) { - try { - for (ColumnInfo column : mColumnInfo) { - int columnIndex = column.projectionIndex; - Field field = column.field; - switch (column.type) { - case TYPE_STRING: - field.set(object, cursor.isNull(columnIndex) - ? null - : cursor.getString(columnIndex)); - break; - case TYPE_BOOLEAN: - field.setBoolean(object, cursor.getShort(columnIndex) == 1); - break; - case TYPE_SHORT: - field.setShort(object, cursor.getShort(columnIndex)); - break; - case TYPE_INT: - field.setInt(object, cursor.getInt(columnIndex)); - break; - case TYPE_LONG: - field.setLong(object, cursor.getLong(columnIndex)); - break; - case TYPE_FLOAT: - field.setFloat(object, cursor.getFloat(columnIndex)); - break; - case TYPE_DOUBLE: - field.setDouble(object, cursor.getDouble(columnIndex)); - break; - case TYPE_BLOB: - field.set(object, cursor.isNull(columnIndex) - ? null - : cursor.getBlob(columnIndex)); - break; - } - } - return object; - } catch (IllegalAccessException e) { - throw new RuntimeException(e); - } - } - - private void setIfNotNull(Field field, Object object, Object value) - throws IllegalAccessException { - if (value != null) field.set(object, value); - } - - /** - * Converts the ContentValues to the object. The ContentValues may not - * contain values for all the fields in the object. - */ - public T valuesToObject(ContentValues values, T object) { - try { - for (ColumnInfo column : mColumnInfo) { - String columnName = column.name; - Field field = column.field; - switch (column.type) { - case TYPE_STRING: - setIfNotNull(field, object, values.getAsString(columnName)); - break; - case TYPE_BOOLEAN: - setIfNotNull(field, object, values.getAsBoolean(columnName)); - break; - case TYPE_SHORT: - setIfNotNull(field, object, values.getAsShort(columnName)); - break; - case TYPE_INT: - setIfNotNull(field, object, values.getAsInteger(columnName)); - break; - case TYPE_LONG: - setIfNotNull(field, object, values.getAsLong(columnName)); - break; - case TYPE_FLOAT: - setIfNotNull(field, object, values.getAsFloat(columnName)); - break; - case TYPE_DOUBLE: - setIfNotNull(field, object, values.getAsDouble(columnName)); - break; - case TYPE_BLOB: - setIfNotNull(field, object, values.getAsByteArray(columnName)); - break; - } - } - return object; - } catch (IllegalAccessException e) { - throw new RuntimeException(e); - } - } - - public void objectToValues(Entry object, ContentValues values) { - try { - for (ColumnInfo column : mColumnInfo) { - String columnName = column.name; - Field field = column.field; - switch (column.type) { - case TYPE_STRING: - values.put(columnName, (String) field.get(object)); - break; - case TYPE_BOOLEAN: - values.put(columnName, field.getBoolean(object)); - break; - case TYPE_SHORT: - values.put(columnName, field.getShort(object)); - break; - case TYPE_INT: - values.put(columnName, field.getInt(object)); - break; - case TYPE_LONG: - values.put(columnName, field.getLong(object)); - break; - case TYPE_FLOAT: - values.put(columnName, field.getFloat(object)); - break; - case TYPE_DOUBLE: - values.put(columnName, field.getDouble(object)); - break; - case TYPE_BLOB: - values.put(columnName, (byte[]) field.get(object)); - break; - } - } - } catch (IllegalAccessException e) { - throw new RuntimeException(e); - } - } - - public String toDebugString(Entry entry) { - try { - StringBuilder sb = new StringBuilder(); - sb.append("ID=").append(entry.id); - for (ColumnInfo column : mColumnInfo) { - String columnName = column.name; - Field field = column.field; - Object value = field.get(entry); - sb.append(" ").append(columnName).append("=") - .append((value == null) ? "null" : value.toString()); - } - return sb.toString(); - } catch (IllegalAccessException e) { - throw new RuntimeException(e); - } - } - - public String toDebugString(Entry entry, String... columnNames) { - try { - StringBuilder sb = new StringBuilder(); - sb.append("ID=").append(entry.id); - for (String columnName : columnNames) { - ColumnInfo column = getColumn(columnName); - Field field = column.field; - Object value = field.get(entry); - sb.append(" ").append(columnName).append("=") - .append((value == null) ? "null" : value.toString()); - } - return sb.toString(); - } catch (IllegalAccessException e) { - throw new RuntimeException(e); - } - } - - public Cursor queryAll(SQLiteDatabase db) { - return db.query(mTableName, mProjection, null, null, null, null, null); - } - - public boolean queryWithId(SQLiteDatabase db, long id, Entry entry) { - Cursor cursor = db.query(mTableName, mProjection, "_id=?", - new String[] {Long.toString(id)}, null, null, null); - boolean success = false; - if (cursor.moveToFirst()) { - cursorToObject(cursor, entry); - success = true; - } - cursor.close(); - return success; - } - - public long insertOrReplace(SQLiteDatabase db, Entry entry) { - ContentValues values = new ContentValues(); - objectToValues(entry, values); - if (entry.id == 0) { - values.remove("_id"); - } - long id = db.replace(mTableName, "_id", values); - entry.id = id; - return id; - } - - public boolean deleteWithId(SQLiteDatabase db, long id) { - return db.delete(mTableName, "_id=?", new String[] { Long.toString(id) }) == 1; - } - - public void createTables(SQLiteDatabase db) { - // Wrapped class must have a @Table.Definition. - String tableName = mTableName; - Utils.assertTrue(tableName != null); - - // Add the CREATE TABLE statement for the main table. - StringBuilder sql = new StringBuilder("CREATE TABLE "); - sql.append(tableName); - sql.append(" (_id INTEGER PRIMARY KEY AUTOINCREMENT"); - StringBuilder unique = new StringBuilder(); - for (ColumnInfo column : mColumnInfo) { - if (!column.isId()) { - sql.append(','); - sql.append(column.name); - sql.append(' '); - sql.append(SQLITE_TYPES[column.type]); - if (!TextUtils.isEmpty(column.defaultValue)) { - sql.append(" DEFAULT "); - sql.append(column.defaultValue); - } - if (column.unique) { - if (unique.length() == 0) { - unique.append(column.name); - } else { - unique.append(',').append(column.name); - } - } - } - } - if (unique.length() > 0) { - sql.append(",UNIQUE(").append(unique).append(')'); - } - sql.append(");"); - logExecSql(db, sql.toString()); - sql.setLength(0); - - // Create indexes for all indexed columns. - for (ColumnInfo column : mColumnInfo) { - // Create an index on the indexed columns. - if (column.indexed) { - sql.append("CREATE INDEX "); - sql.append(tableName); - sql.append("_index_"); - sql.append(column.name); - sql.append(" ON "); - sql.append(tableName); - sql.append(" ("); - sql.append(column.name); - sql.append(");"); - logExecSql(db, sql.toString()); - sql.setLength(0); - } - } - - if (mHasFullTextIndex) { - // Add an FTS virtual table if using full-text search. - String ftsTableName = tableName + FULL_TEXT_INDEX_SUFFIX; - sql.append("CREATE VIRTUAL TABLE "); - sql.append(ftsTableName); - sql.append(" USING FTS3 (_id INTEGER PRIMARY KEY"); - for (ColumnInfo column : mColumnInfo) { - if (column.fullText) { - // Add the column to the FTS table. - String columnName = column.name; - sql.append(','); - sql.append(columnName); - sql.append(" TEXT"); - } - } - sql.append(");"); - logExecSql(db, sql.toString()); - sql.setLength(0); - - // Build an insert statement that will automatically keep the FTS - // table in sync. - StringBuilder insertSql = new StringBuilder("INSERT OR REPLACE INTO "); - insertSql.append(ftsTableName); - insertSql.append(" (_id"); - for (ColumnInfo column : mColumnInfo) { - if (column.fullText) { - insertSql.append(','); - insertSql.append(column.name); - } - } - insertSql.append(") VALUES (new._id"); - for (ColumnInfo column : mColumnInfo) { - if (column.fullText) { - insertSql.append(",new."); - insertSql.append(column.name); - } - } - insertSql.append(");"); - String insertSqlString = insertSql.toString(); - - // Add an insert trigger. - sql.append("CREATE TRIGGER "); - sql.append(tableName); - sql.append("_insert_trigger AFTER INSERT ON "); - sql.append(tableName); - sql.append(" FOR EACH ROW BEGIN "); - sql.append(insertSqlString); - sql.append("END;"); - logExecSql(db, sql.toString()); - sql.setLength(0); - - // Add an update trigger. - sql.append("CREATE TRIGGER "); - sql.append(tableName); - sql.append("_update_trigger AFTER UPDATE ON "); - sql.append(tableName); - sql.append(" FOR EACH ROW BEGIN "); - sql.append(insertSqlString); - sql.append("END;"); - logExecSql(db, sql.toString()); - sql.setLength(0); - - // Add a delete trigger. - sql.append("CREATE TRIGGER "); - sql.append(tableName); - sql.append("_delete_trigger AFTER DELETE ON "); - sql.append(tableName); - sql.append(" FOR EACH ROW BEGIN DELETE FROM "); - sql.append(ftsTableName); - sql.append(" WHERE _id = old._id; END;"); - logExecSql(db, sql.toString()); - sql.setLength(0); - } - } - - public void dropTables(SQLiteDatabase db) { - String tableName = mTableName; - StringBuilder sql = new StringBuilder("DROP TABLE IF EXISTS "); - sql.append(tableName); - sql.append(';'); - logExecSql(db, sql.toString()); - sql.setLength(0); - - if (mHasFullTextIndex) { - sql.append("DROP TABLE IF EXISTS "); - sql.append(tableName); - sql.append(FULL_TEXT_INDEX_SUFFIX); - sql.append(';'); - logExecSql(db, sql.toString()); - } - - } - - public void deleteAll(SQLiteDatabase db) { - StringBuilder sql = new StringBuilder("DELETE FROM "); - sql.append(mTableName); - sql.append(";"); - logExecSql(db, sql.toString()); - } - - private String parseTableName(Class clazz) { - // Check for a table annotation. - Entry.Table table = clazz.getAnnotation(Entry.Table.class); - if (table == null) { - return null; - } - - // Return the table name. - return table.value(); - } - - private ColumnInfo[] parseColumnInfo(Class clazz) { - ArrayList columns = new ArrayList(); - while (clazz != null) { - parseColumnInfo(clazz, columns); - clazz = clazz.getSuperclass(); - } - - // Return a list. - ColumnInfo[] columnList = new ColumnInfo[columns.size()]; - columns.toArray(columnList); - return columnList; - } - - private void parseColumnInfo(Class clazz, ArrayList columns) { - // Gather metadata from each annotated field. - Field[] fields = clazz.getDeclaredFields(); // including non-public fields - for (int i = 0; i != fields.length; ++i) { - // Get column metadata from the annotation. - Field field = fields[i]; - Entry.Column info = ((AnnotatedElement) field).getAnnotation(Entry.Column.class); - if (info == null) continue; - - // Determine the field type. - int type; - Class fieldType = field.getType(); - if (fieldType == String.class) { - type = TYPE_STRING; - } else if (fieldType == boolean.class) { - type = TYPE_BOOLEAN; - } else if (fieldType == short.class) { - type = TYPE_SHORT; - } else if (fieldType == int.class) { - type = TYPE_INT; - } else if (fieldType == long.class) { - type = TYPE_LONG; - } else if (fieldType == float.class) { - type = TYPE_FLOAT; - } else if (fieldType == double.class) { - type = TYPE_DOUBLE; - } else if (fieldType == byte[].class) { - type = TYPE_BLOB; - } else { - throw new IllegalArgumentException( - "Unsupported field type for column: " + fieldType.getName()); - } - - // Add the column to the array. - int index = columns.size(); - columns.add(new ColumnInfo(info.value(), type, info.indexed(), info.unique(), - info.fullText(), info.defaultValue(), field, index)); - } - } - - public static final class ColumnInfo { - private static final String ID_KEY = "_id"; - - public final String name; - public final int type; - public final boolean indexed; - public final boolean unique; - public final boolean fullText; - public final String defaultValue; - public final Field field; - public final int projectionIndex; - - public ColumnInfo(String name, int type, boolean indexed, boolean unique, - boolean fullText, String defaultValue, Field field, int projectionIndex) { - this.name = name.toLowerCase(); - this.type = type; - this.indexed = indexed; - this.unique = unique; - this.fullText = fullText; - this.defaultValue = defaultValue; - this.field = field; - this.projectionIndex = projectionIndex; - - field.setAccessible(true); // in order to set non-public fields - } - - public boolean isId() { - return ID_KEY.equals(name); - } - } -} diff --git a/gallerycommon/src/com/android/gallery3d/common/FileCache.java b/gallerycommon/src/com/android/gallery3d/common/FileCache.java deleted file mode 100644 index d7deda6fa..000000000 --- a/gallerycommon/src/com/android/gallery3d/common/FileCache.java +++ /dev/null @@ -1,312 +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.gallery3d.common; - -import android.content.ContentValues; -import android.content.Context; -import android.database.Cursor; -import android.database.sqlite.SQLiteDatabase; -import android.database.sqlite.SQLiteOpenHelper; -import android.util.Log; - -import com.android.gallery3d.common.Entry.Table; - -import java.io.Closeable; -import java.io.File; -import java.io.IOException; - -public class FileCache implements Closeable { - private static final int LRU_CAPACITY = 4; - private static final int MAX_DELETE_COUNT = 16; - - private static final String TAG = "FileCache"; - private static final String TABLE_NAME = FileEntry.SCHEMA.getTableName(); - private static final String FILE_PREFIX = "download"; - private static final String FILE_POSTFIX = ".tmp"; - - private static final String QUERY_WHERE = - FileEntry.Columns.HASH_CODE + "=? AND " + FileEntry.Columns.CONTENT_URL + "=?"; - private static final String ID_WHERE = FileEntry.Columns.ID + "=?"; - private static final String[] PROJECTION_SIZE_SUM = - {String.format("sum(%s)", FileEntry.Columns.SIZE)}; - private static final String FREESPACE_PROJECTION[] = { - FileEntry.Columns.ID, FileEntry.Columns.FILENAME, - FileEntry.Columns.CONTENT_URL, FileEntry.Columns.SIZE}; - private static final String FREESPACE_ORDER_BY = - String.format("%s ASC", FileEntry.Columns.LAST_ACCESS); - - private final LruCache mEntryMap = - new LruCache(LRU_CAPACITY); - - private File mRootDir; - private long mCapacity; - private boolean mInitialized = false; - private long mTotalBytes; - - private DatabaseHelper mDbHelper; - - public static final class CacheEntry { - private long id; - public String contentUrl; - public File cacheFile; - - private CacheEntry(long id, String contentUrl, File cacheFile) { - this.id = id; - this.contentUrl = contentUrl; - this.cacheFile = cacheFile; - } - } - - public static void deleteFiles(Context context, File rootDir, String dbName) { - try { - context.getDatabasePath(dbName).delete(); - File[] files = rootDir.listFiles(); - if (files == null) return; - for (File file : rootDir.listFiles()) { - String name = file.getName(); - if (file.isFile() && name.startsWith(FILE_PREFIX) - && name.endsWith(FILE_POSTFIX)) file.delete(); - } - } catch (Throwable t) { - Log.w(TAG, "cannot reset database", t); - } - } - - public FileCache(Context context, File rootDir, String dbName, long capacity) { - mRootDir = Utils.checkNotNull(rootDir); - mCapacity = capacity; - mDbHelper = new DatabaseHelper(context, dbName); - } - - @Override - public void close() { - mDbHelper.close(); - } - - public void store(String downloadUrl, File file) { - if (!mInitialized) initialize(); - - Utils.assertTrue(file.getParentFile().equals(mRootDir)); - FileEntry entry = new FileEntry(); - entry.hashCode = Utils.crc64Long(downloadUrl); - entry.contentUrl = downloadUrl; - entry.filename = file.getName(); - entry.size = file.length(); - entry.lastAccess = System.currentTimeMillis(); - if (entry.size >= mCapacity) { - file.delete(); - throw new IllegalArgumentException("file too large: " + entry.size); - } - synchronized (this) { - FileEntry original = queryDatabase(downloadUrl); - if (original != null) { - file.delete(); - entry.filename = original.filename; - entry.size = original.size; - } else { - mTotalBytes += entry.size; - } - FileEntry.SCHEMA.insertOrReplace( - mDbHelper.getWritableDatabase(), entry); - if (mTotalBytes > mCapacity) freeSomeSpaceIfNeed(MAX_DELETE_COUNT); - } - } - - public CacheEntry lookup(String downloadUrl) { - if (!mInitialized) initialize(); - CacheEntry entry; - synchronized (mEntryMap) { - entry = mEntryMap.get(downloadUrl); - } - - if (entry != null) { - synchronized (this) { - updateLastAccess(entry.id); - } - return entry; - } - - synchronized (this) { - FileEntry file = queryDatabase(downloadUrl); - if (file == null) return null; - entry = new CacheEntry( - file.id, downloadUrl, new File(mRootDir, file.filename)); - if (!entry.cacheFile.isFile()) { // file has been removed - try { - mDbHelper.getWritableDatabase().delete( - TABLE_NAME, ID_WHERE, new String[] {String.valueOf(file.id)}); - mTotalBytes -= file.size; - } catch (Throwable t) { - Log.w(TAG, "cannot delete entry: " + file.filename, t); - } - return null; - } - synchronized (mEntryMap) { - mEntryMap.put(downloadUrl, entry); - } - return entry; - } - } - - private FileEntry queryDatabase(String downloadUrl) { - long hash = Utils.crc64Long(downloadUrl); - String whereArgs[] = new String[] {String.valueOf(hash), downloadUrl}; - Cursor cursor = mDbHelper.getReadableDatabase().query(TABLE_NAME, - FileEntry.SCHEMA.getProjection(), - QUERY_WHERE, whereArgs, null, null, null); - try { - if (!cursor.moveToNext()) return null; - FileEntry entry = new FileEntry(); - FileEntry.SCHEMA.cursorToObject(cursor, entry); - updateLastAccess(entry.id); - return entry; - } finally { - cursor.close(); - } - } - - private void updateLastAccess(long id) { - ContentValues values = new ContentValues(); - values.put(FileEntry.Columns.LAST_ACCESS, System.currentTimeMillis()); - mDbHelper.getWritableDatabase().update(TABLE_NAME, - values, ID_WHERE, new String[] {String.valueOf(id)}); - } - - public File createFile() throws IOException { - return File.createTempFile(FILE_PREFIX, FILE_POSTFIX, mRootDir); - } - - private synchronized void initialize() { - if (mInitialized) return; - - if (!mRootDir.isDirectory()) { - mRootDir.mkdirs(); - if (!mRootDir.isDirectory()) { - throw new RuntimeException("cannot create: " + mRootDir.getAbsolutePath()); - } - } - - Cursor cursor = mDbHelper.getReadableDatabase().query( - TABLE_NAME, PROJECTION_SIZE_SUM, - null, null, null, null, null); - try { - if (cursor.moveToNext()) mTotalBytes = cursor.getLong(0); - } finally { - cursor.close(); - } - if (mTotalBytes > mCapacity) freeSomeSpaceIfNeed(MAX_DELETE_COUNT); - - // Mark initialized when everything above went through. If an exception was thrown, - // initialize() will be retried later. - mInitialized = true; - } - - private void freeSomeSpaceIfNeed(int maxDeleteFileCount) { - Cursor cursor = mDbHelper.getReadableDatabase().query( - TABLE_NAME, FREESPACE_PROJECTION, - null, null, null, null, FREESPACE_ORDER_BY); - try { - while (maxDeleteFileCount > 0 - && mTotalBytes > mCapacity && cursor.moveToNext()) { - long id = cursor.getLong(0); - String path = cursor.getString(1); - String url = cursor.getString(2); - long size = cursor.getLong(3); - - synchronized (mEntryMap) { - // if some one still uses it - if (mEntryMap.containsKey(url)) continue; - } - - --maxDeleteFileCount; - if (new File(mRootDir, path).delete()) { - mTotalBytes -= size; - mDbHelper.getWritableDatabase().delete(TABLE_NAME, - ID_WHERE, new String[]{String.valueOf(id)}); - } else { - Log.w(TAG, "unable to delete file: " + path); - } - } - } finally { - cursor.close(); - } - } - - @Table("files") - private static class FileEntry extends Entry { - public static final EntrySchema SCHEMA = new EntrySchema(FileEntry.class); - - public interface Columns extends Entry.Columns { - public static final String HASH_CODE = "hash_code"; - public static final String CONTENT_URL = "content_url"; - public static final String FILENAME = "filename"; - public static final String SIZE = "size"; - public static final String LAST_ACCESS = "last_access"; - } - - @Column(value = Columns.HASH_CODE, indexed = true) - public long hashCode; - - @Column(Columns.CONTENT_URL) - public String contentUrl; - - @Column(Columns.FILENAME) - public String filename; - - @Column(Columns.SIZE) - public long size; - - @Column(value = Columns.LAST_ACCESS, indexed = true) - public long lastAccess; - - @Override - public String toString() { - return new StringBuilder() - .append("hash_code: ").append(hashCode).append(", ") - .append("content_url").append(contentUrl).append(", ") - .append("last_access").append(lastAccess).append(", ") - .append("filename").append(filename).toString(); - } - } - - private final class DatabaseHelper extends SQLiteOpenHelper { - public static final int DATABASE_VERSION = 1; - - public DatabaseHelper(Context context, String dbName) { - super(context, dbName, null, DATABASE_VERSION); - } - - @Override - public void onCreate(SQLiteDatabase db) { - FileEntry.SCHEMA.createTables(db); - - // delete old files - for (File file : mRootDir.listFiles()) { - if (!file.delete()) { - Log.w(TAG, "fail to remove: " + file.getAbsolutePath()); - } - } - } - - @Override - public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { - //reset everything - FileEntry.SCHEMA.dropTables(db); - onCreate(db); - } - } -} diff --git a/gallerycommon/src/com/android/gallery3d/common/Fingerprint.java b/gallerycommon/src/com/android/gallery3d/common/Fingerprint.java deleted file mode 100644 index 2847e57ce..000000000 --- a/gallerycommon/src/com/android/gallery3d/common/Fingerprint.java +++ /dev/null @@ -1,187 +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.gallery3d.common; - -import java.io.IOException; -import java.io.InputStream; -import java.security.DigestInputStream; -import java.security.MessageDigest; -import java.security.NoSuchAlgorithmException; -import java.util.Arrays; -import java.util.List; - -/** - * MD5-based digest Wrapper. - */ -public class Fingerprint { - // Instance of the MessageDigest using our specified digest algorithm. - private static final MessageDigest DIGESTER; - - /** - * Name of the digest algorithm we use in {@link java.security.MessageDigest} - */ - private static final String DIGEST_MD5 = "md5"; - - // Version 1 streamId prefix. - // Hard coded stream id length limit is 40-chars. Don't ask! - private static final String STREAM_ID_CS_PREFIX = "cs_01_"; - - // 16 bytes for 128-bit fingerprint - private static final int FINGERPRINT_BYTE_LENGTH; - - // length of prefix + 32 hex chars for 128-bit fingerprint - private static final int STREAM_ID_CS_01_LENGTH; - - static { - try { - DIGESTER = MessageDigest.getInstance(DIGEST_MD5); - FINGERPRINT_BYTE_LENGTH = DIGESTER.getDigestLength(); - STREAM_ID_CS_01_LENGTH = STREAM_ID_CS_PREFIX.length() - + (FINGERPRINT_BYTE_LENGTH * 2); - } catch (NoSuchAlgorithmException e) { - // can't continue, but really shouldn't happen - throw new IllegalStateException(e); - } - } - - // md5 digest bytes. - private final byte[] mMd5Digest; - - /** - * Creates a new Fingerprint. - */ - public Fingerprint(byte[] bytes) { - if ((bytes == null) || (bytes.length != FINGERPRINT_BYTE_LENGTH)) { - throw new IllegalArgumentException(); - } - mMd5Digest = bytes; - } - - /** - * Creates a Fingerprint based on the contents of a file. - * - * Note that this will close() stream after calculating the digest. - * @param byteCount length of original data will be stored at byteCount[0] as a side product - * of the fingerprint calculation - */ - public static Fingerprint fromInputStream(InputStream stream, long[] byteCount) - throws IOException { - DigestInputStream in = null; - long count = 0; - try { - in = new DigestInputStream(stream, DIGESTER); - byte[] bytes = new byte[8192]; - while (true) { - // scan through file to compute a fingerprint. - int n = in.read(bytes); - if (n < 0) break; - count += n; - } - } finally { - if (in != null) in.close(); - } - if ((byteCount != null) && (byteCount.length > 0)) byteCount[0] = count; - return new Fingerprint(in.getMessageDigest().digest()); - } - - /** - * Decodes a string stream id to a 128-bit fingerprint. - */ - public static Fingerprint fromStreamId(String streamId) { - if ((streamId == null) - || !streamId.startsWith(STREAM_ID_CS_PREFIX) - || (streamId.length() != STREAM_ID_CS_01_LENGTH)) { - throw new IllegalArgumentException("bad streamId: " + streamId); - } - - // decode the hex bytes of the fingerprint portion - byte[] bytes = new byte[FINGERPRINT_BYTE_LENGTH]; - int byteIdx = 0; - for (int idx = STREAM_ID_CS_PREFIX.length(); idx < STREAM_ID_CS_01_LENGTH; - idx += 2) { - int value = (toDigit(streamId, idx) << 4) | toDigit(streamId, idx + 1); - bytes[byteIdx++] = (byte) (value & 0xff); - } - return new Fingerprint(bytes); - } - - /** - * Scans a list of strings for a valid streamId. - * - * @param streamIdList list of stream id's to be scanned - * @return valid fingerprint or null if it can't be found - */ - public static Fingerprint extractFingerprint(List streamIdList) { - for (String streamId : streamIdList) { - if (streamId.startsWith(STREAM_ID_CS_PREFIX)) { - return fromStreamId(streamId); - } - } - return null; - } - - /** - * Encodes a 128-bit fingerprint as a string stream id. - * - * Stream id string is limited to 40 characters, which could be digits, lower case ASCII and - * underscores. - */ - public String toStreamId() { - StringBuilder streamId = new StringBuilder(STREAM_ID_CS_PREFIX); - appendHexFingerprint(streamId, mMd5Digest); - return streamId.toString(); - } - - public byte[] getBytes() { - return mMd5Digest; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) return true; - if (!(obj instanceof Fingerprint)) return false; - Fingerprint other = (Fingerprint) obj; - return Arrays.equals(mMd5Digest, other.mMd5Digest); - } - - public boolean equals(byte[] md5Digest) { - return Arrays.equals(mMd5Digest, md5Digest); - } - - @Override - public int hashCode() { - return Arrays.hashCode(mMd5Digest); - } - - // Utility methods. - - private static int toDigit(String streamId, int index) { - int digit = Character.digit(streamId.charAt(index), 16); - if (digit < 0) { - throw new IllegalArgumentException("illegal hex digit in " + streamId); - } - return digit; - } - - private static void appendHexFingerprint(StringBuilder sb, byte[] bytes) { - for (int idx = 0; idx < FINGERPRINT_BYTE_LENGTH; idx++) { - int value = bytes[idx]; - sb.append(Integer.toHexString((value >> 4) & 0x0f)); - sb.append(Integer.toHexString(value& 0x0f)); - } - } -} diff --git a/gallerycommon/src/com/android/gallery3d/common/HttpClientFactory.java b/gallerycommon/src/com/android/gallery3d/common/HttpClientFactory.java deleted file mode 100644 index 18b7a8875..000000000 --- a/gallerycommon/src/com/android/gallery3d/common/HttpClientFactory.java +++ /dev/null @@ -1,133 +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.gallery3d.common; - -import android.content.Context; -import android.content.pm.PackageInfo; -import android.content.pm.PackageManager.NameNotFoundException; -import android.os.Build; - -import org.apache.http.HttpVersion; -import org.apache.http.client.HttpClient; -import org.apache.http.conn.params.ConnManagerParams; -import org.apache.http.params.CoreProtocolPNames; -import org.apache.http.params.HttpParams; - -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; - -/** - * Constructs {@link HttpClient} instances and isolates client code from API - * level differences. - */ -public final class HttpClientFactory { - // TODO: migrate GDataClient to use this util method instead of apache's - // DefaultHttpClient. - /** - * Creates an HttpClient with the userAgent string constructed from the - * package name contained in the context. - * @return the client - */ - public static HttpClient newHttpClient(Context context) { - return HttpClientFactory.newHttpClient(getUserAgent(context)); - } - - /** - * Creates an HttpClient with the specified userAgent string. - * @param userAgent the userAgent string - * @return the client - */ - public static HttpClient newHttpClient(String userAgent) { - // AndroidHttpClient is available on all platform releases, - // but is hidden until API Level 8 - try { - Class clazz = Class.forName("android.net.http.AndroidHttpClient"); - Method newInstance = clazz.getMethod("newInstance", String.class); - Object instance = newInstance.invoke(null, userAgent); - - HttpClient client = (HttpClient) instance; - - // ensure we default to HTTP 1.1 - HttpParams params = client.getParams(); - params.setParameter(CoreProtocolPNames.PROTOCOL_VERSION, HttpVersion.HTTP_1_1); - - // AndroidHttpClient sets these two parameters thusly by default: - // HttpConnectionParams.setSoTimeout(params, 60 * 1000); - // HttpConnectionParams.setConnectionTimeout(params, 60 * 1000); - - // however it doesn't set this one... - ConnManagerParams.setTimeout(params, 60 * 1000); - - return client; - } catch (InvocationTargetException e) { - throw new RuntimeException(e); - } catch (ClassNotFoundException e) { - throw new RuntimeException(e); - } catch (NoSuchMethodException e) { - throw new RuntimeException(e); - } catch (IllegalAccessException e) { - throw new RuntimeException(e); - } - } - - /** - * Closes an HttpClient. - */ - public static void close(HttpClient client) { - // AndroidHttpClient is available on all platform releases, - // but is hidden until API Level 8 - try { - Class clazz = client.getClass(); - Method method = clazz.getMethod("close", (Class[]) null); - method.invoke(client, (Object[]) null); - } catch (InvocationTargetException e) { - throw new RuntimeException(e); - } catch (NoSuchMethodException e) { - throw new RuntimeException(e); - } catch (IllegalAccessException e) { - throw new RuntimeException(e); - } - } - - private static String sUserAgent = null; - - private static String getUserAgent(Context context) { - if (sUserAgent == null) { - PackageInfo pi; - try { - pi = context.getPackageManager().getPackageInfo( - context.getPackageName(), 0); - } catch (NameNotFoundException e) { - throw new IllegalStateException("getPackageInfo failed"); - } - sUserAgent = String.format("%s/%s; %s/%s/%s/%s; %s/%s/%s", - pi.packageName, - pi.versionName, - Build.BRAND, - Build.DEVICE, - Build.MODEL, - Build.ID, - Build.VERSION.SDK_INT, - Build.VERSION.RELEASE, - Build.VERSION.INCREMENTAL); - } - return sUserAgent; - } - - private HttpClientFactory() { - } -} diff --git a/gallerycommon/src/com/android/gallery3d/common/LruCache.java b/gallerycommon/src/com/android/gallery3d/common/LruCache.java deleted file mode 100644 index 81dabf773..000000000 --- a/gallerycommon/src/com/android/gallery3d/common/LruCache.java +++ /dev/null @@ -1,90 +0,0 @@ -/* - * Copyright (C) 2009 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.gallery3d.common; - -import java.lang.ref.ReferenceQueue; -import java.lang.ref.WeakReference; -import java.util.HashMap; -import java.util.LinkedHashMap; -import java.util.Map; - -/** - * An LRU cache which stores recently inserted entries and all entries ever - * inserted which still has a strong reference elsewhere. - */ -public class LruCache { - - private final HashMap mLruMap; - private final HashMap> mWeakMap = - new HashMap>(); - private ReferenceQueue mQueue = new ReferenceQueue(); - - @SuppressWarnings("serial") - public LruCache(final int capacity) { - mLruMap = new LinkedHashMap(16, 0.75f, true) { - @Override - protected boolean removeEldestEntry(Map.Entry eldest) { - return size() > capacity; - } - }; - } - - private static class Entry extends WeakReference { - K mKey; - - public Entry(K key, V value, ReferenceQueue queue) { - super(value, queue); - mKey = key; - } - } - - @SuppressWarnings("unchecked") - private void cleanUpWeakMap() { - Entry entry = (Entry) mQueue.poll(); - while (entry != null) { - mWeakMap.remove(entry.mKey); - entry = (Entry) mQueue.poll(); - } - } - - public synchronized boolean containsKey(K key) { - cleanUpWeakMap(); - return mWeakMap.containsKey(key); - } - - public synchronized V put(K key, V value) { - cleanUpWeakMap(); - mLruMap.put(key, value); - Entry entry = mWeakMap.put( - key, new Entry(key, value, mQueue)); - return entry == null ? null : entry.get(); - } - - public synchronized V get(K key) { - cleanUpWeakMap(); - V value = mLruMap.get(key); - if (value != null) return value; - Entry entry = mWeakMap.get(key); - return entry == null ? null : entry.get(); - } - - public synchronized void clear() { - mLruMap.clear(); - mWeakMap.clear(); - mQueue = new ReferenceQueue(); - } -} diff --git a/gallerycommon/src/com/android/gallery3d/common/OverScroller.java b/gallerycommon/src/com/android/gallery3d/common/OverScroller.java deleted file mode 100644 index 1ab7953d2..000000000 --- a/gallerycommon/src/com/android/gallery3d/common/OverScroller.java +++ /dev/null @@ -1,958 +0,0 @@ -/* - * Copyright (C) 2010 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.gallery3d.common; - -import android.content.Context; -import android.hardware.SensorManager; -import android.util.FloatMath; -import android.util.Log; -import android.view.ViewConfiguration; -import android.view.animation.AnimationUtils; -import android.view.animation.Interpolator; - -/** - * This class encapsulates scrolling with the ability to overshoot the bounds - * of a scrolling operation. This class is a drop-in replacement for - * {@link android.widget.Scroller} in most cases. - */ -public class OverScroller { - private int mMode; - - private final SplineOverScroller mScrollerX; - private final SplineOverScroller mScrollerY; - - private Interpolator mInterpolator; - - private final boolean mFlywheel; - - private static final int DEFAULT_DURATION = 250; - private static final int SCROLL_MODE = 0; - private static final int FLING_MODE = 1; - - /** - * Creates an OverScroller with a viscous fluid scroll interpolator and flywheel. - * @param context - */ - public OverScroller(Context context) { - this(context, null); - } - - /** - * Creates an OverScroller with flywheel enabled. - * @param context The context of this application. - * @param interpolator The scroll interpolator. If null, a default (viscous) interpolator will - * be used. - */ - public OverScroller(Context context, Interpolator interpolator) { - this(context, interpolator, true); - } - - /** - * Creates an OverScroller. - * @param context The context of this application. - * @param interpolator The scroll interpolator. If null, a default (viscous) interpolator will - * be used. - * @param flywheel If true, successive fling motions will keep on increasing scroll speed. - * @hide - */ - public OverScroller(Context context, Interpolator interpolator, boolean flywheel) { - mInterpolator = interpolator; - mFlywheel = flywheel; - mScrollerX = new SplineOverScroller(); - mScrollerY = new SplineOverScroller(); - - SplineOverScroller.initFromContext(context); - } - - /** - * Creates an OverScroller with flywheel enabled. - * @param context The context of this application. - * @param interpolator The scroll interpolator. If null, a default (viscous) interpolator will - * be used. - * @param bounceCoefficientX A value between 0 and 1 that will determine the proportion of the - * velocity which is preserved in the bounce when the horizontal edge is reached. A null value - * means no bounce. This behavior is no longer supported and this coefficient has no effect. - * @param bounceCoefficientY Same as bounceCoefficientX but for the vertical direction. This - * behavior is no longer supported and this coefficient has no effect. - * !deprecated Use {!link #OverScroller(Context, Interpolator, boolean)} instead. - */ - public OverScroller(Context context, Interpolator interpolator, - float bounceCoefficientX, float bounceCoefficientY) { - this(context, interpolator, true); - } - - /** - * Creates an OverScroller. - * @param context The context of this application. - * @param interpolator The scroll interpolator. If null, a default (viscous) interpolator will - * be used. - * @param bounceCoefficientX A value between 0 and 1 that will determine the proportion of the - * velocity which is preserved in the bounce when the horizontal edge is reached. A null value - * means no bounce. This behavior is no longer supported and this coefficient has no effect. - * @param bounceCoefficientY Same as bounceCoefficientX but for the vertical direction. This - * behavior is no longer supported and this coefficient has no effect. - * @param flywheel If true, successive fling motions will keep on increasing scroll speed. - * !deprecated Use {!link OverScroller(Context, Interpolator, boolean)} instead. - */ - public OverScroller(Context context, Interpolator interpolator, - float bounceCoefficientX, float bounceCoefficientY, boolean flywheel) { - this(context, interpolator, flywheel); - } - - void setInterpolator(Interpolator interpolator) { - mInterpolator = interpolator; - } - - /** - * The amount of friction applied to flings. The default value - * is {@link ViewConfiguration#getScrollFriction}. - * - * @param friction A scalar dimension-less value representing the coefficient of - * friction. - */ - public final void setFriction(float friction) { - mScrollerX.setFriction(friction); - mScrollerY.setFriction(friction); - } - - /** - * - * Returns whether the scroller has finished scrolling. - * - * @return True if the scroller has finished scrolling, false otherwise. - */ - public final boolean isFinished() { - return mScrollerX.mFinished && mScrollerY.mFinished; - } - - /** - * Force the finished field to a particular value. Contrary to - * {@link #abortAnimation()}, forcing the animation to finished - * does NOT cause the scroller to move to the final x and y - * position. - * - * @param finished The new finished value. - */ - public final void forceFinished(boolean finished) { - mScrollerX.mFinished = mScrollerY.mFinished = finished; - } - - /** - * Returns the current X offset in the scroll. - * - * @return The new X offset as an absolute distance from the origin. - */ - public final int getCurrX() { - return mScrollerX.mCurrentPosition; - } - - /** - * Returns the current Y offset in the scroll. - * - * @return The new Y offset as an absolute distance from the origin. - */ - public final int getCurrY() { - return mScrollerY.mCurrentPosition; - } - - /** - * Returns the absolute value of the current velocity. - * - * @return The original velocity less the deceleration, norm of the X and Y velocity vector. - */ - public float getCurrVelocity() { - float squaredNorm = mScrollerX.mCurrVelocity * mScrollerX.mCurrVelocity; - squaredNorm += mScrollerY.mCurrVelocity * mScrollerY.mCurrVelocity; - return FloatMath.sqrt(squaredNorm); - } - - /** - * Returns the start X offset in the scroll. - * - * @return The start X offset as an absolute distance from the origin. - */ - public final int getStartX() { - return mScrollerX.mStart; - } - - /** - * Returns the start Y offset in the scroll. - * - * @return The start Y offset as an absolute distance from the origin. - */ - public final int getStartY() { - return mScrollerY.mStart; - } - - /** - * Returns where the scroll will end. Valid only for "fling" scrolls. - * - * @return The final X offset as an absolute distance from the origin. - */ - public final int getFinalX() { - return mScrollerX.mFinal; - } - - /** - * Returns where the scroll will end. Valid only for "fling" scrolls. - * - * @return The final Y offset as an absolute distance from the origin. - */ - public final int getFinalY() { - return mScrollerY.mFinal; - } - - /** - * Returns how long the scroll event will take, in milliseconds. - * - * @return The duration of the scroll in milliseconds. - * - * @hide Pending removal once nothing depends on it - * @deprecated OverScrollers don't necessarily have a fixed duration. - * This function will lie to the best of its ability. - */ - @Deprecated - public final int getDuration() { - return Math.max(mScrollerX.mDuration, mScrollerY.mDuration); - } - - /** - * Extend the scroll animation. This allows a running animation to scroll - * further and longer, when used with {@link #setFinalX(int)} or {@link #setFinalY(int)}. - * - * @param extend Additional time to scroll in milliseconds. - * @see #setFinalX(int) - * @see #setFinalY(int) - * - * @hide Pending removal once nothing depends on it - * @deprecated OverScrollers don't necessarily have a fixed duration. - * Instead of setting a new final position and extending - * the duration of an existing scroll, use startScroll - * to begin a new animation. - */ - @Deprecated - public void extendDuration(int extend) { - mScrollerX.extendDuration(extend); - mScrollerY.extendDuration(extend); - } - - /** - * Sets the final position (X) for this scroller. - * - * @param newX The new X offset as an absolute distance from the origin. - * @see #extendDuration(int) - * @see #setFinalY(int) - * - * @hide Pending removal once nothing depends on it - * @deprecated OverScroller's final position may change during an animation. - * Instead of setting a new final position and extending - * the duration of an existing scroll, use startScroll - * to begin a new animation. - */ - @Deprecated - public void setFinalX(int newX) { - mScrollerX.setFinalPosition(newX); - } - - /** - * Sets the final position (Y) for this scroller. - * - * @param newY The new Y offset as an absolute distance from the origin. - * @see #extendDuration(int) - * @see #setFinalX(int) - * - * @hide Pending removal once nothing depends on it - * @deprecated OverScroller's final position may change during an animation. - * Instead of setting a new final position and extending - * the duration of an existing scroll, use startScroll - * to begin a new animation. - */ - @Deprecated - public void setFinalY(int newY) { - mScrollerY.setFinalPosition(newY); - } - - /** - * Call this when you want to know the new location. If it returns true, the - * animation is not yet finished. - */ - public boolean computeScrollOffset() { - if (isFinished()) { - return false; - } - - switch (mMode) { - case SCROLL_MODE: - long time = AnimationUtils.currentAnimationTimeMillis(); - // Any scroller can be used for time, since they were started - // together in scroll mode. We use X here. - final long elapsedTime = time - mScrollerX.mStartTime; - - final int duration = mScrollerX.mDuration; - if (elapsedTime < duration) { - float q = (float) (elapsedTime) / duration; - - if (mInterpolator == null) { - q = Scroller.viscousFluid(q); - } else { - q = mInterpolator.getInterpolation(q); - } - - mScrollerX.updateScroll(q); - mScrollerY.updateScroll(q); - } else { - abortAnimation(); - } - break; - - case FLING_MODE: - if (!mScrollerX.mFinished) { - if (!mScrollerX.update()) { - if (!mScrollerX.continueWhenFinished()) { - mScrollerX.finish(); - } - } - } - - if (!mScrollerY.mFinished) { - if (!mScrollerY.update()) { - if (!mScrollerY.continueWhenFinished()) { - mScrollerY.finish(); - } - } - } - - break; - } - - return true; - } - - /** - * Start scrolling by providing a starting point and the distance to travel. - * The scroll will use the default value of 250 milliseconds for the - * duration. - * - * @param startX Starting horizontal scroll offset in pixels. Positive - * numbers will scroll the content to the left. - * @param startY Starting vertical scroll offset in pixels. Positive numbers - * will scroll the content up. - * @param dx Horizontal distance to travel. Positive numbers will scroll the - * content to the left. - * @param dy Vertical distance to travel. Positive numbers will scroll the - * content up. - */ - public void startScroll(int startX, int startY, int dx, int dy) { - startScroll(startX, startY, dx, dy, DEFAULT_DURATION); - } - - /** - * Start scrolling by providing a starting point and the distance to travel. - * - * @param startX Starting horizontal scroll offset in pixels. Positive - * numbers will scroll the content to the left. - * @param startY Starting vertical scroll offset in pixels. Positive numbers - * will scroll the content up. - * @param dx Horizontal distance to travel. Positive numbers will scroll the - * content to the left. - * @param dy Vertical distance to travel. Positive numbers will scroll the - * content up. - * @param duration Duration of the scroll in milliseconds. - */ - public void startScroll(int startX, int startY, int dx, int dy, int duration) { - mMode = SCROLL_MODE; - mScrollerX.startScroll(startX, dx, duration); - mScrollerY.startScroll(startY, dy, duration); - } - - /** - * Call this when you want to 'spring back' into a valid coordinate range. - * - * @param startX Starting X coordinate - * @param startY Starting Y coordinate - * @param minX Minimum valid X value - * @param maxX Maximum valid X value - * @param minY Minimum valid Y value - * @param maxY Minimum valid Y value - * @return true if a springback was initiated, false if startX and startY were - * already within the valid range. - */ - public boolean springBack(int startX, int startY, int minX, int maxX, int minY, int maxY) { - mMode = FLING_MODE; - - // Make sure both methods are called. - final boolean spingbackX = mScrollerX.springback(startX, minX, maxX); - final boolean spingbackY = mScrollerY.springback(startY, minY, maxY); - return spingbackX || spingbackY; - } - - public void fling(int startX, int startY, int velocityX, int velocityY, - int minX, int maxX, int minY, int maxY) { - fling(startX, startY, velocityX, velocityY, minX, maxX, minY, maxY, 0, 0); - } - - /** - * Start scrolling based on a fling gesture. The distance traveled will - * depend on the initial velocity of the fling. - * - * @param startX Starting point of the scroll (X) - * @param startY Starting point of the scroll (Y) - * @param velocityX Initial velocity of the fling (X) measured in pixels per - * second. - * @param velocityY Initial velocity of the fling (Y) measured in pixels per - * second - * @param minX Minimum X value. The scroller will not scroll past this point - * unless overX > 0. If overfling is allowed, it will use minX as - * a springback boundary. - * @param maxX Maximum X value. The scroller will not scroll past this point - * unless overX > 0. If overfling is allowed, it will use maxX as - * a springback boundary. - * @param minY Minimum Y value. The scroller will not scroll past this point - * unless overY > 0. If overfling is allowed, it will use minY as - * a springback boundary. - * @param maxY Maximum Y value. The scroller will not scroll past this point - * unless overY > 0. If overfling is allowed, it will use maxY as - * a springback boundary. - * @param overX Overfling range. If > 0, horizontal overfling in either - * direction will be possible. - * @param overY Overfling range. If > 0, vertical overfling in either - * direction will be possible. - */ - public void fling(int startX, int startY, int velocityX, int velocityY, - int minX, int maxX, int minY, int maxY, int overX, int overY) { - // Continue a scroll or fling in progress - if (mFlywheel && !isFinished()) { - float oldVelocityX = mScrollerX.mCurrVelocity; - float oldVelocityY = mScrollerY.mCurrVelocity; - if (Math.signum(velocityX) == Math.signum(oldVelocityX) && - Math.signum(velocityY) == Math.signum(oldVelocityY)) { - velocityX += oldVelocityX; - velocityY += oldVelocityY; - } - } - - mMode = FLING_MODE; - mScrollerX.fling(startX, velocityX, minX, maxX, overX); - mScrollerY.fling(startY, velocityY, minY, maxY, overY); - } - - /** - * Notify the scroller that we've reached a horizontal boundary. - * Normally the information to handle this will already be known - * when the animation is started, such as in a call to one of the - * fling functions. However there are cases where this cannot be known - * in advance. This function will transition the current motion and - * animate from startX to finalX as appropriate. - * - * @param startX Starting/current X position - * @param finalX Desired final X position - * @param overX Magnitude of overscroll allowed. This should be the maximum - * desired distance from finalX. Absolute value - must be positive. - */ - public void notifyHorizontalEdgeReached(int startX, int finalX, int overX) { - mScrollerX.notifyEdgeReached(startX, finalX, overX); - } - - /** - * Notify the scroller that we've reached a vertical boundary. - * Normally the information to handle this will already be known - * when the animation is started, such as in a call to one of the - * fling functions. However there are cases where this cannot be known - * in advance. This function will animate a parabolic motion from - * startY to finalY. - * - * @param startY Starting/current Y position - * @param finalY Desired final Y position - * @param overY Magnitude of overscroll allowed. This should be the maximum - * desired distance from finalY. Absolute value - must be positive. - */ - public void notifyVerticalEdgeReached(int startY, int finalY, int overY) { - mScrollerY.notifyEdgeReached(startY, finalY, overY); - } - - /** - * Returns whether the current Scroller is currently returning to a valid position. - * Valid bounds were provided by the - * {@link #fling(int, int, int, int, int, int, int, int, int, int)} method. - * - * One should check this value before calling - * {@link #startScroll(int, int, int, int)} as the interpolation currently in progress - * to restore a valid position will then be stopped. The caller has to take into account - * the fact that the started scroll will start from an overscrolled position. - * - * @return true when the current position is overscrolled and in the process of - * interpolating back to a valid value. - */ - public boolean isOverScrolled() { - return ((!mScrollerX.mFinished && - mScrollerX.mState != SplineOverScroller.SPLINE) || - (!mScrollerY.mFinished && - mScrollerY.mState != SplineOverScroller.SPLINE)); - } - - /** - * Stops the animation. Contrary to {@link #forceFinished(boolean)}, - * aborting the animating causes the scroller to move to the final x and y - * positions. - * - * @see #forceFinished(boolean) - */ - public void abortAnimation() { - mScrollerX.finish(); - mScrollerY.finish(); - } - - /** - * Returns the time elapsed since the beginning of the scrolling. - * - * @return The elapsed time in milliseconds. - * - * @hide - */ - public int timePassed() { - final long time = AnimationUtils.currentAnimationTimeMillis(); - final long startTime = Math.min(mScrollerX.mStartTime, mScrollerY.mStartTime); - return (int) (time - startTime); - } - - /** - * @hide - */ - public boolean isScrollingInDirection(float xvel, float yvel) { - final int dx = mScrollerX.mFinal - mScrollerX.mStart; - final int dy = mScrollerY.mFinal - mScrollerY.mStart; - return !isFinished() && Math.signum(xvel) == Math.signum(dx) && - Math.signum(yvel) == Math.signum(dy); - } - - static class SplineOverScroller { - // Initial position - private int mStart; - - // Current position - private int mCurrentPosition; - - // Final position - private int mFinal; - - // Initial velocity - private int mVelocity; - - // Current velocity - private float mCurrVelocity; - - // Constant current deceleration - private float mDeceleration; - - // Animation starting time, in system milliseconds - private long mStartTime; - - // Animation duration, in milliseconds - private int mDuration; - - // Duration to complete spline component of animation - private int mSplineDuration; - - // Distance to travel along spline animation - private int mSplineDistance; - - // Whether the animation is currently in progress - private boolean mFinished; - - // The allowed overshot distance before boundary is reached. - private int mOver; - - // Fling friction - private float mFlingFriction = ViewConfiguration.getScrollFriction(); - - // Current state of the animation. - private int mState = SPLINE; - - // Constant gravity value, used in the deceleration phase. - private static final float GRAVITY = 2000.0f; - - // A device specific coefficient adjusted to physical values. - private static float PHYSICAL_COEF; - - private static float DECELERATION_RATE = (float) (Math.log(0.78) / Math.log(0.9)); - private static final float INFLEXION = 0.35f; // Tension lines cross at (INFLEXION, 1) - private static final float START_TENSION = 0.5f; - private static final float END_TENSION = 1.0f; - private static final float P1 = START_TENSION * INFLEXION; - private static final float P2 = 1.0f - END_TENSION * (1.0f - INFLEXION); - - private static final int NB_SAMPLES = 100; - private static final float[] SPLINE_POSITION = new float[NB_SAMPLES + 1]; - private static final float[] SPLINE_TIME = new float[NB_SAMPLES + 1]; - - private static final int SPLINE = 0; - private static final int CUBIC = 1; - private static final int BALLISTIC = 2; - - static { - float x_min = 0.0f; - float y_min = 0.0f; - for (int i = 0; i < NB_SAMPLES; i++) { - final float alpha = (float) i / NB_SAMPLES; - - float x_max = 1.0f; - float x, tx, coef; - while (true) { - x = x_min + (x_max - x_min) / 2.0f; - coef = 3.0f * x * (1.0f - x); - tx = coef * ((1.0f - x) * P1 + x * P2) + x * x * x; - if (Math.abs(tx - alpha) < 1E-5) break; - if (tx > alpha) x_max = x; - else x_min = x; - } - SPLINE_POSITION[i] = coef * ((1.0f - x) * START_TENSION + x) + x * x * x; - - float y_max = 1.0f; - float y, dy; - while (true) { - y = y_min + (y_max - y_min) / 2.0f; - coef = 3.0f * y * (1.0f - y); - dy = coef * ((1.0f - y) * START_TENSION + y) + y * y * y; - if (Math.abs(dy - alpha) < 1E-5) break; - if (dy > alpha) y_max = y; - else y_min = y; - } - SPLINE_TIME[i] = coef * ((1.0f - y) * P1 + y * P2) + y * y * y; - } - SPLINE_POSITION[NB_SAMPLES] = SPLINE_TIME[NB_SAMPLES] = 1.0f; - } - - static void initFromContext(Context context) { - final float ppi = context.getResources().getDisplayMetrics().density * 160.0f; - PHYSICAL_COEF = SensorManager.GRAVITY_EARTH // g (m/s^2) - * 39.37f // inch/meter - * ppi - * 0.84f; // look and feel tuning - } - - void setFriction(float friction) { - mFlingFriction = friction; - } - - SplineOverScroller() { - mFinished = true; - } - - void updateScroll(float q) { - mCurrentPosition = mStart + Math.round(q * (mFinal - mStart)); - } - - /* - * Get a signed deceleration that will reduce the velocity. - */ - static private float getDeceleration(int velocity) { - return velocity > 0 ? -GRAVITY : GRAVITY; - } - - /* - * Modifies mDuration to the duration it takes to get from start to newFinal using the - * spline interpolation. The previous duration was needed to get to oldFinal. - */ - private void adjustDuration(int start, int oldFinal, int newFinal) { - final int oldDistance = oldFinal - start; - final int newDistance = newFinal - start; - final float x = Math.abs((float) newDistance / oldDistance); - final int index = (int) (NB_SAMPLES * x); - if (index < NB_SAMPLES) { - final float x_inf = (float) index / NB_SAMPLES; - final float x_sup = (float) (index + 1) / NB_SAMPLES; - final float t_inf = SPLINE_TIME[index]; - final float t_sup = SPLINE_TIME[index + 1]; - final float timeCoef = t_inf + (x - x_inf) / (x_sup - x_inf) * (t_sup - t_inf); - mDuration *= timeCoef; - } - } - - void startScroll(int start, int distance, int duration) { - mFinished = false; - - mStart = start; - mFinal = start + distance; - - mStartTime = AnimationUtils.currentAnimationTimeMillis(); - mDuration = duration; - - // Unused - mDeceleration = 0.0f; - mVelocity = 0; - } - - void finish() { - mCurrentPosition = mFinal; - // Not reset since WebView relies on this value for fast fling. - // TODO: restore when WebView uses the fast fling implemented in this class. - // mCurrVelocity = 0.0f; - mFinished = true; - } - - void setFinalPosition(int position) { - mFinal = position; - mFinished = false; - } - - void extendDuration(int extend) { - final long time = AnimationUtils.currentAnimationTimeMillis(); - final int elapsedTime = (int) (time - mStartTime); - mDuration = elapsedTime + extend; - mFinished = false; - } - - boolean springback(int start, int min, int max) { - mFinished = true; - - mStart = mFinal = start; - mVelocity = 0; - - mStartTime = AnimationUtils.currentAnimationTimeMillis(); - mDuration = 0; - - if (start < min) { - startSpringback(start, min, 0); - } else if (start > max) { - startSpringback(start, max, 0); - } - - return !mFinished; - } - - private void startSpringback(int start, int end, int velocity) { - // mStartTime has been set - mFinished = false; - mState = CUBIC; - mStart = start; - mFinal = end; - final int delta = start - end; - mDeceleration = getDeceleration(delta); - // TODO take velocity into account - mVelocity = -delta; // only sign is used - mOver = Math.abs(delta); - mDuration = (int) (1000.0 * Math.sqrt(-2.0 * delta / mDeceleration)); - } - - void fling(int start, int velocity, int min, int max, int over) { - mOver = over; - mFinished = false; - mCurrVelocity = mVelocity = velocity; - mDuration = mSplineDuration = 0; - mStartTime = AnimationUtils.currentAnimationTimeMillis(); - mCurrentPosition = mStart = start; - - if (start > max || start < min) { - startAfterEdge(start, min, max, velocity); - return; - } - - mState = SPLINE; - double totalDistance = 0.0; - - if (velocity != 0) { - mDuration = mSplineDuration = getSplineFlingDuration(velocity); - totalDistance = getSplineFlingDistance(velocity); - } - - mSplineDistance = (int) (totalDistance * Math.signum(velocity)); - mFinal = start + mSplineDistance; - - // Clamp to a valid final position - if (mFinal < min) { - adjustDuration(mStart, mFinal, min); - mFinal = min; - } - - if (mFinal > max) { - adjustDuration(mStart, mFinal, max); - mFinal = max; - } - } - - private double getSplineDeceleration(int velocity) { - return Math.log(INFLEXION * Math.abs(velocity) / (mFlingFriction * PHYSICAL_COEF)); - } - - private double getSplineFlingDistance(int velocity) { - final double l = getSplineDeceleration(velocity); - final double decelMinusOne = DECELERATION_RATE - 1.0; - return mFlingFriction * PHYSICAL_COEF * Math.exp(DECELERATION_RATE / decelMinusOne * l); - } - - /* Returns the duration, expressed in milliseconds */ - private int getSplineFlingDuration(int velocity) { - final double l = getSplineDeceleration(velocity); - final double decelMinusOne = DECELERATION_RATE - 1.0; - return (int) (1000.0 * Math.exp(l / decelMinusOne)); - } - - private void fitOnBounceCurve(int start, int end, int velocity) { - // Simulate a bounce that started from edge - final float durationToApex = - velocity / mDeceleration; - final float distanceToApex = velocity * velocity / 2.0f / Math.abs(mDeceleration); - final float distanceToEdge = Math.abs(end - start); - final float totalDuration = (float) Math.sqrt( - 2.0 * (distanceToApex + distanceToEdge) / Math.abs(mDeceleration)); - mStartTime -= (int) (1000.0f * (totalDuration - durationToApex)); - mStart = end; - mVelocity = (int) (- mDeceleration * totalDuration); - } - - private void startBounceAfterEdge(int start, int end, int velocity) { - mDeceleration = getDeceleration(velocity == 0 ? start - end : velocity); - fitOnBounceCurve(start, end, velocity); - onEdgeReached(); - } - - private void startAfterEdge(int start, int min, int max, int velocity) { - if (start > min && start < max) { - Log.e("OverScroller", "startAfterEdge called from a valid position"); - mFinished = true; - return; - } - final boolean positive = start > max; - final int edge = positive ? max : min; - final int overDistance = start - edge; - boolean keepIncreasing = overDistance * velocity >= 0; - if (keepIncreasing) { - // Will result in a bounce or a to_boundary depending on velocity. - startBounceAfterEdge(start, edge, velocity); - } else { - final double totalDistance = getSplineFlingDistance(velocity); - if (totalDistance > Math.abs(overDistance)) { - fling(start, velocity, positive ? min : start, positive ? start : max, mOver); - } else { - startSpringback(start, edge, velocity); - } - } - } - - void notifyEdgeReached(int start, int end, int over) { - // mState is used to detect successive notifications - if (mState == SPLINE) { - mOver = over; - mStartTime = AnimationUtils.currentAnimationTimeMillis(); - // We were in fling/scroll mode before: current velocity is such that distance to - // edge is increasing. This ensures that startAfterEdge will not start a new fling. - startAfterEdge(start, end, end, (int) mCurrVelocity); - } - } - - private void onEdgeReached() { - // mStart, mVelocity and mStartTime were adjusted to their values when edge was reached. - float distance = mVelocity * mVelocity / (2.0f * Math.abs(mDeceleration)); - final float sign = Math.signum(mVelocity); - - if (distance > mOver) { - // Default deceleration is not sufficient to slow us down before boundary - mDeceleration = - sign * mVelocity * mVelocity / (2.0f * mOver); - distance = mOver; - } - - mOver = (int) distance; - mState = BALLISTIC; - mFinal = mStart + (int) (mVelocity > 0 ? distance : -distance); - mDuration = - (int) (1000.0f * mVelocity / mDeceleration); - } - - boolean continueWhenFinished() { - switch (mState) { - case SPLINE: - // Duration from start to null velocity - if (mDuration < mSplineDuration) { - // If the animation was clamped, we reached the edge - mStart = mFinal; - // TODO Better compute speed when edge was reached - mVelocity = (int) mCurrVelocity; - mDeceleration = getDeceleration(mVelocity); - mStartTime += mDuration; - onEdgeReached(); - } else { - // Normal stop, no need to continue - return false; - } - break; - case BALLISTIC: - mStartTime += mDuration; - startSpringback(mFinal, mStart, 0); - break; - case CUBIC: - return false; - } - - update(); - return true; - } - - /* - * Update the current position and velocity for current time. Returns - * true if update has been done and false if animation duration has been - * reached. - */ - boolean update() { - final long time = AnimationUtils.currentAnimationTimeMillis(); - final long currentTime = time - mStartTime; - - if (currentTime > mDuration) { - return false; - } - - double distance = 0.0; - switch (mState) { - case SPLINE: { - final float t = (float) currentTime / mSplineDuration; - final int index = (int) (NB_SAMPLES * t); - float distanceCoef = 1.f; - float velocityCoef = 0.f; - if (index < NB_SAMPLES) { - final float t_inf = (float) index / NB_SAMPLES; - final float t_sup = (float) (index + 1) / NB_SAMPLES; - final float d_inf = SPLINE_POSITION[index]; - final float d_sup = SPLINE_POSITION[index + 1]; - velocityCoef = (d_sup - d_inf) / (t_sup - t_inf); - distanceCoef = d_inf + (t - t_inf) * velocityCoef; - } - - distance = distanceCoef * mSplineDistance; - mCurrVelocity = velocityCoef * mSplineDistance / mSplineDuration * 1000.0f; - break; - } - - case BALLISTIC: { - final float t = currentTime / 1000.0f; - mCurrVelocity = mVelocity + mDeceleration * t; - distance = mVelocity * t + mDeceleration * t * t / 2.0f; - break; - } - - case CUBIC: { - final float t = (float) (currentTime) / mDuration; - final float t2 = t * t; - final float sign = Math.signum(mVelocity); - distance = sign * mOver * (3.0f * t2 - 2.0f * t * t2); - mCurrVelocity = sign * mOver * 6.0f * (- t + t2); - break; - } - } - - mCurrentPosition = mStart + (int) Math.round(distance); - - return true; - } - } -} diff --git a/gallerycommon/src/com/android/gallery3d/common/Scroller.java b/gallerycommon/src/com/android/gallery3d/common/Scroller.java deleted file mode 100644 index 6cefd6fb0..000000000 --- a/gallerycommon/src/com/android/gallery3d/common/Scroller.java +++ /dev/null @@ -1,507 +0,0 @@ -/* - * Copyright (C) 2006 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.gallery3d.common; - -import android.content.Context; -import android.hardware.SensorManager; -import android.os.Build; -import android.util.FloatMath; -import android.view.ViewConfiguration; -import android.view.animation.AnimationUtils; -import android.view.animation.Interpolator; - - -/** - * This class encapsulates scrolling. The duration of the scroll - * can be passed in the constructor and specifies the maximum time that - * the scrolling animation should take. Past this time, the scrolling is - * automatically moved to its final stage and computeScrollOffset() - * will always return false to indicate that scrolling is over. - */ -public class Scroller { - private int mMode; - - private int mStartX; - private int mStartY; - private int mFinalX; - private int mFinalY; - - private int mMinX; - private int mMaxX; - private int mMinY; - private int mMaxY; - - private int mCurrX; - private int mCurrY; - private long mStartTime; - private int mDuration; - private float mDurationReciprocal; - private float mDeltaX; - private float mDeltaY; - private boolean mFinished; - private Interpolator mInterpolator; - private boolean mFlywheel; - - private float mVelocity; - - private static final int DEFAULT_DURATION = 250; - private static final int SCROLL_MODE = 0; - private static final int FLING_MODE = 1; - - private static float DECELERATION_RATE = (float) (Math.log(0.75) / Math.log(0.9)); - private static float ALPHA = 800; // pixels / seconds - private static float START_TENSION = 0.4f; // Tension at start: (0.4 * total T, 1.0 * Distance) - private static float END_TENSION = 1.0f - START_TENSION; - private static final int NB_SAMPLES = 100; - private static final float[] SPLINE = new float[NB_SAMPLES + 1]; - - private float mDeceleration; - private final float mPpi; - - static { - float x_min = 0.0f; - for (int i = 0; i <= NB_SAMPLES; i++) { - final float t = (float) i / NB_SAMPLES; - float x_max = 1.0f; - float x, tx, coef; - while (true) { - x = x_min + (x_max - x_min) / 2.0f; - coef = 3.0f * x * (1.0f - x); - tx = coef * ((1.0f - x) * START_TENSION + x * END_TENSION) + x * x * x; - if (Math.abs(tx - t) < 1E-5) break; - if (tx > t) x_max = x; - else x_min = x; - } - final float d = coef + x * x * x; - SPLINE[i] = d; - } - SPLINE[NB_SAMPLES] = 1.0f; - - // This controls the viscous fluid effect (how much of it) - sViscousFluidScale = 8.0f; - // must be set to 1.0 (used in viscousFluid()) - sViscousFluidNormalize = 1.0f; - sViscousFluidNormalize = 1.0f / viscousFluid(1.0f); - } - - private static float sViscousFluidScale; - private static float sViscousFluidNormalize; - - /** - * Create a Scroller with the default duration and interpolator. - */ - public Scroller(Context context) { - this(context, null); - } - - /** - * Create a Scroller with the specified interpolator. If the interpolator is - * null, the default (viscous) interpolator will be used. "Flywheel" behavior will - * be in effect for apps targeting Honeycomb or newer. - */ - public Scroller(Context context, Interpolator interpolator) { - this(context, interpolator, - context.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.HONEYCOMB); - } - - /** - * Create a Scroller with the specified interpolator. If the interpolator is - * null, the default (viscous) interpolator will be used. Specify whether or - * not to support progressive "flywheel" behavior in flinging. - */ - public Scroller(Context context, Interpolator interpolator, boolean flywheel) { - mFinished = true; - mInterpolator = interpolator; - mPpi = context.getResources().getDisplayMetrics().density * 160.0f; - mDeceleration = computeDeceleration(ViewConfiguration.getScrollFriction()); - mFlywheel = flywheel; - } - - /** - * The amount of friction applied to flings. The default value - * is {@link ViewConfiguration#getScrollFriction}. - * - * @param friction A scalar dimension-less value representing the coefficient of - * friction. - */ - public final void setFriction(float friction) { - mDeceleration = computeDeceleration(friction); - } - - private float computeDeceleration(float friction) { - return SensorManager.GRAVITY_EARTH // g (m/s^2) - * 39.37f // inch/meter - * mPpi // pixels per inch - * friction; - } - - /** - * - * Returns whether the scroller has finished scrolling. - * - * @return True if the scroller has finished scrolling, false otherwise. - */ - public final boolean isFinished() { - return mFinished; - } - - /** - * Force the finished field to a particular value. - * - * @param finished The new finished value. - */ - public final void forceFinished(boolean finished) { - mFinished = finished; - } - - /** - * Returns how long the scroll event will take, in milliseconds. - * - * @return The duration of the scroll in milliseconds. - */ - public final int getDuration() { - return mDuration; - } - - /** - * Returns the current X offset in the scroll. - * - * @return The new X offset as an absolute distance from the origin. - */ - public final int getCurrX() { - return mCurrX; - } - - /** - * Returns the current Y offset in the scroll. - * - * @return The new Y offset as an absolute distance from the origin. - */ - public final int getCurrY() { - return mCurrY; - } - - /** - * Returns the current velocity. - * - * @return The original velocity less the deceleration. Result may be - * negative. - */ - public float getCurrVelocity() { - return mVelocity - mDeceleration * timePassed() / 2000.0f; - } - - /** - * Returns the start X offset in the scroll. - * - * @return The start X offset as an absolute distance from the origin. - */ - public final int getStartX() { - return mStartX; - } - - /** - * Returns the start Y offset in the scroll. - * - * @return The start Y offset as an absolute distance from the origin. - */ - public final int getStartY() { - return mStartY; - } - - /** - * Returns where the scroll will end. Valid only for "fling" scrolls. - * - * @return The final X offset as an absolute distance from the origin. - */ - public final int getFinalX() { - return mFinalX; - } - - /** - * Returns where the scroll will end. Valid only for "fling" scrolls. - * - * @return The final Y offset as an absolute distance from the origin. - */ - public final int getFinalY() { - return mFinalY; - } - - /** - * Call this when you want to know the new location. If it returns true, - * the animation is not yet finished. loc will be altered to provide the - * new location. - */ - public boolean computeScrollOffset() { - if (mFinished) { - return false; - } - - int timePassed = (int)(AnimationUtils.currentAnimationTimeMillis() - mStartTime); - - if (timePassed < mDuration) { - switch (mMode) { - case SCROLL_MODE: - float x = timePassed * mDurationReciprocal; - - if (mInterpolator == null) - x = viscousFluid(x); - else - x = mInterpolator.getInterpolation(x); - - mCurrX = mStartX + Math.round(x * mDeltaX); - mCurrY = mStartY + Math.round(x * mDeltaY); - break; - case FLING_MODE: - final float t = (float) timePassed / mDuration; - final int index = (int) (NB_SAMPLES * t); - final float t_inf = (float) index / NB_SAMPLES; - final float t_sup = (float) (index + 1) / NB_SAMPLES; - final float d_inf = SPLINE[index]; - final float d_sup = SPLINE[index + 1]; - final float distanceCoef = d_inf + (t - t_inf) / (t_sup - t_inf) * (d_sup - d_inf); - - mCurrX = mStartX + Math.round(distanceCoef * (mFinalX - mStartX)); - // Pin to mMinX <= mCurrX <= mMaxX - mCurrX = Math.min(mCurrX, mMaxX); - mCurrX = Math.max(mCurrX, mMinX); - - mCurrY = mStartY + Math.round(distanceCoef * (mFinalY - mStartY)); - // Pin to mMinY <= mCurrY <= mMaxY - mCurrY = Math.min(mCurrY, mMaxY); - mCurrY = Math.max(mCurrY, mMinY); - - if (mCurrX == mFinalX && mCurrY == mFinalY) { - mFinished = true; - } - - break; - } - } - else { - mCurrX = mFinalX; - mCurrY = mFinalY; - mFinished = true; - } - return true; - } - - /** - * Start scrolling by providing a starting point and the distance to travel. - * The scroll will use the default value of 250 milliseconds for the - * duration. - * - * @param startX Starting horizontal scroll offset in pixels. Positive - * numbers will scroll the content to the left. - * @param startY Starting vertical scroll offset in pixels. Positive numbers - * will scroll the content up. - * @param dx Horizontal distance to travel. Positive numbers will scroll the - * content to the left. - * @param dy Vertical distance to travel. Positive numbers will scroll the - * content up. - */ - public void startScroll(int startX, int startY, int dx, int dy) { - startScroll(startX, startY, dx, dy, DEFAULT_DURATION); - } - - /** - * Start scrolling by providing a starting point and the distance to travel. - * - * @param startX Starting horizontal scroll offset in pixels. Positive - * numbers will scroll the content to the left. - * @param startY Starting vertical scroll offset in pixels. Positive numbers - * will scroll the content up. - * @param dx Horizontal distance to travel. Positive numbers will scroll the - * content to the left. - * @param dy Vertical distance to travel. Positive numbers will scroll the - * content up. - * @param duration Duration of the scroll in milliseconds. - */ - public void startScroll(int startX, int startY, int dx, int dy, int duration) { - mMode = SCROLL_MODE; - mFinished = false; - mDuration = duration; - mStartTime = AnimationUtils.currentAnimationTimeMillis(); - mStartX = startX; - mStartY = startY; - mFinalX = startX + dx; - mFinalY = startY + dy; - mDeltaX = dx; - mDeltaY = dy; - mDurationReciprocal = 1.0f / mDuration; - } - - /** - * Start scrolling based on a fling gesture. The distance travelled will - * depend on the initial velocity of the fling. - * - * @param startX Starting point of the scroll (X) - * @param startY Starting point of the scroll (Y) - * @param velocityX Initial velocity of the fling (X) measured in pixels per - * second. - * @param velocityY Initial velocity of the fling (Y) measured in pixels per - * second - * @param minX Minimum X value. The scroller will not scroll past this - * point. - * @param maxX Maximum X value. The scroller will not scroll past this - * point. - * @param minY Minimum Y value. The scroller will not scroll past this - * point. - * @param maxY Maximum Y value. The scroller will not scroll past this - * point. - */ - public void fling(int startX, int startY, int velocityX, int velocityY, - int minX, int maxX, int minY, int maxY) { - // Continue a scroll or fling in progress - if (mFlywheel && !mFinished) { - float oldVel = getCurrVelocity(); - - float dx = mFinalX - mStartX; - float dy = mFinalY - mStartY; - float hyp = FloatMath.sqrt(dx * dx + dy * dy); - - float ndx = dx / hyp; - float ndy = dy / hyp; - - float oldVelocityX = ndx * oldVel; - float oldVelocityY = ndy * oldVel; - if (Math.signum(velocityX) == Math.signum(oldVelocityX) && - Math.signum(velocityY) == Math.signum(oldVelocityY)) { - velocityX += oldVelocityX; - velocityY += oldVelocityY; - } - } - - mMode = FLING_MODE; - mFinished = false; - - float velocity = FloatMath.sqrt(velocityX * velocityX + velocityY * velocityY); - - mVelocity = velocity; - final double l = Math.log(START_TENSION * velocity / ALPHA); - mDuration = (int) (1000.0 * Math.exp(l / (DECELERATION_RATE - 1.0))); - mStartTime = AnimationUtils.currentAnimationTimeMillis(); - mStartX = startX; - mStartY = startY; - - float coeffX = velocity == 0 ? 1.0f : velocityX / velocity; - float coeffY = velocity == 0 ? 1.0f : velocityY / velocity; - - int totalDistance = - (int) (ALPHA * Math.exp(DECELERATION_RATE / (DECELERATION_RATE - 1.0) * l)); - - mMinX = minX; - mMaxX = maxX; - mMinY = minY; - mMaxY = maxY; - - mFinalX = startX + Math.round(totalDistance * coeffX); - // Pin to mMinX <= mFinalX <= mMaxX - mFinalX = Math.min(mFinalX, mMaxX); - mFinalX = Math.max(mFinalX, mMinX); - - mFinalY = startY + Math.round(totalDistance * coeffY); - // Pin to mMinY <= mFinalY <= mMaxY - mFinalY = Math.min(mFinalY, mMaxY); - mFinalY = Math.max(mFinalY, mMinY); - } - - static float viscousFluid(float x) - { - x *= sViscousFluidScale; - if (x < 1.0f) { - x -= (1.0f - (float)Math.exp(-x)); - } else { - float start = 0.36787944117f; // 1/e == exp(-1) - x = 1.0f - (float)Math.exp(1.0f - x); - x = start + x * (1.0f - start); - } - x *= sViscousFluidNormalize; - return x; - } - - /** - * Stops the animation. Contrary to {@link #forceFinished(boolean)}, - * aborting the animating cause the scroller to move to the final x and y - * position - * - * @see #forceFinished(boolean) - */ - public void abortAnimation() { - mCurrX = mFinalX; - mCurrY = mFinalY; - mFinished = true; - } - - /** - * Extend the scroll animation. This allows a running animation to scroll - * further and longer, when used with {@link #setFinalX(int)} or {@link #setFinalY(int)}. - * - * @param extend Additional time to scroll in milliseconds. - * @see #setFinalX(int) - * @see #setFinalY(int) - */ - public void extendDuration(int extend) { - int passed = timePassed(); - mDuration = passed + extend; - mDurationReciprocal = 1.0f / mDuration; - mFinished = false; - } - - /** - * Returns the time elapsed since the beginning of the scrolling. - * - * @return The elapsed time in milliseconds. - */ - public int timePassed() { - return (int)(AnimationUtils.currentAnimationTimeMillis() - mStartTime); - } - - /** - * Sets the final position (X) for this scroller. - * - * @param newX The new X offset as an absolute distance from the origin. - * @see #extendDuration(int) - * @see #setFinalY(int) - */ - public void setFinalX(int newX) { - mFinalX = newX; - mDeltaX = mFinalX - mStartX; - mFinished = false; - } - - /** - * Sets the final position (Y) for this scroller. - * - * @param newY The new Y offset as an absolute distance from the origin. - * @see #extendDuration(int) - * @see #setFinalX(int) - */ - public void setFinalY(int newY) { - mFinalY = newY; - mDeltaY = mFinalY - mStartY; - mFinished = false; - } - - /** - * @hide - */ - public boolean isScrollingInDirection(float xvel, float yvel) { - return !mFinished && Math.signum(xvel) == Math.signum(mFinalX - mStartX) && - Math.signum(yvel) == Math.signum(mFinalY - mStartY); - } -} diff --git a/gallerycommon/src/com/android/gallery3d/common/Utils.java b/gallerycommon/src/com/android/gallery3d/common/Utils.java deleted file mode 100644 index 614a081c8..000000000 --- a/gallerycommon/src/com/android/gallery3d/common/Utils.java +++ /dev/null @@ -1,340 +0,0 @@ -/* - * Copyright (C) 2010 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.gallery3d.common; - -import android.content.Context; -import android.content.pm.PackageInfo; -import android.content.pm.PackageManager.NameNotFoundException; -import android.database.Cursor; -import android.os.Build; -import android.os.ParcelFileDescriptor; -import android.text.TextUtils; -import android.util.Log; - -import java.io.Closeable; -import java.io.IOException; -import java.io.InterruptedIOException; - -public class Utils { - private static final String TAG = "Utils"; - private static final String DEBUG_TAG = "GalleryDebug"; - - private static final long POLY64REV = 0x95AC9329AC4BC9B5L; - private static final long INITIALCRC = 0xFFFFFFFFFFFFFFFFL; - - private static long[] sCrcTable = new long[256]; - - private static final boolean IS_DEBUG_BUILD = - Build.TYPE.equals("eng") || Build.TYPE.equals("userdebug"); - - private static final String MASK_STRING = "********************************"; - - // Throws AssertionError if the input is false. - public static void assertTrue(boolean cond) { - if (!cond) { - throw new AssertionError(); - } - } - - // Throws AssertionError with the message. We had a method having the form - // assertTrue(boolean cond, String message, Object ... args); - // However a call to that method will cause memory allocation even if the - // condition is false (due to autoboxing generated by "Object ... args"), - // so we don't use that anymore. - public static void fail(String message, Object ... args) { - throw new AssertionError( - args.length == 0 ? message : String.format(message, args)); - } - - // Throws NullPointerException if the input is null. - public static T checkNotNull(T object) { - if (object == null) throw new NullPointerException(); - return object; - } - - // Returns true if two input Object are both null or equal - // to each other. - public static boolean equals(Object a, Object b) { - return (a == b) || (a == null ? false : a.equals(b)); - } - - // Returns the next power of two. - // Returns the input if it is already power of 2. - // Throws IllegalArgumentException if the input is <= 0 or - // the answer overflows. - public static int nextPowerOf2(int n) { - if (n <= 0 || n > (1 << 30)) throw new IllegalArgumentException("n is invalid: " + n); - n -= 1; - n |= n >> 16; - n |= n >> 8; - n |= n >> 4; - n |= n >> 2; - n |= n >> 1; - return n + 1; - } - - // Returns the previous power of two. - // Returns the input if it is already power of 2. - // Throws IllegalArgumentException if the input is <= 0 - public static int prevPowerOf2(int n) { - if (n <= 0) throw new IllegalArgumentException(); - return Integer.highestOneBit(n); - } - - // Returns the input value x clamped to the range [min, max]. - public static int clamp(int x, int min, int max) { - if (x > max) return max; - if (x < min) return min; - return x; - } - - // Returns the input value x clamped to the range [min, max]. - public static float clamp(float x, float min, float max) { - if (x > max) return max; - if (x < min) return min; - return x; - } - - // Returns the input value x clamped to the range [min, max]. - public static long clamp(long x, long min, long max) { - if (x > max) return max; - if (x < min) return min; - return x; - } - - public static boolean isOpaque(int color) { - return color >>> 24 == 0xFF; - } - - public static void swap(int[] array, int i, int j) { - int temp = array[i]; - array[i] = array[j]; - array[j] = temp; - } - - /** - * A function thats returns a 64-bit crc for string - * - * @param in input string - * @return a 64-bit crc value - */ - public static final long crc64Long(String in) { - if (in == null || in.length() == 0) { - return 0; - } - return crc64Long(getBytes(in)); - } - - static { - // http://bioinf.cs.ucl.ac.uk/downloads/crc64/crc64.c - long part; - for (int i = 0; i < 256; i++) { - part = i; - for (int j = 0; j < 8; j++) { - long x = ((int) part & 1) != 0 ? POLY64REV : 0; - part = (part >> 1) ^ x; - } - sCrcTable[i] = part; - } - } - - public static final long crc64Long(byte[] buffer) { - long crc = INITIALCRC; - for (int k = 0, n = buffer.length; k < n; ++k) { - crc = sCrcTable[(((int) crc) ^ buffer[k]) & 0xff] ^ (crc >> 8); - } - return crc; - } - - public static byte[] getBytes(String in) { - byte[] result = new byte[in.length() * 2]; - int output = 0; - for (char ch : in.toCharArray()) { - result[output++] = (byte) (ch & 0xFF); - result[output++] = (byte) (ch >> 8); - } - return result; - } - - public static void closeSilently(Closeable c) { - if (c == null) return; - try { - c.close(); - } catch (IOException t) { - Log.w(TAG, "close fail ", t); - } - } - - public static int compare(long a, long b) { - return a < b ? -1 : a == b ? 0 : 1; - } - - public static int ceilLog2(float value) { - int i; - for (i = 0; i < 31; i++) { - if ((1 << i) >= value) break; - } - return i; - } - - public static int floorLog2(float value) { - int i; - for (i = 0; i < 31; i++) { - if ((1 << i) > value) break; - } - return i - 1; - } - - public static void closeSilently(ParcelFileDescriptor fd) { - try { - if (fd != null) fd.close(); - } catch (Throwable t) { - Log.w(TAG, "fail to close", t); - } - } - - public static void closeSilently(Cursor cursor) { - try { - if (cursor != null) cursor.close(); - } catch (Throwable t) { - Log.w(TAG, "fail to close", t); - } - } - - public static float interpolateAngle( - float source, float target, float progress) { - // interpolate the angle from source to target - // We make the difference in the range of [-179, 180], this is the - // shortest path to change source to target. - float diff = target - source; - if (diff < 0) diff += 360f; - if (diff > 180) diff -= 360f; - - float result = source + diff * progress; - return result < 0 ? result + 360f : result; - } - - public static float interpolateScale( - float source, float target, float progress) { - return source + progress * (target - source); - } - - public static String ensureNotNull(String value) { - return value == null ? "" : value; - } - - public static float parseFloatSafely(String content, float defaultValue) { - if (content == null) return defaultValue; - try { - return Float.parseFloat(content); - } catch (NumberFormatException e) { - return defaultValue; - } - } - - public static int parseIntSafely(String content, int defaultValue) { - if (content == null) return defaultValue; - try { - return Integer.parseInt(content); - } catch (NumberFormatException e) { - return defaultValue; - } - } - - public static boolean isNullOrEmpty(String exifMake) { - return TextUtils.isEmpty(exifMake); - } - - public static void waitWithoutInterrupt(Object object) { - try { - object.wait(); - } catch (InterruptedException e) { - Log.w(TAG, "unexpected interrupt: " + object); - } - } - - public static boolean handleInterrruptedException(Throwable e) { - // A helper to deal with the interrupt exception - // If an interrupt detected, we will setup the bit again. - if (e instanceof InterruptedIOException - || e instanceof InterruptedException) { - Thread.currentThread().interrupt(); - return true; - } - return false; - } - - /** - * @return String with special XML characters escaped. - */ - public static String escapeXml(String s) { - StringBuilder sb = new StringBuilder(); - for (int i = 0, len = s.length(); i < len; ++i) { - char c = s.charAt(i); - switch (c) { - case '<': sb.append("<"); break; - case '>': sb.append(">"); break; - case '\"': sb.append("""); break; - case '\'': sb.append("'"); break; - case '&': sb.append("&"); break; - default: sb.append(c); - } - } - return sb.toString(); - } - - public static String getUserAgent(Context context) { - PackageInfo packageInfo; - try { - packageInfo = context.getPackageManager().getPackageInfo(context.getPackageName(), 0); - } catch (NameNotFoundException e) { - throw new IllegalStateException("getPackageInfo failed"); - } - return String.format("%s/%s; %s/%s/%s/%s; %s/%s/%s", - packageInfo.packageName, - packageInfo.versionName, - Build.BRAND, - Build.DEVICE, - Build.MODEL, - Build.ID, - Build.VERSION.SDK_INT, - Build.VERSION.RELEASE, - Build.VERSION.INCREMENTAL); - } - - public static String[] copyOf(String[] source, int newSize) { - String[] result = new String[newSize]; - newSize = Math.min(source.length, newSize); - System.arraycopy(source, 0, result, 0, newSize); - return result; - } - - // Mask information for debugging only. It returns info.toString() directly - // for debugging build (i.e., 'eng' and 'userdebug') and returns a mask ("****") - // in release build to protect the information (e.g. for privacy issue). - public static String maskDebugInfo(Object info) { - if (info == null) return null; - String s = info.toString(); - int length = Math.min(s.length(), MASK_STRING.length()); - return IS_DEBUG_BUILD ? s : MASK_STRING.substring(0, length); - } - - // This method should be ONLY used for debugging. - public static void debug(String message, Object ... args) { - Log.v(DEBUG_TAG, String.format(message, args)); - } -} diff --git a/gallerycommon/src/com/android/gallery3d/jpegstream/JPEGInputStream.java b/gallerycommon/src/com/android/gallery3d/jpegstream/JPEGInputStream.java deleted file mode 100644 index 44ccd4c6b..000000000 --- a/gallerycommon/src/com/android/gallery3d/jpegstream/JPEGInputStream.java +++ /dev/null @@ -1,193 +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.gallery3d.jpegstream; - -import android.graphics.Point; - -import java.io.FilterInputStream; -import java.io.IOException; -import java.io.InputStream; - -public class JPEGInputStream extends FilterInputStream { - private long JNIPointer = 0; // Used by JNI code. Don't touch. - - private boolean mValidConfig = false; - private boolean mConfigChanged = false; - private int mFormat = -1; - private byte[] mTmpBuffer = new byte[1]; - private int mWidth = 0; - private int mHeight = 0; - - public JPEGInputStream(InputStream in) { - super(in); - } - - public JPEGInputStream(InputStream in, int format) { - super(in); - setConfig(format); - } - - public boolean setConfig(int format) { - // Make sure format is valid - switch (format) { - case JpegConfig.FORMAT_GRAYSCALE: - case JpegConfig.FORMAT_RGB: - case JpegConfig.FORMAT_ABGR: - case JpegConfig.FORMAT_RGBA: - break; - default: - return false; - } - mFormat = format; - mValidConfig = true; - mConfigChanged = true; - return true; - } - - public Point getDimensions() throws IOException { - if (mValidConfig) { - applyConfigChange(); - return new Point(mWidth, mHeight); - } - return null; - } - - @Override - public int available() { - return 0; // TODO - } - - @Override - public void close() throws IOException { - cleanup(); - super.close(); - } - - @Override - public synchronized void mark(int readlimit) { - // Do nothing - } - - @Override - public boolean markSupported() { - return false; - } - - @Override - public int read() throws IOException { - read(mTmpBuffer, 0, 1); - return 0xFF & mTmpBuffer[0]; - } - - @Override - public int read(byte[] buffer) throws IOException { - return read(buffer, 0, buffer.length); - } - - @Override - public int read(byte[] buffer, int offset, int count) throws IOException { - if (offset < 0 || count < 0 || (offset + count) > buffer.length) { - throw new ArrayIndexOutOfBoundsException(String.format( - " buffer length %d, offset %d, length %d", - buffer.length, offset, count)); - } - if (!mValidConfig) { - return 0; - } - applyConfigChange(); - int flag = JpegConfig.J_ERROR_FATAL; - try { - flag = readDecodedBytes(buffer, offset, count); - } finally { - if (flag < 0) { - cleanup(); - } - } - if (flag < 0) { - switch (flag) { - case JpegConfig.J_DONE: - return -1; // Returns -1 after reading EOS. - default: - throw new IOException("Error reading jpeg stream"); - } - } - return flag; - } - - @Override - public synchronized void reset() throws IOException { - throw new IOException("Reset not supported."); - } - - @Override - public long skip(long byteCount) throws IOException { - if (byteCount <= 0) { - return 0; - } - // Shorten skip to a reasonable amount - int flag = skipDecodedBytes((int) (0x7FFFFFFF & byteCount)); - if (flag < 0) { - switch (flag) { - case JpegConfig.J_DONE: - return 0; // Returns 0 after reading EOS. - default: - throw new IOException("Error skipping jpeg stream"); - } - } - return flag; - } - - @Override - protected void finalize() throws Throwable { - try { - cleanup(); - } finally { - super.finalize(); - } - } - - private void applyConfigChange() throws IOException { - if (mConfigChanged) { - cleanup(); - Point dimens = new Point(0, 0); - int flag = setup(dimens, in, mFormat); - switch(flag) { - case JpegConfig.J_SUCCESS: - break; // allow setup to continue - case JpegConfig.J_ERROR_BAD_ARGS: - throw new IllegalArgumentException("Bad arguments to read"); - default: - throw new IOException("Error to reading jpeg headers."); - } - mWidth = dimens.x; - mHeight = dimens.y; - mConfigChanged = false; - } - } - - native private int setup(Point dimens, InputStream in, int format); - - native private void cleanup(); - - native private int readDecodedBytes( byte[] inBuffer, int offset, int inCount); - - native private int skipDecodedBytes(int bytes); - - static { - System.loadLibrary("jni_jpegstream"); - } -} diff --git a/gallerycommon/src/com/android/gallery3d/jpegstream/JPEGOutputStream.java b/gallerycommon/src/com/android/gallery3d/jpegstream/JPEGOutputStream.java deleted file mode 100644 index c49d3759c..000000000 --- a/gallerycommon/src/com/android/gallery3d/jpegstream/JPEGOutputStream.java +++ /dev/null @@ -1,144 +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.gallery3d.jpegstream; - -import java.io.FilterOutputStream; -import java.io.IOException; -import java.io.OutputStream; -public class JPEGOutputStream extends FilterOutputStream { - private long JNIPointer = 0; // Used by JNI code. Don't touch. - - private byte[] mTmpBuffer = new byte[1]; - private int mWidth = 0; - private int mHeight = 0; - private int mQuality = 0; - private int mFormat = -1; - private boolean mValidConfig = false; - private boolean mConfigChanged = false; - - public JPEGOutputStream(OutputStream out) { - super(out); - } - - public JPEGOutputStream(OutputStream out, int width, int height, int quality, - int format) { - super(out); - setConfig(width, height, quality, format); - } - - public boolean setConfig(int width, int height, int quality, int format) { - // Clamp quality to range (0, 100] - quality = Math.max(Math.min(quality, 100), 1); - - // Make sure format is valid - switch (format) { - case JpegConfig.FORMAT_GRAYSCALE: - case JpegConfig.FORMAT_RGB: - case JpegConfig.FORMAT_ABGR: - case JpegConfig.FORMAT_RGBA: - break; - default: - return false; - } - - // If valid, set configuration - if (width > 0 && height > 0) { - mWidth = width; - mHeight = height; - mFormat = format; - mQuality = quality; - mValidConfig = true; - mConfigChanged = true; - } else { - return false; - } - - return mValidConfig; - } - - @Override - public void close() throws IOException { - cleanup(); - super.close(); - } - - @Override - public void write(byte[] buffer, int offset, int length) throws IOException { - if (offset < 0 || length < 0 || (offset + length) > buffer.length) { - throw new ArrayIndexOutOfBoundsException(String.format( - " buffer length %d, offset %d, length %d", - buffer.length, offset, length)); - } - if (!mValidConfig) { - return; - } - if (mConfigChanged) { - cleanup(); - int flag = setup(out, mWidth, mHeight, mFormat, mQuality); - switch(flag) { - case JpegConfig.J_SUCCESS: - break; // allow setup to continue - case JpegConfig.J_ERROR_BAD_ARGS: - throw new IllegalArgumentException("Bad arguments to write"); - default: - throw new IOException("Error to writing jpeg headers."); - } - mConfigChanged = false; - } - int returnCode = JpegConfig.J_ERROR_FATAL; - try { - returnCode = writeInputBytes(buffer, offset, length); - } finally { - if (returnCode < 0) { - cleanup(); - } - } - if (returnCode < 0) { - throw new IOException("Error writing jpeg stream"); - } - } - - @Override - public void write(byte[] buffer) throws IOException { - write(buffer, 0, buffer.length); - } - - @Override - public void write(int oneByte) throws IOException { - mTmpBuffer[0] = (byte) oneByte; - write(mTmpBuffer); - } - - @Override - protected void finalize() throws Throwable { - try { - cleanup(); - } finally { - super.finalize(); - } - } - - native private int setup(OutputStream out, int width, int height, int format, int quality); - - native private void cleanup(); - - native private int writeInputBytes(byte[] inBuffer, int offset, int inCount); - - static { - System.loadLibrary("jni_jpegstream"); - } -} diff --git a/gallerycommon/src/com/android/gallery3d/jpegstream/JpegConfig.java b/gallerycommon/src/com/android/gallery3d/jpegstream/JpegConfig.java deleted file mode 100644 index e514e3b8d..000000000 --- a/gallerycommon/src/com/android/gallery3d/jpegstream/JpegConfig.java +++ /dev/null @@ -1,32 +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.gallery3d.jpegstream; - -public interface JpegConfig { - // Pixel formats - public static final int FORMAT_GRAYSCALE = 0x001; // 1 byte/pixel - public static final int FORMAT_RGB = 0x003; // 3 bytes/pixel RGBRGBRGBRGB... - public static final int FORMAT_RGBA = 0x004; // 4 bytes/pixel RGBARGBARGBARGBA... - public static final int FORMAT_ABGR = 0x104; // 4 bytes/pixel ABGRABGRABGR... - - // Jni error codes - static final int J_SUCCESS = 0; - static final int J_ERROR_FATAL = -1; - static final int J_ERROR_BAD_ARGS = -2; - static final int J_EXCEPTION = -3; - static final int J_DONE = -4; -} diff --git a/gallerycommon/src/com/android/gallery3d/jpegstream/StreamUtils.java b/gallerycommon/src/com/android/gallery3d/jpegstream/StreamUtils.java deleted file mode 100644 index abd8f681e..000000000 --- a/gallerycommon/src/com/android/gallery3d/jpegstream/StreamUtils.java +++ /dev/null @@ -1,80 +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.gallery3d.jpegstream; - -import java.nio.ByteOrder; - -public class StreamUtils { - - private StreamUtils() { - } - - /** - * Copies the input byte array into the output int array with the given - * endianness. If input is not a multiple of 4, ignores the last 1-3 bytes - * and returns true. - */ - public static boolean byteToIntArray(int[] output, byte[] input, ByteOrder endianness) { - int length = input.length - (input.length % 4); - if (output.length * 4 < length) { - throw new ArrayIndexOutOfBoundsException("Output array is too short to hold input"); - } - if (endianness == ByteOrder.BIG_ENDIAN) { - for (int i = 0, j = 0; i < output.length; i++, j += 4) { - output[i] = ((input[j] & 0xFF) << 24) | ((input[j + 1] & 0xFF) << 16) - | ((input[j + 2] & 0xFF) << 8) | ((input[j + 3] & 0xFF)); - } - } else { - for (int i = 0, j = 0; i < output.length; i++, j += 4) { - output[i] = ((input[j + 3] & 0xFF) << 24) | ((input[j + 2] & 0xFF) << 16) - | ((input[j + 1] & 0xFF) << 8) | ((input[j] & 0xFF)); - } - } - return input.length % 4 != 0; - } - - public static int[] byteToIntArray(byte[] input, ByteOrder endianness) { - int[] output = new int[input.length / 4]; - byteToIntArray(output, input, endianness); - return output; - } - - /** - * Uses native endianness. - */ - public static int[] byteToIntArray(byte[] input) { - return byteToIntArray(input, ByteOrder.nativeOrder()); - } - - /** - * Returns the number of bytes in a pixel for a given format defined in - * JpegConfig. - */ - public static int pixelSize(int format) { - switch (format) { - case JpegConfig.FORMAT_ABGR: - case JpegConfig.FORMAT_RGBA: - return 4; - case JpegConfig.FORMAT_RGB: - return 3; - case JpegConfig.FORMAT_GRAYSCALE: - return 1; - default: - return -1; - } - } -} diff --git a/gallerycommon/src/com/android/gallery3d/util/Future.java b/gallerycommon/src/com/android/gallery3d/util/Future.java deleted file mode 100644 index 580a2a120..000000000 --- a/gallerycommon/src/com/android/gallery3d/util/Future.java +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright (C) 2010 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.gallery3d.util; - -// This Future differs from the java.util.concurrent.Future in these aspects: -// -// - Once cancel() is called, isCancelled() always returns true. It is a sticky -// flag used to communicate to the implementation. The implmentation may -// ignore that flag. Regardless whether the Future is cancelled, a return -// value will be provided to get(). The implementation may choose to return -// null if it finds the Future is cancelled. -// -// - get() does not throw exceptions. -// -public interface Future { - public void cancel(); - public boolean isCancelled(); - public boolean isDone(); - public T get(); - public void waitDone(); -} diff --git a/gallerycommon/src/com/android/gallery3d/util/FutureListener.java b/gallerycommon/src/com/android/gallery3d/util/FutureListener.java deleted file mode 100644 index ed1f820c7..000000000 --- a/gallerycommon/src/com/android/gallery3d/util/FutureListener.java +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright (C) 2010 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.gallery3d.util; - -public interface FutureListener { - public void onFutureDone(Future future); -} diff --git a/gallerycommon/src/com/android/gallery3d/util/PriorityThreadFactory.java b/gallerycommon/src/com/android/gallery3d/util/PriorityThreadFactory.java deleted file mode 100644 index 30d8e4a96..000000000 --- a/gallerycommon/src/com/android/gallery3d/util/PriorityThreadFactory.java +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright (C) 2010 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.gallery3d.util; - - -import android.os.Process; - -import java.util.concurrent.ThreadFactory; -import java.util.concurrent.atomic.AtomicInteger; - -/** - * A thread factory that creates threads with a given thread priority. - */ -public class PriorityThreadFactory implements ThreadFactory { - - private final int mPriority; - private final AtomicInteger mNumber = new AtomicInteger(); - private final String mName; - - public PriorityThreadFactory(String name, int priority) { - mName = name; - mPriority = priority; - } - - @Override - public Thread newThread(Runnable r) { - return new Thread(r, mName + '-' + mNumber.getAndIncrement()) { - @Override - public void run() { - Process.setThreadPriority(mPriority); - super.run(); - } - }; - } - -} diff --git a/gallerycommon/src/com/android/gallery3d/util/ThreadPool.java b/gallerycommon/src/com/android/gallery3d/util/ThreadPool.java deleted file mode 100644 index 115dc6625..000000000 --- a/gallerycommon/src/com/android/gallery3d/util/ThreadPool.java +++ /dev/null @@ -1,268 +0,0 @@ -/* - * Copyright (C) 2010 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.gallery3d.util; - -import android.util.Log; - -import java.util.concurrent.Executor; -import java.util.concurrent.LinkedBlockingQueue; -import java.util.concurrent.ThreadPoolExecutor; -import java.util.concurrent.TimeUnit; - -public class ThreadPool { - @SuppressWarnings("unused") - private static final String TAG = "ThreadPool"; - private static final int CORE_POOL_SIZE = 4; - private static final int MAX_POOL_SIZE = 8; - private static final int KEEP_ALIVE_TIME = 10; // 10 seconds - - // Resource type - public static final int MODE_NONE = 0; - public static final int MODE_CPU = 1; - public static final int MODE_NETWORK = 2; - - public static final JobContext JOB_CONTEXT_STUB = new JobContextStub(); - - ResourceCounter mCpuCounter = new ResourceCounter(2); - ResourceCounter mNetworkCounter = new ResourceCounter(2); - - // A Job is like a Callable, but it has an addition JobContext parameter. - public interface Job { - public T run(JobContext jc); - } - - public interface JobContext { - boolean isCancelled(); - void setCancelListener(CancelListener listener); - boolean setMode(int mode); - } - - private static class JobContextStub implements JobContext { - @Override - public boolean isCancelled() { - return false; - } - - @Override - public void setCancelListener(CancelListener listener) { - } - - @Override - public boolean setMode(int mode) { - return true; - } - } - - public interface CancelListener { - public void onCancel(); - } - - private static class ResourceCounter { - public int value; - public ResourceCounter(int v) { - value = v; - } - } - - private final Executor mExecutor; - - public ThreadPool() { - this(CORE_POOL_SIZE, MAX_POOL_SIZE); - } - - public ThreadPool(int initPoolSize, int maxPoolSize) { - mExecutor = new ThreadPoolExecutor( - initPoolSize, maxPoolSize, KEEP_ALIVE_TIME, - TimeUnit.SECONDS, new LinkedBlockingQueue(), - new PriorityThreadFactory("thread-pool", - android.os.Process.THREAD_PRIORITY_BACKGROUND)); - } - - // Submit a job to the thread pool. The listener will be called when the - // job is finished (or cancelled). - public Future submit(Job job, FutureListener listener) { - Worker w = new Worker(job, listener); - mExecutor.execute(w); - return w; - } - - public Future submit(Job job) { - return submit(job, null); - } - - private class Worker implements Runnable, Future, JobContext { - @SuppressWarnings("hiding") - private static final String TAG = "Worker"; - private Job mJob; - private FutureListener mListener; - private CancelListener mCancelListener; - private ResourceCounter mWaitOnResource; - private volatile boolean mIsCancelled; - private boolean mIsDone; - private T mResult; - private int mMode; - - public Worker(Job job, FutureListener listener) { - mJob = job; - mListener = listener; - } - - // This is called by a thread in the thread pool. - @Override - public void run() { - T result = null; - - // A job is in CPU mode by default. setMode returns false - // if the job is cancelled. - if (setMode(MODE_CPU)) { - try { - result = mJob.run(this); - } catch (Throwable ex) { - Log.w(TAG, "Exception in running a job", ex); - } - } - - synchronized(this) { - setMode(MODE_NONE); - mResult = result; - mIsDone = true; - notifyAll(); - } - if (mListener != null) mListener.onFutureDone(this); - } - - // Below are the methods for Future. - @Override - public synchronized void cancel() { - if (mIsCancelled) return; - mIsCancelled = true; - if (mWaitOnResource != null) { - synchronized (mWaitOnResource) { - mWaitOnResource.notifyAll(); - } - } - if (mCancelListener != null) { - mCancelListener.onCancel(); - } - } - - @Override - public boolean isCancelled() { - return mIsCancelled; - } - - @Override - public synchronized boolean isDone() { - return mIsDone; - } - - @Override - public synchronized T get() { - while (!mIsDone) { - try { - wait(); - } catch (Exception ex) { - Log.w(TAG, "ingore exception", ex); - // ignore. - } - } - return mResult; - } - - @Override - public void waitDone() { - get(); - } - - // Below are the methods for JobContext (only called from the - // thread running the job) - @Override - public synchronized void setCancelListener(CancelListener listener) { - mCancelListener = listener; - if (mIsCancelled && mCancelListener != null) { - mCancelListener.onCancel(); - } - } - - @Override - public boolean setMode(int mode) { - // Release old resource - ResourceCounter rc = modeToCounter(mMode); - if (rc != null) releaseResource(rc); - mMode = MODE_NONE; - - // Acquire new resource - rc = modeToCounter(mode); - if (rc != null) { - if (!acquireResource(rc)) { - return false; - } - mMode = mode; - } - - return true; - } - - private ResourceCounter modeToCounter(int mode) { - if (mode == MODE_CPU) { - return mCpuCounter; - } else if (mode == MODE_NETWORK) { - return mNetworkCounter; - } else { - return null; - } - } - - private boolean acquireResource(ResourceCounter counter) { - while (true) { - synchronized (this) { - if (mIsCancelled) { - mWaitOnResource = null; - return false; - } - mWaitOnResource = counter; - } - - synchronized (counter) { - if (counter.value > 0) { - counter.value--; - break; - } else { - try { - counter.wait(); - } catch (InterruptedException ex) { - // ignore. - } - } - } - } - - synchronized (this) { - mWaitOnResource = null; - } - - return true; - } - - private void releaseResource(ResourceCounter counter) { - synchronized (counter) { - counter.value++; - counter.notifyAll(); - } - } - } -} diff --git a/proguard.flags b/proguard.flags index 39e43cbae..198df7556 100644 --- a/proguard.flags +++ b/proguard.flags @@ -1,14 +1,6 @@ # Disable the warnings of using dynamic method call in common library. -dontnote com.android.gallery3d.common.* -# Keep all classes extended from com.android.gallery3d.common.Entry -# Since we annotate on the fields and use reflection to create SQL -# according to those field. - --keep class * extends com.android.gallery3d.common.Entry { - @com.android.gallery3d.common.Entry$Column ; -} - # ctors of subclasses of CameraPreference are called with Java reflection. -keep class * extends com.android.camera.CameraPreference { (...); @@ -40,9 +32,6 @@ # Disable the warnings of using dynamic method calls in EffectsRecorder -dontnote com.android.camera.EffectsRecorder --keep class android.support.v8.renderscript.** { *; } - - # For unit testing: # - Required for running exif tests on userdebug @@ -53,21 +42,3 @@ *** closeSilently(...); } -# - Required for running blobcache tests on userdebug --keep class com.android.gallery3d.common.BlobCache { *; } - -# - Required for running glcanvas tests on userdebug --keep class com.android.gallery3d.ui.GLPaint { *; } --keep class com.android.gallery3d.ui.GLCanvas { *; } --keep class com.android.gallery3d.glrenderer.GLPaint { *; } --keep class com.android.gallery3d.glrenderer.GLCanvas { *; } --keep class com.android.gallery3d.ui.GLView { *; } --keepclassmembers class com.android.gallery3d.util.IntArray { - *** toArray(...); -} --keep class com.android.gallery3d.util.ProfileData { *; } - -# - Required for running jpeg stream tests on userdebug --keep class com.android.gallery3d.jpegstream.JPEGOutputStream { *; } --keep class com.android.gallery3d.jpegstream.JPEGInputStream { *; } --keep class com.android.gallery3d.jpegstream.StreamUtils { *; } diff --git a/src/com/android/camera/AndroidCameraManagerImpl.java b/src/com/android/camera/AndroidCameraManagerImpl.java index 667630609..00fe905a9 100644 --- a/src/com/android/camera/AndroidCameraManagerImpl.java +++ b/src/com/android/camera/AndroidCameraManagerImpl.java @@ -16,7 +16,7 @@ package com.android.camera; -import static com.android.camera.Util.Assert; +import static com.android.camera.util.CameraUtil.Assert; import java.io.IOException; @@ -39,7 +39,7 @@ import android.os.Message; import android.util.Log; import android.view.SurfaceHolder; -import com.android.camera.support.common.ApiHelper; +import com.android.camera.util.ApiHelper; /** * A class to implement {@link CameraManager} of the Android camera framework. diff --git a/src/com/android/camera/CameraActivity.java b/src/com/android/camera/CameraActivity.java index 508dfea34..ae2f34562 100644 --- a/src/com/android/camera/CameraActivity.java +++ b/src/com/android/camera/CameraActivity.java @@ -50,10 +50,11 @@ import com.android.camera.data.FixedLastDataAdapter; import com.android.camera.data.LocalData; import com.android.camera.data.LocalDataAdapter; import com.android.camera.data.SimpleViewData; -import com.android.camera.support.common.ApiHelper; +import com.android.camera.util.ApiHelper; import com.android.camera.ui.CameraSwitcher; import com.android.camera.ui.CameraSwitcher.CameraSwitchListener; import com.android.camera.ui.FilmStripView; +import com.android.camera.util.CameraUtil; import com.android.camera.util.PhotoSphereHelper.PanoramaViewHelper; import com.android.camera.util.PhotoSphereHelper; import com.android.camera.util.RefocusHelper; @@ -280,10 +281,10 @@ public class CameraActivity extends Activity ContentResolver cr = getContentResolver(); String mimeType = cr.getType(uri); if (mimeType.startsWith("video/")) { - sendBroadcast(new Intent(Util.ACTION_NEW_VIDEO, uri)); + sendBroadcast(new Intent(CameraUtil.ACTION_NEW_VIDEO, uri)); mDataAdapter.addNewVideo(cr, uri); } else if (mimeType.startsWith("image/")) { - Util.broadcastNewPicture(this, uri); + CameraUtil.broadcastNewPicture(this, uri); mDataAdapter.addNewPhoto(cr, uri); } else if (mimeType.startsWith("application/stitching-preview")) { mDataAdapter.addNewPhoto(cr, uri); diff --git a/src/com/android/camera/CameraHolder.java b/src/com/android/camera/CameraHolder.java index d913df709..0ffc73663 100644 --- a/src/com/android/camera/CameraHolder.java +++ b/src/com/android/camera/CameraHolder.java @@ -16,7 +16,7 @@ package com.android.camera; -import static com.android.camera.Util.Assert; +import static com.android.camera.util.CameraUtil.Assert; import android.hardware.Camera.CameraInfo; import android.hardware.Camera.Parameters; diff --git a/src/com/android/camera/CameraManager.java b/src/com/android/camera/CameraManager.java index fd9b2ced3..4a8057d3f 100644 --- a/src/com/android/camera/CameraManager.java +++ b/src/com/android/camera/CameraManager.java @@ -27,7 +27,7 @@ import android.hardware.Camera.Parameters; import android.os.Handler; import android.view.SurfaceHolder; -import com.android.camera.support.common.ApiHelper; +import com.android.camera.util.ApiHelper; /** * An interface which provides possible camera device operations. diff --git a/src/com/android/camera/CameraScreenNail.java b/src/com/android/camera/CameraScreenNail.java deleted file mode 100644 index 9fe2c067a..000000000 --- a/src/com/android/camera/CameraScreenNail.java +++ /dev/null @@ -1,526 +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.camera; - -import android.annotation.SuppressLint; -import android.annotation.TargetApi; -import android.content.Context; -import android.graphics.SurfaceTexture; -import android.opengl.Matrix; -import android.util.Log; -import com.android.camera.support.common.ApiHelper; -import com.android.camera.support.glrenderer.GLCanvas; -import com.android.camera.support.glrenderer.RawTexture; -import com.android.camera.support.ui.SurfaceTextureScreenNail; - -/* - * This is a ScreenNail which can display camera's preview. - */ -@TargetApi(ApiHelper.VERSION_CODES.HONEYCOMB) -public class CameraScreenNail extends SurfaceTextureScreenNail { - private static final String TAG = "CAM_ScreenNail"; - private static final int ANIM_NONE = 0; - // Capture animation is about to start. - private static final int ANIM_CAPTURE_START = 1; - // Capture animation is running. - private static final int ANIM_CAPTURE_RUNNING = 2; - // Switch camera animation needs to copy texture. - private static final int ANIM_SWITCH_COPY_TEXTURE = 3; - // Switch camera animation shows the initial feedback by darkening the - // preview. - private static final int ANIM_SWITCH_DARK_PREVIEW = 4; - // Switch camera animation is waiting for the first frame. - private static final int ANIM_SWITCH_WAITING_FIRST_FRAME = 5; - // Switch camera animation is about to start. - private static final int ANIM_SWITCH_START = 6; - // Switch camera animation is running. - private static final int ANIM_SWITCH_RUNNING = 7; - - private boolean mVisible; - // True if first onFrameAvailable has been called. If screen nail is drawn - // too early, it will be all white. - private boolean mFirstFrameArrived; - private Listener mListener; - private final float[] mTextureTransformMatrix = new float[16]; - - // Animation. - private CaptureAnimManager mCaptureAnimManager; - private SwitchAnimManager mSwitchAnimManager = new SwitchAnimManager(); - private int mAnimState = ANIM_NONE; - private RawTexture mAnimTexture; - // Some methods are called by GL thread and some are called by main thread. - // This protects mAnimState, mVisible, and surface texture. This also makes - // sure some code are atomic. For example, requestRender and setting - // mAnimState. - private Object mLock = new Object(); - - private OnFrameDrawnListener mOneTimeFrameDrawnListener; - private int mRenderWidth; - private int mRenderHeight; - // This represents the scaled, uncropped size of the texture - // Needed for FaceView - private int mUncroppedRenderWidth; - private int mUncroppedRenderHeight; - private float mScaleX = 1f, mScaleY = 1f; - private boolean mFullScreen; - private boolean mEnableAspectRatioClamping = false; - private boolean mAcquireTexture = false; - private final DrawClient mDefaultDraw = new DrawClient() { - @Override - public void onDraw(GLCanvas canvas, int x, int y, int width, int height) { - CameraScreenNail.super.draw(canvas, x, y, width, height); - } - - @Override - public boolean requiresSurfaceTexture() { - return true; - } - - @Override - public RawTexture copyToTexture(GLCanvas c, RawTexture texture, int w, int h) { - // We shouldn't be here since requireSurfaceTexture() returns true. - return null; - } - }; - private DrawClient mDraw = mDefaultDraw; - private float mAlpha = 1f; - private Runnable mOnFrameDrawnListener; - - public interface Listener { - void requestRender(); - // Preview has been copied to a texture. - void onPreviewTextureCopied(); - - void onCaptureTextureCopied(); - } - - public interface OnFrameDrawnListener { - void onFrameDrawn(CameraScreenNail c); - } - - public interface DrawClient { - void onDraw(GLCanvas canvas, int x, int y, int width, int height); - - boolean requiresSurfaceTexture(); - // The client should implement this if requiresSurfaceTexture() is false; - RawTexture copyToTexture(GLCanvas c, RawTexture texture, int width, int height); - } - - public CameraScreenNail(Listener listener, Context ctx) { - mListener = listener; - mCaptureAnimManager = new CaptureAnimManager(ctx); - } - - public void setFullScreen(boolean full) { - synchronized (mLock) { - mFullScreen = full; - } - } - - /** - * returns the uncropped, but scaled, width of the rendered texture - */ - public int getUncroppedRenderWidth() { - return mUncroppedRenderWidth; - } - - /** - * returns the uncropped, but scaled, width of the rendered texture - */ - public int getUncroppedRenderHeight() { - return mUncroppedRenderHeight; - } - - @Override - public int getWidth() { - return mEnableAspectRatioClamping ? mRenderWidth : getTextureWidth(); - } - - @Override - public int getHeight() { - return mEnableAspectRatioClamping ? mRenderHeight : getTextureHeight(); - } - - private int getTextureWidth() { - return super.getWidth(); - } - - private int getTextureHeight() { - return super.getHeight(); - } - - @Override - public void setSize(int w, int h) { - super.setSize(w, h); - mEnableAspectRatioClamping = false; - if (mRenderWidth == 0) { - mRenderWidth = w; - mRenderHeight = h; - } - updateRenderSize(); - } - - /** - * Tells the ScreenNail to override the default aspect ratio scaling - * and instead perform custom scaling to basically do a centerCrop instead - * of the default centerInside - * - * Note that calls to setSize will disable this - */ - public void enableAspectRatioClamping() { - mEnableAspectRatioClamping = true; - updateRenderSize(); - } - - private void setPreviewLayoutSize(int w, int h) { - Log.i(TAG, "preview layout size: "+w+"/"+h); - mRenderWidth = w; - mRenderHeight = h; - updateRenderSize(); - } - - private void updateRenderSize() { - if (!mEnableAspectRatioClamping) { - mScaleX = mScaleY = 1f; - mUncroppedRenderWidth = getTextureWidth(); - mUncroppedRenderHeight = getTextureHeight(); - Log.i(TAG, "aspect ratio clamping disabled"); - return; - } - - float aspectRatio; - if (getTextureWidth() > getTextureHeight()) { - aspectRatio = (float) getTextureWidth() / (float) getTextureHeight(); - } else { - aspectRatio = (float) getTextureHeight() / (float) getTextureWidth(); - } - float scaledTextureWidth, scaledTextureHeight; - if (mRenderWidth > mRenderHeight) { - scaledTextureWidth = Math.max(mRenderWidth, - (int) (mRenderHeight * aspectRatio)); - scaledTextureHeight = Math.max(mRenderHeight, - (int)(mRenderWidth / aspectRatio)); - } else { - scaledTextureWidth = Math.max(mRenderWidth, - (int) (mRenderHeight / aspectRatio)); - scaledTextureHeight = Math.max(mRenderHeight, - (int) (mRenderWidth * aspectRatio)); - } - mScaleX = mRenderWidth / scaledTextureWidth; - mScaleY = mRenderHeight / scaledTextureHeight; - mUncroppedRenderWidth = Math.round(scaledTextureWidth); - mUncroppedRenderHeight = Math.round(scaledTextureHeight); - Log.i(TAG, "aspect ratio clamping enabled, surfaceTexture scale: " + mScaleX + ", " + mScaleY); - } - - public void acquireSurfaceTexture() { - synchronized (mLock) { - mFirstFrameArrived = false; - mAnimTexture = new RawTexture(getTextureWidth(), getTextureHeight(), true); - mAcquireTexture = true; - } - mListener.requestRender(); - } - - @Override - public void releaseSurfaceTexture() { - synchronized (mLock) { - if (mAcquireTexture) { - mAcquireTexture = false; - mLock.notifyAll(); - } else { - if (super.getSurfaceTexture() != null) { - super.releaseSurfaceTexture(); - } - mAnimState = ANIM_NONE; // stop the animation - } - } - } - - public void copyTexture() { - synchronized (mLock) { - mListener.requestRender(); - mAnimState = ANIM_SWITCH_COPY_TEXTURE; - } - } - - public void animateSwitchCamera() { - Log.v(TAG, "animateSwitchCamera"); - synchronized (mLock) { - if (mAnimState == ANIM_SWITCH_DARK_PREVIEW) { - // Do not request render here because camera has been just - // started. We do not want to draw black frames. - mAnimState = ANIM_SWITCH_WAITING_FIRST_FRAME; - } - } - } - - public void animateCapture(int displayRotation) { - synchronized (mLock) { - mCaptureAnimManager.setOrientation(displayRotation); - mCaptureAnimManager.animateFlashAndSlide(); - mListener.requestRender(); - mAnimState = ANIM_CAPTURE_START; - } - } - - public RawTexture getAnimationTexture() { - return mAnimTexture; - } - - public void animateFlash(int displayRotation) { - synchronized (mLock) { - mCaptureAnimManager.setOrientation(displayRotation); - mCaptureAnimManager.animateFlash(); - mListener.requestRender(); - mAnimState = ANIM_CAPTURE_START; - } - } - - public void animateSlide() { - synchronized (mLock) { - mCaptureAnimManager.animateSlide(); - mListener.requestRender(); - } - } - - private void callbackIfNeeded() { - if (mOneTimeFrameDrawnListener != null) { - mOneTimeFrameDrawnListener.onFrameDrawn(this); - mOneTimeFrameDrawnListener = null; - } - } - - @Override - protected void updateTransformMatrix(float[] matrix) { - super.updateTransformMatrix(matrix); - Matrix.translateM(matrix, 0, .5f, .5f, 0); - Matrix.scaleM(matrix, 0, mScaleX, mScaleY, 1f); - Matrix.translateM(matrix, 0, -.5f, -.5f, 0); - } - - @SuppressLint("WrongCall") - public void directDraw(GLCanvas canvas, int x, int y, int width, int height) { - DrawClient draw; - synchronized (mLock) { - draw = mDraw; - } - // TODO: Should this call draw or onDraw? - draw.onDraw(canvas, x, y, width, height); - } - - public void setDraw(DrawClient draw) { - synchronized (mLock) { - if (draw == null) { - mDraw = mDefaultDraw; - } else { - mDraw = draw; - } - } - mListener.requestRender(); - } - - @Override - public void draw(GLCanvas canvas, int x, int y, int width, int height) { - synchronized (mLock) { - allocateTextureIfRequested(canvas); - if (!mVisible) mVisible = true; - SurfaceTexture surfaceTexture = getSurfaceTexture(); - if (mDraw.requiresSurfaceTexture() && (surfaceTexture == null || !mFirstFrameArrived)) { - return; - } - if (mOnFrameDrawnListener != null) { - mOnFrameDrawnListener.run(); - mOnFrameDrawnListener = null; - } - float oldAlpha = canvas.getAlpha(); - canvas.setAlpha(mAlpha); - - switch (mAnimState) { - case ANIM_NONE: - directDraw(canvas, x, y, width, height); - break; - case ANIM_SWITCH_COPY_TEXTURE: - copyPreviewTexture(canvas); - mSwitchAnimManager.setReviewDrawingSize(width, height); - mListener.onPreviewTextureCopied(); - mAnimState = ANIM_SWITCH_DARK_PREVIEW; - // The texture is ready. Fall through to draw darkened - // preview. - case ANIM_SWITCH_DARK_PREVIEW: - case ANIM_SWITCH_WAITING_FIRST_FRAME: - // Consume the frame. If the buffers are full, - // onFrameAvailable will not be called. Animation state - // relies on onFrameAvailable. - surfaceTexture.updateTexImage(); - mSwitchAnimManager.drawDarkPreview(canvas, x, y, width, - height, mAnimTexture); - break; - case ANIM_SWITCH_START: - mSwitchAnimManager.startAnimation(); - mAnimState = ANIM_SWITCH_RUNNING; - break; - case ANIM_CAPTURE_START: - copyPreviewTexture(canvas); - mListener.onCaptureTextureCopied(); - mCaptureAnimManager.startAnimation(); - mAnimState = ANIM_CAPTURE_RUNNING; - break; - } - - if (mAnimState == ANIM_CAPTURE_RUNNING || mAnimState == ANIM_SWITCH_RUNNING) { - boolean drawn; - if (mAnimState == ANIM_CAPTURE_RUNNING) { - if (!mFullScreen) { - // Skip the animation if no longer in full screen mode - drawn = false; - } else { - drawn = mCaptureAnimManager.drawAnimation(canvas, this, mAnimTexture, - x, y, width, height); - } - } else { - drawn = mSwitchAnimManager.drawAnimation(canvas, x, y, - width, height, this, mAnimTexture); - } - if (drawn) { - mListener.requestRender(); - } else { - // Continue to the normal draw procedure if the animation is - // not drawn. - mAnimState = ANIM_NONE; - directDraw(canvas, x, y, width, height); - } - } - canvas.setAlpha(oldAlpha); - callbackIfNeeded(); - } // mLock - } - - private void copyPreviewTexture(GLCanvas canvas) { - if (!mDraw.requiresSurfaceTexture()) { - mAnimTexture = mDraw.copyToTexture( - canvas, mAnimTexture, getTextureWidth(), getTextureHeight()); - } else { - int width = mAnimTexture.getWidth(); - int height = mAnimTexture.getHeight(); - canvas.beginRenderTarget(mAnimTexture); - // Flip preview texture vertically. OpenGL uses bottom left point - // as the origin (0, 0). - canvas.translate(0, height); - canvas.scale(1, -1, 1); - getSurfaceTexture().getTransformMatrix(mTextureTransformMatrix); - updateTransformMatrix(mTextureTransformMatrix); - canvas.drawTexture(mExtTexture, mTextureTransformMatrix, 0, 0, width, height); - canvas.endRenderTarget(); - } - } - - @Override - public void noDraw() { - synchronized (mLock) { - mVisible = false; - } - } - - @Override - public void recycle() { - synchronized (mLock) { - mVisible = false; - } - } - - @Override - public void onFrameAvailable(SurfaceTexture surfaceTexture) { - synchronized (mLock) { - if (getSurfaceTexture() != surfaceTexture) { - return; - } - mFirstFrameArrived = true; - if (mVisible) { - if (mAnimState == ANIM_SWITCH_WAITING_FIRST_FRAME) { - mAnimState = ANIM_SWITCH_START; - } - // We need to ask for re-render if the SurfaceTexture receives a new - // frame. - mListener.requestRender(); - } - } - } - - // We need to keep track of the size of preview frame on the screen because - // it's needed when we do switch-camera animation. See comments in - // SwitchAnimManager.java. This is based on the natural orientation, not the - // view system orientation. - public void setPreviewFrameLayoutSize(int width, int height) { - synchronized (mLock) { - mSwitchAnimManager.setPreviewFrameLayoutSize(width, height); - setPreviewLayoutSize(width, height); - } - } - - public void setOneTimeOnFrameDrawnListener(OnFrameDrawnListener l) { - synchronized (mLock) { - mFirstFrameArrived = false; - mOneTimeFrameDrawnListener = l; - } - } - - @Override - public SurfaceTexture getSurfaceTexture() { - synchronized (mLock) { - SurfaceTexture surfaceTexture = super.getSurfaceTexture(); - if (surfaceTexture == null && mAcquireTexture) { - try { - mLock.wait(); - surfaceTexture = super.getSurfaceTexture(); - } catch (InterruptedException e) { - Log.w(TAG, "unexpected interruption"); - } - } - return surfaceTexture; - } - } - - private void allocateTextureIfRequested(GLCanvas canvas) { - synchronized (mLock) { - if (mAcquireTexture) { - super.acquireSurfaceTexture(canvas); - mAcquireTexture = false; - mLock.notifyAll(); - } - } - } - - public void setOnFrameDrawnOneShot(Runnable run) { - synchronized (mLock) { - mOnFrameDrawnListener = run; - } - } - - public float getAlpha() { - synchronized (mLock) { - return mAlpha; - } - } - - public void setAlpha(float alpha) { - synchronized (mLock) { - mAlpha = alpha; - mListener.requestRender(); - } - } -} diff --git a/src/com/android/camera/CameraSettings.java b/src/com/android/camera/CameraSettings.java index 9e01d5c9b..791a4f719 100644 --- a/src/com/android/camera/CameraSettings.java +++ b/src/com/android/camera/CameraSettings.java @@ -29,8 +29,9 @@ import android.media.CamcorderProfile; import android.util.FloatMath; import android.util.Log; +import com.android.camera.util.CameraUtil; import com.android.camera2.R; -import com.android.camera.support.common.ApiHelper; +import com.android.camera.util.ApiHelper; import java.util.ArrayList; import java.util.List; @@ -197,7 +198,7 @@ public class CameraSettings { flashMode, mParameters.getSupportedFlashModes()); } if (focusMode != null) { - if (!Util.isFocusAreaSupported(mParameters)) { + if (!CameraUtil.isFocusAreaSupported(mParameters)) { filterUnsupportedOptions(group, focusMode, mParameters.getSupportedFocusModes()); } else { @@ -228,7 +229,7 @@ public class CameraSettings { } } if (cameraHdr != null && (!ApiHelper.HAS_CAMERA_HDR - || !Util.isCameraHdrSupported(mParameters))) { + || !CameraUtil.isCameraHdrSupported(mParameters))) { removePreference(group, cameraHdr.getKey()); } } @@ -553,8 +554,8 @@ public class CameraSettings { EffectsRecorder.isEffectSupported(EffectsRecorder.EFFECT_GOOFY_FACE); boolean backdropperSupported = EffectsRecorder.isEffectSupported(EffectsRecorder.EFFECT_BACKDROPPER) && - Util.isAutoExposureLockSupported(mParameters) && - Util.isAutoWhiteBalanceLockSupported(mParameters); + CameraUtil.isAutoExposureLockSupported(mParameters) && + CameraUtil.isAutoWhiteBalanceLockSupported(mParameters); ArrayList supported = new ArrayList(); for (CharSequence value : values) { diff --git a/src/com/android/camera/CaptureAnimManager.java b/src/com/android/camera/CaptureAnimManager.java deleted file mode 100644 index 3510d7528..000000000 --- a/src/com/android/camera/CaptureAnimManager.java +++ /dev/null @@ -1,228 +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.camera; - -import android.content.Context; -import android.content.res.Resources; -import android.graphics.Color; -import android.os.SystemClock; -import android.view.animation.DecelerateInterpolator; -import android.view.animation.Interpolator; - -import com.android.camera.support.glrenderer.GLCanvas; -import com.android.camera.support.glrenderer.NinePatchTexture; -import com.android.camera.support.glrenderer.RawTexture; -import com.android.camera2.R; - -/** - * Class to handle the capture animation. - */ -public class CaptureAnimManager { - @SuppressWarnings("unused") - private static final String TAG = "CAM_Capture"; - // times mark endpoint of animation phase - private static final int TIME_FLASH = 200; - private static final int TIME_HOLD = 400; - private static final int TIME_SLIDE = 800; - private static final int TIME_HOLD2 = 3300; - private static final int TIME_SLIDE2 = 4100; - - private static final int ANIM_BOTH = 0; - private static final int ANIM_FLASH = 1; - private static final int ANIM_SLIDE = 2; - private static final int ANIM_HOLD2 = 3; - private static final int ANIM_SLIDE2 = 4; - - private final Interpolator mSlideInterpolator = new DecelerateInterpolator(); - - private volatile int mAnimOrientation; // Could be 0, 90, 180 or 270 degrees. - private long mAnimStartTime; // milliseconds. - private float mX; // The center of the whole view including preview and review. - private float mY; - private int mDrawWidth; - private int mDrawHeight; - private int mAnimType; - - private int mHoldX; - private int mHoldY; - private int mHoldW; - private int mHoldH; - - private int mOffset; - - private int mMarginRight; - private int mMarginTop; - private int mSize; - private Resources mResources; - private NinePatchTexture mBorder; - private int mShadowSize; - - public static int getAnimationDuration() { - return TIME_SLIDE2; - } - - /* preview: camera preview view. - * review: view of picture just taken. - */ - public CaptureAnimManager(Context ctx) { - mBorder = new NinePatchTexture(ctx, R.drawable.capture_thumbnail_shadow); - mResources = ctx.getResources(); - } - - public void setOrientation(int displayRotation) { - mAnimOrientation = (360 - displayRotation) % 360; - } - - public void animateSlide() { - if (mAnimType != ANIM_FLASH) { - return; - } - mAnimType = ANIM_SLIDE; - mAnimStartTime = SystemClock.uptimeMillis(); - } - - public void animateFlash() { - mAnimType = ANIM_FLASH; - } - - public void animateFlashAndSlide() { - mAnimType = ANIM_BOTH; - } - - public void startAnimation() { - mAnimStartTime = SystemClock.uptimeMillis(); - } - - private void setAnimationGeometry(int x, int y, int w, int h) { - mMarginRight = mResources.getDimensionPixelSize(R.dimen.capture_margin_right); - mMarginTop = mResources.getDimensionPixelSize(R.dimen.capture_margin_top); - mSize = mResources.getDimensionPixelSize(R.dimen.capture_size); - mShadowSize = mResources.getDimensionPixelSize(R.dimen.capture_border); - mOffset = mMarginRight + mSize; - // Set the views to the initial positions. - mDrawWidth = w; - mDrawHeight = h; - mX = x; - mY = y; - mHoldW = mSize; - mHoldH = mSize; - switch (mAnimOrientation) { - case 0: // Preview is on the left. - mHoldX = x + w - mMarginRight - mSize; - mHoldY = y + mMarginTop; - break; - case 90: // Preview is below. - mHoldX = x + mMarginTop; - mHoldY = y + mMarginRight; - break; - case 180: // Preview on the right. - mHoldX = x + mMarginRight; - mHoldY = y + h - mMarginTop - mSize; - break; - case 270: // Preview is above. - mHoldX = x + w - mMarginTop - mSize; - mHoldY = y + h - mMarginRight - mSize; - break; - } - } - - // Returns true if the animation has been drawn. - public boolean drawAnimation(GLCanvas canvas, CameraScreenNail preview, - RawTexture review, int lx, int ly, int lw, int lh) { - setAnimationGeometry(lx, ly, lw, lh); - long timeDiff = SystemClock.uptimeMillis() - mAnimStartTime; - // Check if the animation is over - if (mAnimType == ANIM_SLIDE && timeDiff > TIME_SLIDE2 - TIME_HOLD) return false; - if (mAnimType == ANIM_BOTH && timeDiff > TIME_SLIDE2) return false; - - // determine phase and time in phase - int animStep = mAnimType; - if (mAnimType == ANIM_SLIDE) { - timeDiff += TIME_HOLD; - } - if (mAnimType == ANIM_SLIDE || mAnimType == ANIM_BOTH) { - if (timeDiff < TIME_HOLD) { - animStep = ANIM_FLASH; - } else if (timeDiff < TIME_SLIDE) { - animStep = ANIM_SLIDE; - timeDiff -= TIME_HOLD; - } else if (timeDiff < TIME_HOLD2) { - animStep = ANIM_HOLD2; - timeDiff -= TIME_SLIDE; - } else { - // SLIDE2 - animStep = ANIM_SLIDE2; - timeDiff -= TIME_HOLD2; - } - } - - if (animStep == ANIM_FLASH) { - review.draw(canvas, (int) mX, (int) mY, mDrawWidth, mDrawHeight); - if (timeDiff < TIME_FLASH) { - float f = 0.3f - 0.3f * timeDiff / TIME_FLASH; - int color = Color.argb((int) (255 * f), 255, 255, 255); - canvas.fillRect(mX, mY, mDrawWidth, mDrawHeight, color); - } - } else if (animStep == ANIM_SLIDE) { - float fraction = mSlideInterpolator.getInterpolation((float) (timeDiff) / (TIME_SLIDE - TIME_HOLD)); - float x = mX; - float y = mY; - float w = 0; - float h = 0; - x = interpolate(mX, mHoldX, fraction); - y = interpolate(mY, mHoldY, fraction); - w = interpolate(mDrawWidth, mHoldW, fraction); - h = interpolate(mDrawHeight, mHoldH, fraction); - preview.directDraw(canvas, (int) mX, (int) mY, mDrawWidth, mDrawHeight); - review.draw(canvas, (int) x, (int) y, (int) w, (int) h); - } else if (animStep == ANIM_HOLD2) { - preview.directDraw(canvas, (int) mX, (int) mY, mDrawWidth, mDrawHeight); - review.draw(canvas, mHoldX, mHoldY, mHoldW, mHoldH); - mBorder.draw(canvas, (int) mHoldX - mShadowSize, (int) mHoldY - mShadowSize, - (int) mHoldW + 2 * mShadowSize, (int) mHoldH + 2 * mShadowSize); - } else if (animStep == ANIM_SLIDE2) { - float fraction = (float)(timeDiff) / (TIME_SLIDE2 - TIME_HOLD2); - float x = mHoldX; - float y = mHoldY; - float d = mOffset * fraction; - switch (mAnimOrientation) { - case 0: - x = mHoldX + d; - break; - case 180: - x = mHoldX - d; - break; - case 90: - y = mHoldY - d; - break; - case 270: - y = mHoldY + d; - break; - } - preview.directDraw(canvas, (int) mX, (int) mY, mDrawWidth, mDrawHeight); - mBorder.draw(canvas, (int) x - mShadowSize, (int) y - mShadowSize, - (int) mHoldW + 2 * mShadowSize, (int) mHoldH + 2 * mShadowSize); - review.draw(canvas, (int) x, (int) y, mHoldW, mHoldH); - } - return true; - } - - private static float interpolate(float start, float end, float fraction) { - return start + (end - start) * fraction; - } - -} diff --git a/src/com/android/camera/EffectsRecorder.java b/src/com/android/camera/EffectsRecorder.java index 901226364..151441ef9 100644 --- a/src/com/android/camera/EffectsRecorder.java +++ b/src/com/android/camera/EffectsRecorder.java @@ -33,7 +33,8 @@ import android.os.Handler; import android.os.Looper; import android.util.Log; -import com.android.camera.support.common.ApiHelper; +import com.android.camera.util.ApiHelper; +import com.android.camera.util.CameraUtil; import com.android.camera2.R; @@ -1056,8 +1057,8 @@ public class EffectsRecorder { return false; } Camera.Parameters params = mCameraDevice.getParameters(); - if (Util.isAutoExposureLockSupported(params) && - Util.isAutoWhiteBalanceLockSupported(params)) { + if (CameraUtil.isAutoExposureLockSupported(params) && + CameraUtil.isAutoWhiteBalanceLockSupported(params)) { params.setAutoExposureLock(toggle); params.setAutoWhiteBalanceLock(toggle); mCameraDevice.setParameters(params); diff --git a/src/com/android/camera/FocusOverlayManager.java b/src/com/android/camera/FocusOverlayManager.java index 7a9e1dd6d..d3215e7a6 100644 --- a/src/com/android/camera/FocusOverlayManager.java +++ b/src/com/android/camera/FocusOverlayManager.java @@ -30,7 +30,8 @@ import android.os.Looper; import android.os.Message; import android.util.Log; -import com.android.camera.support.common.ApiHelper; +import com.android.camera.util.ApiHelper; +import com.android.camera.util.CameraUtil; /* A class that handles everything about focus in still picture mode. * This also handles the metering area because it is the same as focus area. @@ -151,10 +152,10 @@ public class FocusOverlayManager { // camera is open. if (parameters == null) return; mParameters = parameters; - mFocusAreaSupported = Util.isFocusAreaSupported(parameters); - mMeteringAreaSupported = Util.isMeteringAreaSupported(parameters); - mLockAeAwbNeeded = (Util.isAutoExposureLockSupported(mParameters) || - Util.isAutoWhiteBalanceLockSupported(mParameters)); + mFocusAreaSupported = CameraUtil.isFocusAreaSupported(parameters); + mMeteringAreaSupported = CameraUtil.isMeteringAreaSupported(parameters); + mLockAeAwbNeeded = (CameraUtil.isAutoExposureLockSupported(mParameters) || + CameraUtil.isAutoWhiteBalanceLockSupported(mParameters)); } public void setPreviewSize(int previewWidth, int previewHeight) { @@ -178,7 +179,7 @@ public class FocusOverlayManager { private void setMatrix() { if (mPreviewWidth != 0 && mPreviewHeight != 0) { Matrix matrix = new Matrix(); - Util.prepareMatrix(matrix, mMirror, mDisplayOrientation, + CameraUtil.prepareMatrix(matrix, mMirror, mDisplayOrientation, mPreviewWidth, mPreviewHeight); // In face detection, the matrix converts the driver coordinates to UI // coordinates. In tap focus, the inverted matrix converts the UI @@ -440,17 +441,17 @@ public class FocusOverlayManager { if (mFocusMode == null) { for (int i = 0; i < mDefaultFocusModes.length; i++) { String mode = mDefaultFocusModes[i]; - if (Util.isSupported(mode, supportedFocusModes)) { + if (CameraUtil.isSupported(mode, supportedFocusModes)) { mFocusMode = mode; break; } } } } - if (!Util.isSupported(mFocusMode, supportedFocusModes)) { + if (!CameraUtil.isSupported(mFocusMode, supportedFocusModes)) { // For some reasons, the driver does not support the current // focus mode. Fall back to auto. - if (Util.isSupported(Parameters.FOCUS_MODE_AUTO, + if (CameraUtil.isSupported(Parameters.FOCUS_MODE_AUTO, mParameters.getSupportedFocusModes())) { mFocusMode = Parameters.FOCUS_MODE_AUTO; } else { @@ -484,7 +485,7 @@ public class FocusOverlayManager { } else if (mState == STATE_FOCUSING || mState == STATE_FOCUSING_SNAP_ON_FINISH) { mUI.onFocusStarted(); } else { - if (Util.FOCUS_MODE_CONTINUOUS_PICTURE.equals(mFocusMode)) { + if (CameraUtil.FOCUS_MODE_CONTINUOUS_PICTURE.equals(mFocusMode)) { // TODO: check HAL behavior and decide if this can be removed. mUI.onFocusSucceeded(false); } else if (mState == STATE_SUCCESS) { @@ -513,12 +514,12 @@ public class FocusOverlayManager { private void calculateTapArea(int x, int y, float areaMultiple, Rect rect) { int areaSize = (int) (Math.min(mPreviewWidth, mPreviewHeight) * areaMultiple / 20); - int left = Util.clamp(x - areaSize, 0, mPreviewWidth - 2 * areaSize); - int top = Util.clamp(y - areaSize, 0, mPreviewHeight - 2 * areaSize); + int left = CameraUtil.clamp(x - areaSize, 0, mPreviewWidth - 2 * areaSize); + int top = CameraUtil.clamp(y - areaSize, 0, mPreviewHeight - 2 * areaSize); RectF rectF = new RectF(left, top, left + 2 * areaSize, top + 2 * areaSize); mMatrix.mapRect(rectF); - Util.rectFToRect(rectF, rect); + CameraUtil.rectFToRect(rectF, rect); } /* package */ int getFocusState() { diff --git a/src/com/android/camera/ListPreference.java b/src/com/android/camera/ListPreference.java index f593b35e5..34040d79b 100644 --- a/src/com/android/camera/ListPreference.java +++ b/src/com/android/camera/ListPreference.java @@ -26,6 +26,7 @@ import android.util.AttributeSet; import android.util.Log; import android.util.TypedValue; +import com.android.camera.util.CameraUtil; import com.android.camera2.R; /** @@ -49,7 +50,7 @@ public class ListPreference extends CameraPreference { TypedArray a = context.obtainStyledAttributes( attrs, R.styleable.ListPreference, 0, 0); - mKey = Util.checkNotNull( + mKey = CameraUtil.checkNotNull( a.getString(R.styleable.ListPreference_key)); // We allow the defaultValue attribute to be a string or an array of @@ -137,7 +138,7 @@ public class ListPreference extends CameraPreference { public int findIndexOfValue(String value) { for (int i = 0, n = mEntryValues.length; i < n; ++i) { - if (Util.equals(mEntryValues[i], value)) return i; + if (CameraUtil.equals(mEntryValues[i], value)) return i; } return -1; } diff --git a/src/com/android/camera/PhotoModule.java b/src/com/android/camera/PhotoModule.java index 5c96592fe..8d68320bf 100644 --- a/src/com/android/camera/PhotoModule.java +++ b/src/com/android/camera/PhotoModule.java @@ -65,13 +65,13 @@ import com.android.camera.CameraManager.CameraAFMoveCallback; import com.android.camera.CameraManager.CameraPictureCallback; import com.android.camera.CameraManager.CameraProxy; import com.android.camera.CameraManager.CameraShutterCallback; -import com.android.camera.support.filtershow.crop.CropExtras; +import com.android.camera.util.ApiHelper; import com.android.camera.ui.CountDownView.OnCountDownFinishedListener; import com.android.camera.ui.PopupManager; import com.android.camera.ui.RotateTextToast; +import com.android.camera.util.CameraUtil; import com.android.camera.util.UsageStatistics; import com.android.camera2.R; -import com.android.gallery3d.common.ApiHelper; import com.android.gallery3d.exif.ExifInterface; import com.android.gallery3d.exif.ExifTag; import com.android.gallery3d.exif.Rational; @@ -277,7 +277,7 @@ public class PhotoModule // We need to check whether the activity is paused before long // operations to ensure that onPause() can be done ASAP. if (mCancelled) return; - mCameraDevice = Util.openCamera(mActivity, mCameraId); + mCameraDevice = CameraUtil.openCamera(mActivity, mCameraId); mParameters = mCameraDevice.getParameters(); // Wait until all the initialization needed by startPreview are // done. @@ -336,7 +336,7 @@ public class PhotoModule // down and camera app is opened. Rotation animation will // take some time and the rotation value we have got may be // wrong. Framework does not have a callback for this now. - if (Util.getDisplayRotation(mActivity) != mDisplayRotation) { + if (CameraUtil.getDisplayRotation(mActivity) != mDisplayRotation) { setDisplayOrientation(); } if (SystemClock.uptimeMillis() - mOnResumeTime < 5000) { @@ -374,7 +374,7 @@ public class PhotoModule case OPEN_CAMERA_FAIL: { mCameraStartUpThread = null; mOpenCameraFail = true; - Util.showErrorAndFinish(mActivity, + CameraUtil.showErrorAndFinish(mActivity, R.string.cannot_connect_camera); break; } @@ -382,7 +382,7 @@ public class PhotoModule case CAMERA_DISABLED: { mCameraStartUpThread = null; mCameraDisabled = true; - Util.showErrorAndFinish(mActivity, + CameraUtil.showErrorAndFinish(mActivity, R.string.camera_disabled); break; } @@ -495,13 +495,13 @@ public class PhotoModule mPreferences.setLocalId(mActivity, mCameraId); CameraSettings.upgradeLocalPreferences(mPreferences.getLocal()); try { - mCameraDevice = Util.openCamera(mActivity, mCameraId); + mCameraDevice = CameraUtil.openCamera(mActivity, mCameraId); mParameters = mCameraDevice.getParameters(); } catch (CameraHardwareException e) { - Util.showErrorAndFinish(mActivity, R.string.cannot_connect_camera); + CameraUtil.showErrorAndFinish(mActivity, R.string.cannot_connect_camera); return; } catch (CameraDisabledException e) { - Util.showErrorAndFinish(mActivity, R.string.camera_disabled); + CameraUtil.showErrorAndFinish(mActivity, R.string.camera_disabled); return; } initializeCapabilities(); @@ -734,7 +734,7 @@ public class PhotoModule // Animate capture with real jpeg data instead of a preview frame. mUI.animateCapture(jpegData); } - if (mSceneMode == Util.SCENE_MODE_HDR) { + if (mSceneMode == CameraUtil.SCENE_MODE_HDR) { mUI.showSwitcher(); mUI.setSwipingEnabled(true); } @@ -860,7 +860,7 @@ public class PhotoModule public void nameNewImage(ContentResolver resolver, long date) { NamedEntity r = new NamedEntity(); - r.title = Util.createJpegName(date); + r.title = CameraUtil.createJpegName(date); r.date = date; mQueue.add(r); } @@ -924,7 +924,7 @@ public class PhotoModule mPostViewPictureCallbackTime = 0; mJpegImageData = null; - final boolean animateBefore = (mSceneMode == Util.SCENE_MODE_HDR); + final boolean animateBefore = (mSceneMode == CameraUtil.SCENE_MODE_HDR); if (animateBefore) { animateAfterShutter(); @@ -939,10 +939,10 @@ public class PhotoModule } else { orientation = mOrientation; } - mJpegRotation = Util.getJpegRotation(mCameraId, orientation); + mJpegRotation = CameraUtil.getJpegRotation(mCameraId, orientation); mParameters.setRotation(mJpegRotation); Location loc = mLocationManager.getCurrentLocation(); - Util.setGpsParameters(mParameters, loc); + CameraUtil.setGpsParameters(mParameters, loc); mCameraDevice.setParameters(mParameters); mCameraDevice.takePicture(mHandler, @@ -965,7 +965,7 @@ public class PhotoModule } private int getPreferredCameraId(ComboPreferences preferences) { - int intentCameraId = Util.getCameraFacingIntentExtras(mActivity); + int intentCameraId = CameraUtil.getCameraFacingIntentExtras(mActivity); if (intentCameraId != -1) { // Testing purpose. Launch a specific camera through the intent // extras. @@ -1006,7 +1006,7 @@ public class PhotoModule // the camera then point the camera to floor or sky, we still have // the correct orientation. if (orientation == OrientationEventListener.ORIENTATION_UNKNOWN) return; - mOrientation = Util.roundOrientation(orientation, mOrientation); + mOrientation = CameraUtil.roundOrientation(orientation, mOrientation); // Show the toast after getting the first orientation changed. if (mHandler.hasMessages(SHOW_TAP_TO_FOCUS_TOAST)) { @@ -1062,13 +1062,13 @@ public class PhotoModule } catch (IOException ex) { // ignore exception } finally { - Util.closeSilently(outputStream); + CameraUtil.closeSilently(outputStream); } } else { ExifInterface exif = Exif.getExif(data); int orientation = Exif.getOrientation(exif); - Bitmap bitmap = Util.makeBitmap(data, 50 * 1024); - bitmap = Util.rotate(bitmap, orientation); + Bitmap bitmap = CameraUtil.makeBitmap(data, 50 * 1024); + bitmap = CameraUtil.rotate(bitmap, orientation); mActivity.setResultEx(Activity.RESULT_OK, new Intent("inline-data").putExtra("data", bitmap)); mActivity.finish(); @@ -1093,7 +1093,7 @@ public class PhotoModule mActivity.finish(); return; } finally { - Util.closeSilently(tempStream); + CameraUtil.closeSilently(tempStream); } Bundle newExtras = new Bundle(); @@ -1103,10 +1103,10 @@ public class PhotoModule if (mSaveUri != null) { newExtras.putParcelable(MediaStore.EXTRA_OUTPUT, mSaveUri); } else { - newExtras.putBoolean(CropExtras.KEY_RETURN_DATA, true); + newExtras.putBoolean(CameraUtil.KEY_RETURN_DATA, true); } if (mActivity.isSecureCamera()) { - newExtras.putBoolean(CropExtras.KEY_SHOW_WHEN_LOCKED, true); + newExtras.putBoolean(CameraUtil.KEY_SHOW_WHEN_LOCKED, true); } // TODO: Share this constant. @@ -1154,7 +1154,7 @@ public class PhotoModule } Log.v(TAG, "onShutterButtonClick: mCameraState=" + mCameraState); - if (mSceneMode == Util.SCENE_MODE_HDR) { + if (mSceneMode == CameraUtil.SCENE_MODE_HDR) { mUI.hideSwitcher(); mUI.setSwipingEnabled(false); } @@ -1355,7 +1355,7 @@ public class PhotoModule @Override public void updateCameraOrientation() { - if (mDisplayRotation != Util.getDisplayRotation(mActivity)) { + if (mDisplayRotation != CameraUtil.getDisplayRotation(mActivity)) { setDisplayOrientation(); } } @@ -1490,8 +1490,8 @@ public class PhotoModule } private void setDisplayOrientation() { - mDisplayRotation = Util.getDisplayRotation(mActivity); - mDisplayOrientation = Util.getDisplayOrientation(mDisplayRotation, mCameraId); + mDisplayRotation = CameraUtil.getDisplayRotation(mActivity); + mDisplayOrientation = CameraUtil.getDisplayOrientation(mDisplayRotation, mCameraId); mCameraDisplayOrientation = mDisplayOrientation; mUI.setDisplayOrientation(mDisplayOrientation); if (mFocusManager != null) { @@ -1526,7 +1526,7 @@ public class PhotoModule if (!mSnapshotOnIdle) { // If the focus mode is continuous autofocus, call cancelAutoFocus to // resume it because it may have been paused by autoFocus call. - if (Util.FOCUS_MODE_CONTINUOUS_PICTURE.equals(mFocusManager.getFocusMode())) { + if (CameraUtil.FOCUS_MODE_CONTINUOUS_PICTURE.equals(mFocusManager.getFocusMode())) { mCameraDevice.cancelAutoFocus(); } mFocusManager.setAeAwbLock(false); // Unlock AE and AWB. @@ -1563,14 +1563,14 @@ public class PhotoModule private void updateCameraParametersInitialize() { // Reset preview frame rate to the maximum because it may be lowered by // video camera application. - int[] fpsRange = Util.getMaxPreviewFpsRange(mParameters); + int[] fpsRange = CameraUtil.getMaxPreviewFpsRange(mParameters); if (fpsRange.length > 0) { mParameters.setPreviewFpsRange( fpsRange[Parameters.PREVIEW_FPS_MIN_INDEX], fpsRange[Parameters.PREVIEW_FPS_MAX_INDEX]); } - mParameters.set(Util.RECORDING_HINT, Util.FALSE); + mParameters.set(CameraUtil.RECORDING_HINT, CameraUtil.FALSE); // Disable video stabilization. Convenience methods not available in API // level <= 14 @@ -1637,7 +1637,7 @@ public class PhotoModule // Set a preview size that is closest to the viewfinder height and has // the right aspect ratio. List sizes = mParameters.getSupportedPreviewSizes(); - Size optimalSize = Util.getOptimalPreviewSize(mActivity, sizes, + Size optimalSize = CameraUtil.getOptimalPreviewSize(mActivity, sizes, (double) size.width / size.height); Size original = mParameters.getPreviewSize(); if (!original.equals(optimalSize)) { @@ -1661,13 +1661,13 @@ public class PhotoModule String hdr = mPreferences.getString(CameraSettings.KEY_CAMERA_HDR, mActivity.getString(R.string.pref_camera_hdr_default)); if (mActivity.getString(R.string.setting_on_value).equals(hdr)) { - mSceneMode = Util.SCENE_MODE_HDR; + mSceneMode = CameraUtil.SCENE_MODE_HDR; } else { mSceneMode = mPreferences.getString( CameraSettings.KEY_SCENE_MODE, mActivity.getString(R.string.pref_camera_scenemode_default)); } - if (Util.isSupported(mSceneMode, mParameters.getSupportedSceneModes())) { + if (CameraUtil.isSupported(mSceneMode, mParameters.getSupportedSceneModes())) { if (!mParameters.getSceneMode().equals(mSceneMode)) { mParameters.setSceneMode(mSceneMode); @@ -1708,7 +1708,7 @@ public class PhotoModule CameraSettings.KEY_FLASH_MODE, mActivity.getString(R.string.pref_camera_flashmode_default)); List supportedFlash = mParameters.getSupportedFlashModes(); - if (Util.isSupported(flashMode, supportedFlash)) { + if (CameraUtil.isSupported(flashMode, supportedFlash)) { mParameters.setFlashMode(flashMode); } else { flashMode = mParameters.getFlashMode(); @@ -1722,7 +1722,7 @@ public class PhotoModule String whiteBalance = mPreferences.getString( CameraSettings.KEY_WHITE_BALANCE, mActivity.getString(R.string.pref_camera_whitebalance_default)); - if (Util.isSupported(whiteBalance, + if (CameraUtil.isSupported(whiteBalance, mParameters.getSupportedWhiteBalance())) { mParameters.setWhiteBalance(whiteBalance); } else { @@ -1746,7 +1746,7 @@ public class PhotoModule @TargetApi(ApiHelper.VERSION_CODES.JELLY_BEAN) private void updateAutoFocusMoveCallback() { - if (mParameters.getFocusMode().equals(Util.FOCUS_MODE_CONTINUOUS_PICTURE)) { + if (mParameters.getFocusMode().equals(CameraUtil.FOCUS_MODE_CONTINUOUS_PICTURE)) { mCameraDevice.setAutoFocusMoveCallback(mHandler, (CameraManager.CameraAFMoveCallback) mAutoFocusMoveCallback); } else { @@ -1885,12 +1885,12 @@ public class PhotoModule private void initializeCapabilities() { mInitialParams = mCameraDevice.getParameters(); - mFocusAreaSupported = Util.isFocusAreaSupported(mInitialParams); - mMeteringAreaSupported = Util.isMeteringAreaSupported(mInitialParams); - mAeLockSupported = Util.isAutoExposureLockSupported(mInitialParams); - mAwbLockSupported = Util.isAutoWhiteBalanceLockSupported(mInitialParams); + mFocusAreaSupported = CameraUtil.isFocusAreaSupported(mInitialParams); + mMeteringAreaSupported = CameraUtil.isMeteringAreaSupported(mInitialParams); + mAeLockSupported = CameraUtil.isAutoExposureLockSupported(mInitialParams); + mAwbLockSupported = CameraUtil.isAutoWhiteBalanceLockSupported(mInitialParams); mContinousFocusSupported = mInitialParams.getSupportedFocusModes().contains( - Util.FOCUS_MODE_CONTINUOUS_PICTURE); + CameraUtil.FOCUS_MODE_CONTINUOUS_PICTURE); } @Override diff --git a/src/com/android/camera/PhotoUI.java b/src/com/android/camera/PhotoUI.java index a1de60af2..f5748ba3d 100644 --- a/src/com/android/camera/PhotoUI.java +++ b/src/com/android/camera/PhotoUI.java @@ -17,8 +17,6 @@ package com.android.camera; -import java.util.List; - import android.app.AlertDialog; import android.content.DialogInterface; import android.graphics.Bitmap; @@ -47,6 +45,7 @@ import android.widget.Toast; import com.android.camera.CameraPreference.OnPreferenceChangedListener; import com.android.camera.FocusOverlayManager.FocusUI; +import com.android.camera.util.ApiHelper; import com.android.camera.ui.AbstractSettingPopup; import com.android.camera.ui.CameraControls; import com.android.camera.ui.CameraRootView; @@ -60,8 +59,10 @@ import com.android.camera.ui.PieRenderer; import com.android.camera.ui.PieRenderer.PieListener; import com.android.camera.ui.RenderOverlay; import com.android.camera.ui.ZoomRenderer; +import com.android.camera.util.CameraUtil; import com.android.camera2.R; -import com.android.gallery3d.common.ApiHelper; + +import java.util.List; public class PhotoUI implements PieListener, PreviewGestures.SingleTapListener, @@ -145,7 +146,7 @@ public class PhotoUI implements PieListener, // Full-screen screennail int w = width; int h = height; - if (Util.getDisplayRotation(mActivity) % 180 != 0) { + if (CameraUtil.getDisplayRotation(mActivity) % 180 != 0) { w = height; h = width; } @@ -168,7 +169,7 @@ public class PhotoUI implements PieListener, @Override protected Bitmap doInBackground(Integer... params) { // Decode image in background. - return Util.downSample(mData, DOWN_SAMPLE_FACTOR); + return CameraUtil.downSample(mData, DOWN_SAMPLE_FACTOR); } @Override @@ -238,7 +239,7 @@ public class PhotoUI implements PieListener, private void setTransformMatrix(int width, int height) { mMatrix = mTextureView.getTransform(mMatrix); - int orientation = Util.getDisplayRotation(mActivity); + int orientation = CameraUtil.getDisplayRotation(mActivity); float scaleX = 1f, scaleY = 1f; float scaledTextureWidth, scaledTextureHeight; if (width > height) { @@ -620,18 +621,18 @@ public class PhotoUI implements PieListener, protected void showPostCaptureAlert() { mOnScreenIndicators.setVisibility(View.GONE); mMenuButton.setVisibility(View.GONE); - Util.fadeIn(mReviewDoneButton); + CameraUtil.fadeIn(mReviewDoneButton); mShutterButton.setVisibility(View.INVISIBLE); - Util.fadeIn(mReviewRetakeButton); + CameraUtil.fadeIn(mReviewRetakeButton); pauseFaceDetection(); } protected void hidePostCaptureAlert() { mOnScreenIndicators.setVisibility(View.VISIBLE); mMenuButton.setVisibility(View.VISIBLE); - Util.fadeOut(mReviewDoneButton); + CameraUtil.fadeOut(mReviewDoneButton); mShutterButton.setVisibility(View.VISIBLE); - Util.fadeOut(mReviewRetakeButton); + CameraUtil.fadeOut(mReviewRetakeButton); resumeFaceDetection(); } diff --git a/src/com/android/camera/PreviewFrameLayout.java b/src/com/android/camera/PreviewFrameLayout.java index cc2d28099..2bdace69c 100644 --- a/src/com/android/camera/PreviewFrameLayout.java +++ b/src/com/android/camera/PreviewFrameLayout.java @@ -21,9 +21,10 @@ import android.content.Context; import android.util.AttributeSet; import android.view.View; import android.widget.RelativeLayout; -import com.android.camera.support.common.ApiHelper; +import com.android.camera.util.ApiHelper; import com.android.camera.ui.LayoutChangeHelper; import com.android.camera.ui.LayoutChangeNotifier; +import com.android.camera.util.CameraUtil; import com.android.camera2.R; /** @@ -68,7 +69,7 @@ public class PreviewFrameLayout extends RelativeLayout implements LayoutChangeNo } public void fadeOutBorder() { - Util.fadeOut(mBorder); + CameraUtil.fadeOut(mBorder); } @Override diff --git a/src/com/android/camera/RotateDialogController.java b/src/com/android/camera/RotateDialogController.java deleted file mode 100644 index 45163f196..000000000 --- a/src/com/android/camera/RotateDialogController.java +++ /dev/null @@ -1,169 +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.camera; - -import android.app.Activity; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.view.animation.Animation; -import android.view.animation.AnimationUtils; -import android.widget.Button; -import android.widget.ProgressBar; -import android.widget.TextView; - -import com.android.camera.ui.Rotatable; -import com.android.camera.ui.RotateLayout; -import com.android.camera2.R; - -public class RotateDialogController implements Rotatable { - - @SuppressWarnings("unused") - private static final String TAG = "RotateDialogController"; - private static final long ANIM_DURATION = 150; // millis - - private Activity mActivity; - private int mLayoutResourceID; - private View mDialogRootLayout; - private RotateLayout mRotateDialog; - private View mRotateDialogTitleLayout; - private View mRotateDialogButtonLayout; - private TextView mRotateDialogTitle; - private ProgressBar mRotateDialogSpinner; - private TextView mRotateDialogText; - private TextView mRotateDialogButton1; - private TextView mRotateDialogButton2; - - private Animation mFadeInAnim, mFadeOutAnim; - - public RotateDialogController(Activity a, int layoutResource) { - mActivity = a; - mLayoutResourceID = layoutResource; - } - - private void inflateDialogLayout() { - if (mDialogRootLayout == null) { - ViewGroup layoutRoot = (ViewGroup) mActivity.getWindow().getDecorView(); - LayoutInflater inflater = mActivity.getLayoutInflater(); - View v = inflater.inflate(mLayoutResourceID, layoutRoot); - mDialogRootLayout = v.findViewById(R.id.rotate_dialog_root_layout); - mRotateDialog = (RotateLayout) v.findViewById(R.id.rotate_dialog_layout); - mRotateDialogTitleLayout = v.findViewById(R.id.rotate_dialog_title_layout); - mRotateDialogButtonLayout = v.findViewById(R.id.rotate_dialog_button_layout); - mRotateDialogTitle = (TextView) v.findViewById(R.id.rotate_dialog_title); - mRotateDialogSpinner = (ProgressBar) v.findViewById(R.id.rotate_dialog_spinner); - mRotateDialogText = (TextView) v.findViewById(R.id.rotate_dialog_text); - mRotateDialogButton1 = (Button) v.findViewById(R.id.rotate_dialog_button1); - mRotateDialogButton2 = (Button) v.findViewById(R.id.rotate_dialog_button2); - - mFadeInAnim = AnimationUtils.loadAnimation( - mActivity, android.R.anim.fade_in); - mFadeOutAnim = AnimationUtils.loadAnimation( - mActivity, android.R.anim.fade_out); - mFadeInAnim.setDuration(ANIM_DURATION); - mFadeOutAnim.setDuration(ANIM_DURATION); - } - } - - @Override - public void setOrientation(int orientation, boolean animation) { - inflateDialogLayout(); - mRotateDialog.setOrientation(orientation, animation); - } - - public void resetRotateDialog() { - inflateDialogLayout(); - mRotateDialogTitleLayout.setVisibility(View.GONE); - mRotateDialogSpinner.setVisibility(View.GONE); - mRotateDialogButton1.setVisibility(View.GONE); - mRotateDialogButton2.setVisibility(View.GONE); - mRotateDialogButtonLayout.setVisibility(View.GONE); - } - - private void fadeOutDialog() { - mDialogRootLayout.startAnimation(mFadeOutAnim); - mDialogRootLayout.setVisibility(View.GONE); - } - - private void fadeInDialog() { - mDialogRootLayout.startAnimation(mFadeInAnim); - mDialogRootLayout.setVisibility(View.VISIBLE); - } - - public void dismissDialog() { - if (mDialogRootLayout != null && mDialogRootLayout.getVisibility() != View.GONE) { - fadeOutDialog(); - } - } - - public void showAlertDialog(String title, String msg, String button1Text, - final Runnable r1, String button2Text, final Runnable r2) { - resetRotateDialog(); - - if (title != null) { - mRotateDialogTitle.setText(title); - mRotateDialogTitleLayout.setVisibility(View.VISIBLE); - } - - mRotateDialogText.setText(msg); - - if (button1Text != null) { - mRotateDialogButton1.setText(button1Text); - mRotateDialogButton1.setContentDescription(button1Text); - mRotateDialogButton1.setVisibility(View.VISIBLE); - mRotateDialogButton1.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - if (r1 != null) r1.run(); - dismissDialog(); - } - }); - mRotateDialogButtonLayout.setVisibility(View.VISIBLE); - } - if (button2Text != null) { - mRotateDialogButton2.setText(button2Text); - mRotateDialogButton2.setContentDescription(button2Text); - mRotateDialogButton2.setVisibility(View.VISIBLE); - mRotateDialogButton2.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - if (r2 != null) r2.run(); - dismissDialog(); - } - }); - mRotateDialogButtonLayout.setVisibility(View.VISIBLE); - } - - fadeInDialog(); - } - - public void showWaitingDialog(String msg) { - resetRotateDialog(); - - mRotateDialogText.setText(msg); - mRotateDialogSpinner.setVisibility(View.VISIBLE); - - fadeInDialog(); - } - - public int getVisibility() { - if (mDialogRootLayout != null) { - return mDialogRootLayout.getVisibility(); - } - return View.INVISIBLE; - } -} diff --git a/src/com/android/camera/SoundClips.java b/src/com/android/camera/SoundClips.java index b5cfeddaa..f981bd238 100644 --- a/src/com/android/camera/SoundClips.java +++ b/src/com/android/camera/SoundClips.java @@ -24,7 +24,7 @@ import android.media.SoundPool; import android.util.Log; import com.android.camera2.R; -import com.android.gallery3d.common.ApiHelper; +import com.android.camera.util.ApiHelper; /* * This class controls the sound playback according to the API level. diff --git a/src/com/android/camera/StaticBitmapScreenNail.java b/src/com/android/camera/StaticBitmapScreenNail.java deleted file mode 100644 index 91ac35c31..000000000 --- a/src/com/android/camera/StaticBitmapScreenNail.java +++ /dev/null @@ -1,32 +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.camera; - -import android.graphics.Bitmap; - -import com.android.camera.support.ui.BitmapScreenNail; - -public class StaticBitmapScreenNail extends BitmapScreenNail { - public StaticBitmapScreenNail(Bitmap bitmap) { - super(bitmap); - } - - @Override - public void recycle() { - // Always keep the bitmap in memory. - } -} diff --git a/src/com/android/camera/Storage.java b/src/com/android/camera/Storage.java index 4e5a7dd0e..aa2a9721b 100644 --- a/src/com/android/camera/Storage.java +++ b/src/com/android/camera/Storage.java @@ -32,7 +32,7 @@ import android.provider.MediaStore.Images.ImageColumns; import android.provider.MediaStore.MediaColumns; import android.util.Log; -import com.android.camera.support.common.ApiHelper; +import com.android.camera.util.ApiHelper; import com.android.gallery3d.exif.ExifInterface; public class Storage { diff --git a/src/com/android/camera/SwitchAnimManager.java b/src/com/android/camera/SwitchAnimManager.java deleted file mode 100644 index 20a4bfd97..000000000 --- a/src/com/android/camera/SwitchAnimManager.java +++ /dev/null @@ -1,146 +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.camera; - -import android.os.SystemClock; -import android.util.Log; - -import com.android.camera.support.glrenderer.GLCanvas; -import com.android.camera.support.glrenderer.RawTexture; - -/** - * Class to handle the animation when switching between back and front cameras. - * An image of the previous camera zooms in and fades out. The preview of the - * new camera zooms in and fades in. The image of the previous camera is called - * review in this class. - */ -public class SwitchAnimManager { - private static final String TAG = "SwitchAnimManager"; - // The amount of change for zooming in and out. - private static final float ZOOM_DELTA_PREVIEW = 0.2f; - private static final float ZOOM_DELTA_REVIEW = 0.5f; - private static final float ANIMATION_DURATION = 400; // ms - public static final float INITIAL_DARKEN_ALPHA = 0.8f; - - private long mAnimStartTime; // milliseconds. - // The drawing width and height of the review image. This is saved when the - // texture is copied. - private int mReviewDrawingWidth; - private int mReviewDrawingHeight; - // The maximum width of the camera screen nail width from onDraw. We need to - // know how much the preview is scaled and scale the review the same amount. - // For example, the preview is not full screen in film strip mode. - private int mPreviewFrameLayoutWidth; - - public SwitchAnimManager() { - } - - public void setReviewDrawingSize(int width, int height) { - mReviewDrawingWidth = width; - mReviewDrawingHeight = height; - } - - // width: the width of PreviewFrameLayout view. - // height: the height of PreviewFrameLayout view. Not used. Kept for - // consistency. - public void setPreviewFrameLayoutSize(int width, int height) { - mPreviewFrameLayoutWidth = width; - } - - // w and h: the rectangle area where the animation takes place. - public void startAnimation() { - mAnimStartTime = SystemClock.uptimeMillis(); - } - - // Returns true if the animation has been drawn. - // preview: camera preview view. - // review: snapshot of the preview before switching the camera. - public boolean drawAnimation(GLCanvas canvas, int x, int y, int width, - int height, CameraScreenNail preview, RawTexture review) { - long timeDiff = SystemClock.uptimeMillis() - mAnimStartTime; - if (timeDiff > ANIMATION_DURATION) return false; - float fraction = timeDiff / ANIMATION_DURATION; - - // Calculate the position and the size of the preview. - float centerX = x + width / 2f; - float centerY = y + height / 2f; - float previewAnimScale = 1 - ZOOM_DELTA_PREVIEW * (1 - fraction); - float previewWidth = width * previewAnimScale; - float previewHeight = height * previewAnimScale; - int previewX = Math.round(centerX - previewWidth / 2); - int previewY = Math.round(centerY - previewHeight / 2); - - // Calculate the position and the size of the review. - float reviewAnimScale = 1 + ZOOM_DELTA_REVIEW * fraction; - - // Calculate how much preview is scaled. - // The scaling is done by PhotoView in Gallery so we don't have the - // scaling information but only the width and the height passed to this - // method. The inference of the scale ratio is done by matching the - // current width and the original width we have at first when the camera - // layout is inflated. - float scaleRatio = 1; - if (mPreviewFrameLayoutWidth != 0) { - scaleRatio = (float) width / mPreviewFrameLayoutWidth; - } else { - Log.e(TAG, "mPreviewFrameLayoutWidth is 0."); - } - float reviewWidth = mReviewDrawingWidth * reviewAnimScale * scaleRatio; - float reviewHeight = mReviewDrawingHeight * reviewAnimScale * scaleRatio; - int reviewX = Math.round(centerX - reviewWidth / 2); - int reviewY = Math.round(centerY - reviewHeight / 2); - - // Draw the preview. - float alpha = canvas.getAlpha(); - canvas.setAlpha(fraction); // fade in - preview.directDraw(canvas, previewX, previewY, Math.round(previewWidth), - Math.round(previewHeight)); - - // Draw the review. - canvas.setAlpha((1f - fraction) * INITIAL_DARKEN_ALPHA); // fade out - review.draw(canvas, reviewX, reviewY, Math.round(reviewWidth), - Math.round(reviewHeight)); - canvas.setAlpha(alpha); - return true; - } - - public boolean drawDarkPreview(GLCanvas canvas, int x, int y, int width, - int height, RawTexture review) { - // Calculate the position and the size. - float centerX = x + width / 2f; - float centerY = y + height / 2f; - float scaleRatio = 1; - if (mPreviewFrameLayoutWidth != 0) { - scaleRatio = (float) width / mPreviewFrameLayoutWidth; - } else { - Log.e(TAG, "mPreviewFrameLayoutWidth is 0."); - } - float reviewWidth = mReviewDrawingWidth * scaleRatio; - float reviewHeight = mReviewDrawingHeight * scaleRatio; - int reviewX = Math.round(centerX - reviewWidth / 2); - int reviewY = Math.round(centerY - reviewHeight / 2); - - // Draw the review. - float alpha = canvas.getAlpha(); - canvas.setAlpha(INITIAL_DARKEN_ALPHA); - review.draw(canvas, reviewX, reviewY, Math.round(reviewWidth), - Math.round(reviewHeight)); - canvas.setAlpha(alpha); - return true; - } - -} diff --git a/src/com/android/camera/Util.java b/src/com/android/camera/Util.java deleted file mode 100644 index 11176a79b..000000000 --- a/src/com/android/camera/Util.java +++ /dev/null @@ -1,818 +0,0 @@ -/* - * Copyright (C) 2009 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.camera; - -import java.io.Closeable; -import java.io.IOException; -import java.lang.reflect.Method; -import java.text.SimpleDateFormat; -import java.util.Date; -import java.util.List; -import java.util.StringTokenizer; - -import android.annotation.TargetApi; -import android.app.Activity; -import android.app.AlertDialog; -import android.app.admin.DevicePolicyManager; -import android.content.ActivityNotFoundException; -import android.content.ContentResolver; -import android.content.Context; -import android.content.DialogInterface; -import android.content.Intent; -import android.graphics.Bitmap; -import android.graphics.BitmapFactory; -import android.graphics.Matrix; -import android.graphics.Point; -import android.graphics.Rect; -import android.graphics.RectF; -import android.hardware.Camera; -import android.hardware.Camera.CameraInfo; -import android.hardware.Camera.Parameters; -import android.hardware.Camera.Size; -import android.location.Location; -import android.net.Uri; -import android.os.Build; -import android.os.ParcelFileDescriptor; -import android.telephony.TelephonyManager; -import android.util.DisplayMetrics; -import android.util.FloatMath; -import android.util.Log; -import android.util.TypedValue; -import android.view.Display; -import android.view.OrientationEventListener; -import android.view.Surface; -import android.view.View; -import android.view.WindowManager; -import android.view.animation.AlphaAnimation; -import android.view.animation.Animation; -import android.widget.Toast; - -import com.android.camera2.R; -import com.android.gallery3d.common.ApiHelper; - -/** - * Collection of utility functions used in this package. - */ -public class Util { - private static final String TAG = "Util"; - - // Orientation hysteresis amount used in rounding, in degrees - public static final int ORIENTATION_HYSTERESIS = 5; - - public static final String REVIEW_ACTION = "com.android.camera.action.REVIEW"; - // See android.hardware.Camera.ACTION_NEW_PICTURE. - public static final String ACTION_NEW_PICTURE = "android.hardware.action.NEW_PICTURE"; - // See android.hardware.Camera.ACTION_NEW_VIDEO. - public static final String ACTION_NEW_VIDEO = "android.hardware.action.NEW_VIDEO"; - - // Fields from android.hardware.Camera.Parameters - public static final String FOCUS_MODE_CONTINUOUS_PICTURE = "continuous-picture"; - public static final String RECORDING_HINT = "recording-hint"; - private static final String AUTO_EXPOSURE_LOCK_SUPPORTED = "auto-exposure-lock-supported"; - private static final String AUTO_WHITE_BALANCE_LOCK_SUPPORTED = "auto-whitebalance-lock-supported"; - private static final String VIDEO_SNAPSHOT_SUPPORTED = "video-snapshot-supported"; - public static final String SCENE_MODE_HDR = "hdr"; - public static final String TRUE = "true"; - public static final String FALSE = "false"; - - /** Has to be in sync with the receiving MovieActivity. */ - public static final String KEY_TREAT_UP_AS_BACK = "treat-up-as-back"; - - public static boolean isSupported(String value, List supported) { - return supported == null ? false : supported.indexOf(value) >= 0; - } - - public static boolean isAutoExposureLockSupported(Parameters params) { - return TRUE.equals(params.get(AUTO_EXPOSURE_LOCK_SUPPORTED)); - } - - public static boolean isAutoWhiteBalanceLockSupported(Parameters params) { - return TRUE.equals(params.get(AUTO_WHITE_BALANCE_LOCK_SUPPORTED)); - } - - public static boolean isVideoSnapshotSupported(Parameters params) { - return TRUE.equals(params.get(VIDEO_SNAPSHOT_SUPPORTED)); - } - - public static boolean isCameraHdrSupported(Parameters params) { - List supported = params.getSupportedSceneModes(); - return (supported != null) && supported.contains(SCENE_MODE_HDR); - } - - @TargetApi(ApiHelper.VERSION_CODES.ICE_CREAM_SANDWICH) - public static boolean isMeteringAreaSupported(Parameters params) { - if (ApiHelper.HAS_CAMERA_METERING_AREA) { - return params.getMaxNumMeteringAreas() > 0; - } - return false; - } - - @TargetApi(ApiHelper.VERSION_CODES.ICE_CREAM_SANDWICH) - public static boolean isFocusAreaSupported(Parameters params) { - if (ApiHelper.HAS_CAMERA_FOCUS_AREA) { - return (params.getMaxNumFocusAreas() > 0 - && isSupported(Parameters.FOCUS_MODE_AUTO, - params.getSupportedFocusModes())); - } - return false; - } - - // Private intent extras. Test only. - private static final String EXTRAS_CAMERA_FACING = - "android.intent.extras.CAMERA_FACING"; - - private static float sPixelDensity = 1; - private static ImageFileNamer sImageFileNamer; - - private Util() { - } - - public static void initialize(Context context) { - DisplayMetrics metrics = new DisplayMetrics(); - WindowManager wm = (WindowManager) - context.getSystemService(Context.WINDOW_SERVICE); - wm.getDefaultDisplay().getMetrics(metrics); - sPixelDensity = metrics.density; - sImageFileNamer = new ImageFileNamer( - context.getString(R.string.image_file_name_format)); - } - - public static int dpToPixel(int dp) { - return Math.round(sPixelDensity * dp); - } - - // Rotates the bitmap by the specified degree. - // If a new bitmap is created, the original bitmap is recycled. - public static Bitmap rotate(Bitmap b, int degrees) { - return rotateAndMirror(b, degrees, false); - } - - // Rotates and/or mirrors the bitmap. If a new bitmap is created, the - // original bitmap is recycled. - public static Bitmap rotateAndMirror(Bitmap b, int degrees, boolean mirror) { - if ((degrees != 0 || mirror) && b != null) { - Matrix m = new Matrix(); - // Mirror first. - // horizontal flip + rotation = -rotation + horizontal flip - if (mirror) { - m.postScale(-1, 1); - degrees = (degrees + 360) % 360; - if (degrees == 0 || degrees == 180) { - m.postTranslate(b.getWidth(), 0); - } else if (degrees == 90 || degrees == 270) { - m.postTranslate(b.getHeight(), 0); - } else { - throw new IllegalArgumentException("Invalid degrees=" + degrees); - } - } - if (degrees != 0) { - // clockwise - m.postRotate(degrees, - (float) b.getWidth() / 2, (float) b.getHeight() / 2); - } - - try { - Bitmap b2 = Bitmap.createBitmap( - b, 0, 0, b.getWidth(), b.getHeight(), m, true); - if (b != b2) { - b.recycle(); - b = b2; - } - } catch (OutOfMemoryError ex) { - // We have no memory to rotate. Return the original bitmap. - } - } - return b; - } - - /* - * Compute the sample size as a function of minSideLength - * and maxNumOfPixels. - * minSideLength is used to specify that minimal width or height of a - * bitmap. - * maxNumOfPixels is used to specify the maximal size in pixels that is - * tolerable in terms of memory usage. - * - * The function returns a sample size based on the constraints. - * Both size and minSideLength can be passed in as -1 - * which indicates no care of the corresponding constraint. - * The functions prefers returning a sample size that - * generates a smaller bitmap, unless minSideLength = -1. - * - * Also, the function rounds up the sample size to a power of 2 or multiple - * of 8 because BitmapFactory only honors sample size this way. - * For example, BitmapFactory downsamples an image by 2 even though the - * request is 3. So we round up the sample size to avoid OOM. - */ - public static int computeSampleSize(BitmapFactory.Options options, - int minSideLength, int maxNumOfPixels) { - int initialSize = computeInitialSampleSize(options, minSideLength, - maxNumOfPixels); - - int roundedSize; - if (initialSize <= 8) { - roundedSize = 1; - while (roundedSize < initialSize) { - roundedSize <<= 1; - } - } else { - roundedSize = (initialSize + 7) / 8 * 8; - } - - return roundedSize; - } - - private static int computeInitialSampleSize(BitmapFactory.Options options, - int minSideLength, int maxNumOfPixels) { - double w = options.outWidth; - double h = options.outHeight; - - int lowerBound = (maxNumOfPixels < 0) ? 1 : - (int) Math.ceil(Math.sqrt(w * h / maxNumOfPixels)); - int upperBound = (minSideLength < 0) ? 128 : - (int) Math.min(Math.floor(w / minSideLength), - Math.floor(h / minSideLength)); - - if (upperBound < lowerBound) { - // return the larger one when there is no overlapping zone. - return lowerBound; - } - - if (maxNumOfPixels < 0 && minSideLength < 0) { - return 1; - } else if (minSideLength < 0) { - return lowerBound; - } else { - return upperBound; - } - } - - public static Bitmap makeBitmap(byte[] jpegData, int maxNumOfPixels) { - try { - BitmapFactory.Options options = new BitmapFactory.Options(); - options.inJustDecodeBounds = true; - BitmapFactory.decodeByteArray(jpegData, 0, jpegData.length, - options); - if (options.mCancel || options.outWidth == -1 - || options.outHeight == -1) { - return null; - } - options.inSampleSize = computeSampleSize( - options, -1, maxNumOfPixels); - options.inJustDecodeBounds = false; - - options.inDither = false; - options.inPreferredConfig = Bitmap.Config.ARGB_8888; - return BitmapFactory.decodeByteArray(jpegData, 0, jpegData.length, - options); - } catch (OutOfMemoryError ex) { - Log.e(TAG, "Got oom exception ", ex); - return null; - } - } - - public static void closeSilently(Closeable c) { - if (c == null) return; - try { - c.close(); - } catch (Throwable t) { - // do nothing - } - } - - public static void Assert(boolean cond) { - if (!cond) { - throw new AssertionError(); - } - } - - @TargetApi(ApiHelper.VERSION_CODES.ICE_CREAM_SANDWICH) - private static void throwIfCameraDisabled(Activity activity) throws CameraDisabledException { - // Check if device policy has disabled the camera. - if (ApiHelper.HAS_GET_CAMERA_DISABLED) { - DevicePolicyManager dpm = (DevicePolicyManager) activity.getSystemService( - Context.DEVICE_POLICY_SERVICE); - if (dpm.getCameraDisabled(null)) { - throw new CameraDisabledException(); - } - } - } - - public static CameraManager.CameraProxy openCamera( - Activity activity, int cameraId) - throws CameraHardwareException, CameraDisabledException { - throwIfCameraDisabled(activity); - - try { - return CameraHolder.instance().open(cameraId); - } catch (CameraHardwareException e) { - // In eng build, we throw the exception so that test tool - // can detect it and report it - if ("eng".equals(Build.TYPE)) { - throw new RuntimeException("openCamera failed", e); - } else { - throw e; - } - } - } - - public static void showErrorAndFinish(final Activity activity, int msgId) { - DialogInterface.OnClickListener buttonListener = - new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - activity.finish(); - } - }; - TypedValue out = new TypedValue(); - activity.getTheme().resolveAttribute(android.R.attr.alertDialogIcon, out, true); - new AlertDialog.Builder(activity) - .setCancelable(false) - .setTitle(R.string.camera_error_title) - .setMessage(msgId) - .setNeutralButton(R.string.dialog_ok, buttonListener) - .setIcon(out.resourceId) - .show(); - } - - public static T checkNotNull(T object) { - if (object == null) throw new NullPointerException(); - return object; - } - - public static boolean equals(Object a, Object b) { - return (a == b) || (a == null ? false : a.equals(b)); - } - - public static int nextPowerOf2(int n) { - n -= 1; - n |= n >>> 16; - n |= n >>> 8; - n |= n >>> 4; - n |= n >>> 2; - n |= n >>> 1; - return n + 1; - } - - public static float distance(float x, float y, float sx, float sy) { - float dx = x - sx; - float dy = y - sy; - return FloatMath.sqrt(dx * dx + dy * dy); - } - - public static int clamp(int x, int min, int max) { - if (x > max) return max; - if (x < min) return min; - return x; - } - - public static int getDisplayRotation(Activity activity) { - int rotation = activity.getWindowManager().getDefaultDisplay() - .getRotation(); - switch (rotation) { - case Surface.ROTATION_0: return 0; - case Surface.ROTATION_90: return 90; - case Surface.ROTATION_180: return 180; - case Surface.ROTATION_270: return 270; - } - return 0; - } - - public static int getDisplayOrientation(int degrees, int cameraId) { - // See android.hardware.Camera.setDisplayOrientation for - // documentation. - Camera.CameraInfo info = new Camera.CameraInfo(); - Camera.getCameraInfo(cameraId, info); - int result; - if (info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) { - result = (info.orientation + degrees) % 360; - result = (360 - result) % 360; // compensate the mirror - } else { // back-facing - result = (info.orientation - degrees + 360) % 360; - } - return result; - } - - public static int getCameraOrientation(int cameraId) { - Camera.CameraInfo info = new Camera.CameraInfo(); - Camera.getCameraInfo(cameraId, info); - return info.orientation; - } - - public static int roundOrientation(int orientation, int orientationHistory) { - boolean changeOrientation = false; - if (orientationHistory == OrientationEventListener.ORIENTATION_UNKNOWN) { - changeOrientation = true; - } else { - int dist = Math.abs(orientation - orientationHistory); - dist = Math.min( dist, 360 - dist ); - changeOrientation = ( dist >= 45 + ORIENTATION_HYSTERESIS ); - } - if (changeOrientation) { - return ((orientation + 45) / 90 * 90) % 360; - } - return orientationHistory; - } - - @SuppressWarnings("deprecation") - @TargetApi(Build.VERSION_CODES.HONEYCOMB_MR2) - private static Point getDefaultDisplaySize(Activity activity, Point size) { - Display d = activity.getWindowManager().getDefaultDisplay(); - if (Build.VERSION.SDK_INT >= ApiHelper.VERSION_CODES.HONEYCOMB_MR2) { - d.getSize(size); - } else { - size.set(d.getWidth(), d.getHeight()); - } - return size; - } - - public static Size getOptimalPreviewSize(Activity currentActivity, - List sizes, double targetRatio) { - // Use a very small tolerance because we want an exact match. - final double ASPECT_TOLERANCE = 0.001; - if (sizes == null) return null; - - Size optimalSize = null; - double minDiff = Double.MAX_VALUE; - - // Because of bugs of overlay and layout, we sometimes will try to - // layout the viewfinder in the portrait orientation and thus get the - // wrong size of preview surface. When we change the preview size, the - // new overlay will be created before the old one closed, which causes - // an exception. For now, just get the screen size. - Point point = getDefaultDisplaySize(currentActivity, new Point()); - int targetHeight = Math.min(point.x, point.y); - // Try to find an size match aspect ratio and size - for (Size size : sizes) { - double ratio = (double) size.width / size.height; - if (Math.abs(ratio - targetRatio) > ASPECT_TOLERANCE) continue; - if (Math.abs(size.height - targetHeight) < minDiff) { - optimalSize = size; - minDiff = Math.abs(size.height - targetHeight); - } - } - // Cannot find the one match the aspect ratio. This should not happen. - // Ignore the requirement. - if (optimalSize == null) { - Log.w(TAG, "No preview size match the aspect ratio"); - minDiff = Double.MAX_VALUE; - for (Size size : sizes) { - if (Math.abs(size.height - targetHeight) < minDiff) { - optimalSize = size; - minDiff = Math.abs(size.height - targetHeight); - } - } - } - return optimalSize; - } - - // Returns the largest picture size which matches the given aspect ratio. - public static Size getOptimalVideoSnapshotPictureSize( - List sizes, double targetRatio) { - // Use a very small tolerance because we want an exact match. - final double ASPECT_TOLERANCE = 0.001; - if (sizes == null) return null; - - Size optimalSize = null; - - // Try to find a size matches aspect ratio and has the largest width - for (Size size : sizes) { - double ratio = (double) size.width / size.height; - if (Math.abs(ratio - targetRatio) > ASPECT_TOLERANCE) continue; - if (optimalSize == null || size.width > optimalSize.width) { - optimalSize = size; - } - } - - // Cannot find one that matches the aspect ratio. This should not happen. - // Ignore the requirement. - if (optimalSize == null) { - Log.w(TAG, "No picture size match the aspect ratio"); - for (Size size : sizes) { - if (optimalSize == null || size.width > optimalSize.width) { - optimalSize = size; - } - } - } - return optimalSize; - } - - public static void dumpParameters(Parameters parameters) { - String flattened = parameters.flatten(); - StringTokenizer tokenizer = new StringTokenizer(flattened, ";"); - Log.d(TAG, "Dump all camera parameters:"); - while (tokenizer.hasMoreElements()) { - Log.d(TAG, tokenizer.nextToken()); - } - } - - /** - * Returns whether the device is voice-capable (meaning, it can do MMS). - */ - public static boolean isMmsCapable(Context context) { - TelephonyManager telephonyManager = (TelephonyManager) - context.getSystemService(Context.TELEPHONY_SERVICE); - if (telephonyManager == null) { - return false; - } - - try { - Class partypes[] = new Class[0]; - Method sIsVoiceCapable = TelephonyManager.class.getMethod( - "isVoiceCapable", partypes); - - Object arglist[] = new Object[0]; - Object retobj = sIsVoiceCapable.invoke(telephonyManager, arglist); - return (Boolean) retobj; - } catch (java.lang.reflect.InvocationTargetException ite) { - // Failure, must be another device. - // Assume that it is voice capable. - } catch (IllegalAccessException iae) { - // Failure, must be an other device. - // Assume that it is voice capable. - } catch (NoSuchMethodException nsme) { - } - return true; - } - - // This is for test only. Allow the camera to launch the specific camera. - public static int getCameraFacingIntentExtras(Activity currentActivity) { - int cameraId = -1; - - int intentCameraId = - currentActivity.getIntent().getIntExtra(Util.EXTRAS_CAMERA_FACING, -1); - - if (isFrontCameraIntent(intentCameraId)) { - // Check if the front camera exist - int frontCameraId = CameraHolder.instance().getFrontCameraId(); - if (frontCameraId != -1) { - cameraId = frontCameraId; - } - } else if (isBackCameraIntent(intentCameraId)) { - // Check if the back camera exist - int backCameraId = CameraHolder.instance().getBackCameraId(); - if (backCameraId != -1) { - cameraId = backCameraId; - } - } - return cameraId; - } - - private static boolean isFrontCameraIntent(int intentCameraId) { - return (intentCameraId == android.hardware.Camera.CameraInfo.CAMERA_FACING_FRONT); - } - - private static boolean isBackCameraIntent(int intentCameraId) { - return (intentCameraId == android.hardware.Camera.CameraInfo.CAMERA_FACING_BACK); - } - - private static int sLocation[] = new int[2]; - - // This method is not thread-safe. - public static boolean pointInView(float x, float y, View v) { - v.getLocationInWindow(sLocation); - return x >= sLocation[0] && x < (sLocation[0] + v.getWidth()) - && y >= sLocation[1] && y < (sLocation[1] + v.getHeight()); - } - - public static int[] getRelativeLocation(View reference, View view) { - reference.getLocationInWindow(sLocation); - int referenceX = sLocation[0]; - int referenceY = sLocation[1]; - view.getLocationInWindow(sLocation); - sLocation[0] -= referenceX; - sLocation[1] -= referenceY; - return sLocation; - } - - public static boolean isUriValid(Uri uri, ContentResolver resolver) { - if (uri == null) return false; - - try { - ParcelFileDescriptor pfd = resolver.openFileDescriptor(uri, "r"); - if (pfd == null) { - Log.e(TAG, "Fail to open URI. URI=" + uri); - return false; - } - pfd.close(); - } catch (IOException ex) { - return false; - } - return true; - } - - public static void viewUri(Uri uri, Context context) { - if (!isUriValid(uri, context.getContentResolver())) { - Log.e(TAG, "Uri invalid. uri=" + uri); - return; - } - - try { - context.startActivity(new Intent(Util.REVIEW_ACTION, uri)); - } catch (ActivityNotFoundException ex) { - try { - context.startActivity(new Intent(Intent.ACTION_VIEW, uri)); - } catch (ActivityNotFoundException e) { - Log.e(TAG, "review image fail. uri=" + uri, e); - } - } - } - - public static void dumpRect(RectF rect, String msg) { - Log.v(TAG, msg + "=(" + rect.left + "," + rect.top - + "," + rect.right + "," + rect.bottom + ")"); - } - - public static void rectFToRect(RectF rectF, Rect rect) { - rect.left = Math.round(rectF.left); - rect.top = Math.round(rectF.top); - rect.right = Math.round(rectF.right); - rect.bottom = Math.round(rectF.bottom); - } - - public static void prepareMatrix(Matrix matrix, boolean mirror, int displayOrientation, - int viewWidth, int viewHeight) { - // Need mirror for front camera. - matrix.setScale(mirror ? -1 : 1, 1); - // This is the value for android.hardware.Camera.setDisplayOrientation. - matrix.postRotate(displayOrientation); - // Camera driver coordinates range from (-1000, -1000) to (1000, 1000). - // UI coordinates range from (0, 0) to (width, height). - matrix.postScale(viewWidth / 2000f, viewHeight / 2000f); - matrix.postTranslate(viewWidth / 2f, viewHeight / 2f); - } - - public static String createJpegName(long dateTaken) { - synchronized (sImageFileNamer) { - return sImageFileNamer.generateName(dateTaken); - } - } - - public static void broadcastNewPicture(Context context, Uri uri) { - context.sendBroadcast(new Intent(ACTION_NEW_PICTURE, uri)); - // Keep compatibility - context.sendBroadcast(new Intent("com.android.camera.NEW_PICTURE", uri)); - } - - public static void fadeIn(View view, float startAlpha, float endAlpha, long duration) { - if (view.getVisibility() == View.VISIBLE) return; - - view.setVisibility(View.VISIBLE); - Animation animation = new AlphaAnimation(startAlpha, endAlpha); - animation.setDuration(duration); - view.startAnimation(animation); - } - - public static void fadeIn(View view) { - fadeIn(view, 0F, 1F, 400); - - // We disabled the button in fadeOut(), so enable it here. - view.setEnabled(true); - } - - public static void fadeOut(View view) { - if (view.getVisibility() != View.VISIBLE) return; - - // Since the button is still clickable before fade-out animation - // ends, we disable the button first to block click. - view.setEnabled(false); - Animation animation = new AlphaAnimation(1F, 0F); - animation.setDuration(400); - view.startAnimation(animation); - view.setVisibility(View.GONE); - } - - public static int getJpegRotation(int cameraId, int orientation) { - // See android.hardware.Camera.Parameters.setRotation for - // documentation. - int rotation = 0; - if (orientation != OrientationEventListener.ORIENTATION_UNKNOWN) { - CameraInfo info = CameraHolder.instance().getCameraInfo()[cameraId]; - if (info.facing == CameraInfo.CAMERA_FACING_FRONT) { - rotation = (info.orientation - orientation + 360) % 360; - } else { // back-facing camera - rotation = (info.orientation + orientation) % 360; - } - } - return rotation; - } - - /** - * Down-samples a jpeg byte array. - * @param data a byte array of jpeg data - * @param downSampleFactor down-sample factor - * @return decoded and down-sampled bitmap - */ - public static Bitmap downSample(final byte[] data, int downSampleFactor) { - final BitmapFactory.Options opts = new BitmapFactory.Options(); - // Downsample the image - opts.inSampleSize = downSampleFactor; - return BitmapFactory.decodeByteArray(data, 0, data.length, opts); - } - - public static void setGpsParameters(Parameters parameters, Location loc) { - // Clear previous GPS location from the parameters. - parameters.removeGpsData(); - - // We always encode GpsTimeStamp - parameters.setGpsTimestamp(System.currentTimeMillis() / 1000); - - // Set GPS location. - if (loc != null) { - double lat = loc.getLatitude(); - double lon = loc.getLongitude(); - boolean hasLatLon = (lat != 0.0d) || (lon != 0.0d); - - if (hasLatLon) { - Log.d(TAG, "Set gps location"); - parameters.setGpsLatitude(lat); - parameters.setGpsLongitude(lon); - parameters.setGpsProcessingMethod(loc.getProvider().toUpperCase()); - if (loc.hasAltitude()) { - parameters.setGpsAltitude(loc.getAltitude()); - } else { - // for NETWORK_PROVIDER location provider, we may have - // no altitude information, but the driver needs it, so - // we fake one. - parameters.setGpsAltitude(0); - } - if (loc.getTime() != 0) { - // Location.getTime() is UTC in milliseconds. - // gps-timestamp is UTC in seconds. - long utcTimeSeconds = loc.getTime() / 1000; - parameters.setGpsTimestamp(utcTimeSeconds); - } - } else { - loc = null; - } - } - } - - - public static int[] getMaxPreviewFpsRange(Parameters params) { - List frameRates = params.getSupportedPreviewFpsRange(); - if (frameRates != null && frameRates.size() > 0) { - // The list is sorted. Return the last element. - return frameRates.get(frameRates.size() - 1); - } - return new int[0]; - } - - private static class ImageFileNamer { - private SimpleDateFormat mFormat; - - // The date (in milliseconds) used to generate the last name. - private long mLastDate; - - // Number of names generated for the same second. - private int mSameSecondCount; - - public ImageFileNamer(String format) { - mFormat = new SimpleDateFormat(format); - } - - public String generateName(long dateTaken) { - Date date = new Date(dateTaken); - String result = mFormat.format(date); - - // If the last name was generated for the same second, - // we append _1, _2, etc to the name. - if (dateTaken / 1000 == mLastDate / 1000) { - mSameSecondCount++; - result += "_" + mSameSecondCount; - } else { - mLastDate = dateTaken; - mSameSecondCount = 0; - } - - return result; - } - } - - public static void playVideo(Context context, Uri uri, String title) { - try { - Intent intent = new Intent(Intent.ACTION_VIEW) - .setDataAndType(uri, "video/*") - .putExtra(Intent.EXTRA_TITLE, title) - .putExtra(KEY_TREAT_UP_AS_BACK, true); - context.startActivity(intent); - } catch (ActivityNotFoundException e) { - Toast.makeText(context, context.getString(R.string.video_err), - Toast.LENGTH_SHORT).show(); - } - } -} diff --git a/src/com/android/camera/VideoModule.java b/src/com/android/camera/VideoModule.java index 80bbbca37..90915334e 100644 --- a/src/com/android/camera/VideoModule.java +++ b/src/com/android/camera/VideoModule.java @@ -16,13 +16,6 @@ package com.android.camera; -import java.io.File; -import java.io.IOException; -import java.text.SimpleDateFormat; -import java.util.Date; -import java.util.Iterator; -import java.util.List; - import android.annotation.TargetApi; import android.app.Activity; import android.content.ActivityNotFoundException; @@ -63,15 +56,23 @@ import android.widget.Toast; import com.android.camera.CameraManager.CameraPictureCallback; import com.android.camera.CameraManager.CameraProxy; -import com.android.camera.support.app.OrientationManager; -import com.android.camera.support.util.AccessibilityUtils; +import com.android.camera.app.OrientationManager; +import com.android.camera.util.ApiHelper; +import com.android.camera.util.AccessibilityUtils; import com.android.camera.ui.PopupManager; import com.android.camera.ui.RotateTextToast; +import com.android.camera.util.CameraUtil; import com.android.camera.util.UsageStatistics; import com.android.camera2.R; -import com.android.gallery3d.common.ApiHelper; import com.android.gallery3d.exif.ExifInterface; +import java.io.File; +import java.io.IOException; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.Iterator; +import java.util.List; + public class VideoModule implements CameraModule, VideoController, CameraPreference.OnPreferenceChangedListener, @@ -223,7 +224,7 @@ public class VideoModule implements CameraModule, private void openCamera() { try { if (mCameraDevice == null) { - mCameraDevice = Util.openCamera(mActivity, mCameraId); + mCameraDevice = CameraUtil.openCamera(mActivity, mCameraId); } mParameters = mCameraDevice.getParameters(); } catch (CameraHardwareException e) { @@ -261,7 +262,7 @@ public class VideoModule implements CameraModule, // down and camera app is opened. Rotation animation will // take some time and the rotation value we have got may be // wrong. Framework does not have a callback for this now. - if ((Util.getDisplayRotation(mActivity) != mDisplayRotation) + if ((CameraUtil.getDisplayRotation(mActivity) != mDisplayRotation) && !mMediaRecorderRecording && !mSwitchingCamera) { startPreview(); } @@ -321,7 +322,7 @@ public class VideoModule implements CameraModule, } private int getPreferredCameraId(ComboPreferences preferences) { - int intentCameraId = Util.getCameraFacingIntentExtras(mActivity); + int intentCameraId = CameraUtil.getCameraFacingIntentExtras(mActivity); if (intentCameraId != -1) { // Testing purpose. Launch a specific camera through the intent // extras. @@ -370,10 +371,10 @@ public class VideoModule implements CameraModule, try { cameraOpenThread.join(); if (mOpenCameraFail) { - Util.showErrorAndFinish(mActivity, R.string.cannot_connect_camera); + CameraUtil.showErrorAndFinish(mActivity, R.string.cannot_connect_camera); return; } else if (mCameraDisabled) { - Util.showErrorAndFinish(mActivity, R.string.camera_disabled); + CameraUtil.showErrorAndFinish(mActivity, R.string.camera_disabled); return; } } catch (InterruptedException ex) { @@ -424,10 +425,10 @@ public class VideoModule implements CameraModule, } // Set rotation and gps data. - int rotation = Util.getJpegRotation(mCameraId, mOrientation); + int rotation = CameraUtil.getJpegRotation(mCameraId, mOrientation); mParameters.setRotation(rotation); Location loc = mLocationManager.getCurrentLocation(); - Util.setGpsParameters(mParameters, loc); + CameraUtil.setGpsParameters(mParameters, loc); mCameraDevice.setParameters(mParameters); Log.v(TAG, "Video snapshot start"); @@ -466,7 +467,7 @@ public class VideoModule implements CameraModule, // the camera then point the camera to floor or sky, we still have // the correct orientation. if (orientation == OrientationEventListener.ORIENTATION_UNKNOWN) return; - int newOrientation = Util.roundOrientation(orientation, mOrientation); + int newOrientation = CameraUtil.roundOrientation(orientation, mOrientation); if (mOrientation != newOrientation) { mOrientation = newOrientation; @@ -668,7 +669,7 @@ public class VideoModule implements CameraModule, it.remove(); } } - Size optimalSize = Util.getOptimalPreviewSize(mActivity, sizes, + Size optimalSize = CameraUtil.getOptimalPreviewSize(mActivity, sizes, (double) mProfile.videoFrameWidth / mProfile.videoFrameHeight); mDesiredPreviewWidth = optimalSize.width; mDesiredPreviewHeight = optimalSize.height; @@ -716,11 +717,11 @@ public class VideoModule implements CameraModule, resetEffect(); openCamera(); if (mOpenCameraFail) { - Util.showErrorAndFinish(mActivity, + CameraUtil.showErrorAndFinish(mActivity, R.string.cannot_connect_camera); return; } else if (mCameraDisabled) { - Util.showErrorAndFinish(mActivity, R.string.camera_disabled); + CameraUtil.showErrorAndFinish(mActivity, R.string.camera_disabled); return; } readVideoPreferences(); @@ -753,8 +754,8 @@ public class VideoModule implements CameraModule, } private void setDisplayOrientation() { - mDisplayRotation = Util.getDisplayRotation(mActivity); - mCameraDisplayOrientation = Util.getDisplayOrientation(mDisplayRotation, mCameraId); + mDisplayRotation = CameraUtil.getDisplayRotation(mActivity); + mCameraDisplayOrientation = CameraUtil.getDisplayOrientation(mDisplayRotation, mCameraId); // Change the camera display orientation if (mCameraDevice != null) { mCameraDevice.setDisplayOrientation(mCameraDisplayOrientation); @@ -764,7 +765,7 @@ public class VideoModule implements CameraModule, @Override public void updateCameraOrientation() { if (mMediaRecorderRecording) return; - if (mDisplayRotation != Util.getDisplayRotation(mActivity)) { + if (mDisplayRotation != CameraUtil.getDisplayRotation(mActivity)) { setDisplayOrientation(); } } @@ -819,9 +820,9 @@ public class VideoModule implements CameraModule, throw new RuntimeException("startPreview failed", ex); } finally { if (mOpenCameraFail) { - Util.showErrorAndFinish(mActivity, R.string.cannot_connect_camera); + CameraUtil.showErrorAndFinish(mActivity, R.string.cannot_connect_camera); } else if (mCameraDisabled) { - Util.showErrorAndFinish(mActivity, R.string.camera_disabled); + CameraUtil.showErrorAndFinish(mActivity, R.string.camera_disabled); } } @@ -1048,7 +1049,7 @@ public class VideoModule implements CameraModule, // SurfaceView we will have to take everything into account so the // display rotation is considered. mCameraDevice.setDisplayOrientation( - Util.getDisplayOrientation(mDisplayRotation, mCameraId)); + CameraUtil.getDisplayOrientation(mDisplayRotation, mCameraId)); mCameraDevice.startPreview(); mPreviewing = true; mMediaRecorder.setPreviewDisplay(mUI.getSurfaceHolder().getSurface()); @@ -1496,7 +1497,7 @@ public class VideoModule implements CameraModule, // it to match the UI orientation (and mirror if it is front-facing camera). CameraInfo[] info = CameraHolder.instance().getCameraInfo(); boolean mirror = (info[mCameraId].facing == CameraInfo.CAMERA_FACING_FRONT); - bitmap = Util.rotateAndMirror(bitmap, 0, mirror); + bitmap = CameraUtil.rotateAndMirror(bitmap, 0, mirror); } return bitmap; } @@ -1730,7 +1731,7 @@ public class VideoModule implements CameraModule, @SuppressWarnings("deprecation") private void setCameraParameters() { mParameters.setPreviewSize(mDesiredPreviewWidth, mDesiredPreviewHeight); - int[] fpsRange = Util.getMaxPreviewFpsRange(mParameters); + int[] fpsRange = CameraUtil.getMaxPreviewFpsRange(mParameters); if (fpsRange.length > 0) { mParameters.setPreviewFpsRange( fpsRange[Parameters.PREVIEW_FPS_MIN_INDEX], @@ -1784,7 +1785,7 @@ public class VideoModule implements CameraModule, mParameters.setFocusMode(Parameters.FOCUS_MODE_CONTINUOUS_VIDEO); } - mParameters.set(Util.RECORDING_HINT, Util.TRUE); + mParameters.set(CameraUtil.RECORDING_HINT, CameraUtil.TRUE); // Enable video stabilization. Convenience methods not available in API // level <= 14 @@ -1798,7 +1799,7 @@ public class VideoModule implements CameraModule, // There we determine the preview size based on the picture size, but // here we determine the picture size based on the preview size. List supported = mParameters.getSupportedPictureSizes(); - Size optimalSize = Util.getOptimalVideoSnapshotPictureSize(supported, + Size optimalSize = CameraUtil.getOptimalVideoSnapshotPictureSize(supported, (double) mDesiredPreviewWidth / mDesiredPreviewHeight); Size original = mParameters.getPictureSize(); if (!original.equals(optimalSize)) { @@ -2058,7 +2059,7 @@ public class VideoModule implements CameraModule, private void initializeVideoSnapshot() { if (mParameters == null) return; - if (Util.isVideoSnapshotSupported(mParameters) && !mIsVideoCaptureIntent) { + if (CameraUtil.isVideoSnapshotSupported(mParameters) && !mIsVideoCaptureIntent) { // Show the tap to focus toast if this is the first start. if (mPreferences.getBoolean( CameraSettings.KEY_VIDEO_FIRST_USE_HINT_SHOWN, true)) { @@ -2070,7 +2071,7 @@ public class VideoModule implements CameraModule, void showVideoSnapshotUI(boolean enabled) { if (mParameters == null) return; - if (Util.isVideoSnapshotSupported(mParameters) && !mIsVideoCaptureIntent) { + if (CameraUtil.isVideoSnapshotSupported(mParameters) && !mIsVideoCaptureIntent) { if (enabled) { mUI.animateFlash(); } else { @@ -2121,7 +2122,7 @@ public class VideoModule implements CameraModule, private void storeImage(final byte[] data, Location loc) { long dateTaken = System.currentTimeMillis(); - String title = Util.createJpegName(dateTaken); + String title = CameraUtil.createJpegName(dateTaken); ExifInterface exif = Exif.getExif(data); int orientation = Exif.getOrientation(exif); Size s = mParameters.getPictureSize(); diff --git a/src/com/android/camera/VideoUI.java b/src/com/android/camera/VideoUI.java index ee49277dc..5c7eca214 100644 --- a/src/com/android/camera/VideoUI.java +++ b/src/com/android/camera/VideoUI.java @@ -53,8 +53,9 @@ import com.android.camera.ui.PieRenderer; import com.android.camera.ui.RenderOverlay; import com.android.camera.ui.RotateLayout; import com.android.camera.ui.ZoomRenderer; +import com.android.camera.util.CameraUtil; import com.android.camera2.R; -import com.android.gallery3d.common.ApiHelper; +import com.android.camera.util.ApiHelper; public class VideoUI implements PieRenderer.PieListener, PreviewGestures.SingleTapListener, @@ -87,7 +88,6 @@ public class VideoUI implements PieRenderer.PieListener, private View mMenuButton; private OnScreenIndicators mOnScreenIndicators; private RotateLayout mRecordingTimeRect; - private final Object mLock = new Object(); private SurfaceTexture mSurfaceTexture; private VideoController mController; private int mZoomMax; @@ -124,7 +124,7 @@ public class VideoUI implements PieRenderer.PieListener, // Full-screen screennail int w = width; int h = height; - if (Util.getDisplayRotation(mActivity) % 180 != 0) { + if (CameraUtil.getDisplayRotation(mActivity) % 180 != 0) { w = height; h = width; } @@ -262,7 +262,7 @@ public class VideoUI implements PieRenderer.PieListener, private void setTransformMatrix(int width, int height) { mMatrix = mTextureView.getTransform(mMatrix); - int orientation = Util.getDisplayRotation(mActivity); + int orientation = CameraUtil.getDisplayRotation(mActivity); float scaleX = 1f, scaleY = 1f; float scaledTextureWidth, scaledTextureHeight; if (width > height) { @@ -569,9 +569,9 @@ public class VideoUI implements PieRenderer.PieListener, } public void showReviewControls() { - Util.fadeOut(mShutterButton); - Util.fadeIn(mReviewDoneButton); - Util.fadeIn(mReviewPlayButton); + CameraUtil.fadeOut(mShutterButton); + CameraUtil.fadeIn(mReviewDoneButton); + CameraUtil.fadeIn(mReviewPlayButton); mReviewImage.setVisibility(View.VISIBLE); mMenuButton.setVisibility(View.GONE); mOnScreenIndicators.setVisibility(View.GONE); @@ -582,9 +582,9 @@ public class VideoUI implements PieRenderer.PieListener, mShutterButton.setEnabled(true); mMenuButton.setVisibility(View.VISIBLE); mOnScreenIndicators.setVisibility(View.VISIBLE); - Util.fadeOut(mReviewDoneButton); - Util.fadeOut(mReviewPlayButton); - Util.fadeIn(mShutterButton); + CameraUtil.fadeOut(mReviewDoneButton); + CameraUtil.fadeOut(mReviewPlayButton); + CameraUtil.fadeIn(mShutterButton); } private void setShowMenu(boolean show) { diff --git a/src/com/android/camera/app/CameraApp.java b/src/com/android/camera/app/CameraApp.java index a72581531..e4da41461 100644 --- a/src/com/android/camera/app/CameraApp.java +++ b/src/com/android/camera/app/CameraApp.java @@ -16,28 +16,15 @@ package com.android.camera.app; -import com.android.camera.Util; -import com.android.camera.util.PhotoSphereHelper; - import android.app.Application; -import android.content.Context; -public class CameraApp extends Application { +import com.android.camera.util.CameraUtil; - private StitchingProgressManager mStitchingProgressManager; +public class CameraApp extends Application { @Override public void onCreate() { super.onCreate(); - Util.initialize(this); - mStitchingProgressManager = PhotoSphereHelper.createStitchingManagerInstance(this); - } - - public Context getAndroidContext() { - return this; - } - - public StitchingProgressManager getStitchingProgressManager() { - return mStitchingProgressManager; + CameraUtil.initialize(this); } } diff --git a/src/com/android/camera/app/OrientationManager.java b/src/com/android/camera/app/OrientationManager.java new file mode 100644 index 000000000..412be3024 --- /dev/null +++ b/src/com/android/camera/app/OrientationManager.java @@ -0,0 +1,151 @@ +package com.android.camera.app; + + +import android.app.Activity; +import android.content.ContentResolver; +import android.content.Context; +import android.content.pm.ActivityInfo; +import android.content.res.Configuration; +import android.provider.Settings; +import android.util.Log; +import android.view.OrientationEventListener; +import android.view.Surface; + +import com.android.camera.util.ApiHelper; + +public class OrientationManager implements OrientationSource { + private static final String TAG = "OrientationManager"; + + // Orientation hysteresis amount used in rounding, in degrees + private static final int ORIENTATION_HYSTERESIS = 5; + + private Activity mActivity; + private MyOrientationEventListener mOrientationListener; + // If the framework orientation is locked. + private boolean mOrientationLocked = false; + + // This is true if "Settings -> Display -> Rotation Lock" is checked. We + // don't allow the orientation to be unlocked if the value is true. + private boolean mRotationLockedSetting = false; + + public OrientationManager(Activity activity) { + mActivity = activity; + mOrientationListener = new MyOrientationEventListener(activity); + } + + public void resume() { + ContentResolver resolver = mActivity.getContentResolver(); + mRotationLockedSetting = Settings.System.getInt( + resolver, Settings.System.ACCELEROMETER_ROTATION, 0) != 1; + mOrientationListener.enable(); + } + + public void pause() { + mOrientationListener.disable(); + } + + //////////////////////////////////////////////////////////////////////////// + // Orientation handling + // + // We can choose to lock the framework orientation or not. If we lock the + // framework orientation, we calculate a a compensation value according to + // current device orientation and send it to listeners. If we don't lock + // the framework orientation, we always set the compensation value to 0. + //////////////////////////////////////////////////////////////////////////// + + // Lock the framework orientation to the current device orientation + public void lockOrientation() { + if (mOrientationLocked) return; + mOrientationLocked = true; + if (ApiHelper.HAS_ORIENTATION_LOCK) { + mActivity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LOCKED); + } else { + mActivity.setRequestedOrientation(calculateCurrentScreenOrientation()); + } + } + + // Unlock the framework orientation, so it can change when the device + // rotates. + public void unlockOrientation() { + if (!mOrientationLocked) return; + mOrientationLocked = false; + Log.d(TAG, "unlock orientation"); + mActivity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_FULL_SENSOR); + } + + private int calculateCurrentScreenOrientation() { + int displayRotation = getDisplayRotation(); + // Display rotation >= 180 means we need to use the REVERSE landscape/portrait + boolean standard = displayRotation < 180; + if (mActivity.getResources().getConfiguration().orientation + == Configuration.ORIENTATION_LANDSCAPE) { + return standard + ? ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE + : ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE; + } else { + if (displayRotation == 90 || displayRotation == 270) { + // If displayRotation = 90 or 270 then we are on a landscape + // device. On landscape devices, portrait is a 90 degree + // clockwise rotation from landscape, so we need + // to flip which portrait we pick as display rotation is counter clockwise + standard = !standard; + } + return standard + ? ActivityInfo.SCREEN_ORIENTATION_PORTRAIT + : ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT; + } + } + + // This listens to the device orientation, so we can update the compensation. + private class MyOrientationEventListener extends OrientationEventListener { + public MyOrientationEventListener(Context context) { + super(context); + } + + @Override + public void onOrientationChanged(int orientation) { + // We keep the last known orientation. So if the user first orient + // the camera then point the camera to floor or sky, we still have + // the correct orientation. + if (orientation == ORIENTATION_UNKNOWN) return; + orientation = roundOrientation(orientation, 0); + } + } + + @Override + public int getDisplayRotation() { + return getDisplayRotation(mActivity); + } + + @Override + public int getCompensation() { + return 0; + } + + private static int roundOrientation(int orientation, int orientationHistory) { + boolean changeOrientation = false; + if (orientationHistory == OrientationEventListener.ORIENTATION_UNKNOWN) { + changeOrientation = true; + } else { + int dist = Math.abs(orientation - orientationHistory); + dist = Math.min(dist, 360 - dist); + changeOrientation = (dist >= 45 + ORIENTATION_HYSTERESIS); + } + if (changeOrientation) { + return ((orientation + 45) / 90 * 90) % 360; + } + return orientationHistory; + } + + private static int getDisplayRotation(Activity activity) { + int rotation = activity.getWindowManager().getDefaultDisplay() + .getRotation(); + switch (rotation) { + case Surface.ROTATION_0: return 0; + case Surface.ROTATION_90: return 90; + case Surface.ROTATION_180: return 180; + case Surface.ROTATION_270: return 270; + } + return 0; + } +} \ No newline at end of file diff --git a/src/com/android/camera/app/OrientationSource.java b/src/com/android/camera/app/OrientationSource.java new file mode 100644 index 000000000..57dcfeb40 --- /dev/null +++ b/src/com/android/camera/app/OrientationSource.java @@ -0,0 +1,6 @@ +package com.android.camera.app; + +public interface OrientationSource { + public int getDisplayRotation(); + public int getCompensation(); +} diff --git a/src/com/android/camera/data/LocalMediaData.java b/src/com/android/camera/data/LocalMediaData.java index d80862cad..cd568239d 100644 --- a/src/com/android/camera/data/LocalMediaData.java +++ b/src/com/android/camera/data/LocalMediaData.java @@ -35,7 +35,7 @@ import android.view.ViewGroup; import android.widget.FrameLayout; import android.widget.ImageView; -import com.android.camera.Util; +import com.android.camera.util.CameraUtil; import com.android.camera.ui.FilmStripView; import com.android.camera.util.PhotoSphereHelper; import com.android.camera2.R; @@ -526,7 +526,7 @@ public abstract class LocalMediaData implements LocalData { icon.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { - Util.playVideo(ctx, mPlayUri, title); + CameraUtil.playVideo(ctx, mPlayUri, title); } }); diff --git a/src/com/android/camera/support/app/OrientationManager.java b/src/com/android/camera/support/app/OrientationManager.java deleted file mode 100644 index 850d46591..000000000 --- a/src/com/android/camera/support/app/OrientationManager.java +++ /dev/null @@ -1,152 +0,0 @@ -package com.android.camera.support.app; - - -import android.app.Activity; -import android.content.ContentResolver; -import android.content.Context; -import android.content.pm.ActivityInfo; -import android.content.res.Configuration; -import android.provider.Settings; -import android.util.Log; -import android.view.OrientationEventListener; -import android.view.Surface; - -import com.android.camera.support.ui.OrientationSource; -import com.android.gallery3d.common.ApiHelper; - -public class OrientationManager implements OrientationSource { - private static final String TAG = "OrientationManager"; - - // Orientation hysteresis amount used in rounding, in degrees - private static final int ORIENTATION_HYSTERESIS = 5; - - private Activity mActivity; - private MyOrientationEventListener mOrientationListener; - // If the framework orientation is locked. - private boolean mOrientationLocked = false; - - // This is true if "Settings -> Display -> Rotation Lock" is checked. We - // don't allow the orientation to be unlocked if the value is true. - private boolean mRotationLockedSetting = false; - - public OrientationManager(Activity activity) { - mActivity = activity; - mOrientationListener = new MyOrientationEventListener(activity); - } - - public void resume() { - ContentResolver resolver = mActivity.getContentResolver(); - mRotationLockedSetting = Settings.System.getInt( - resolver, Settings.System.ACCELEROMETER_ROTATION, 0) != 1; - mOrientationListener.enable(); - } - - public void pause() { - mOrientationListener.disable(); - } - - //////////////////////////////////////////////////////////////////////////// - // Orientation handling - // - // We can choose to lock the framework orientation or not. If we lock the - // framework orientation, we calculate a a compensation value according to - // current device orientation and send it to listeners. If we don't lock - // the framework orientation, we always set the compensation value to 0. - //////////////////////////////////////////////////////////////////////////// - - // Lock the framework orientation to the current device orientation - public void lockOrientation() { - if (mOrientationLocked) return; - mOrientationLocked = true; - if (ApiHelper.HAS_ORIENTATION_LOCK) { - mActivity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LOCKED); - } else { - mActivity.setRequestedOrientation(calculateCurrentScreenOrientation()); - } - } - - // Unlock the framework orientation, so it can change when the device - // rotates. - public void unlockOrientation() { - if (!mOrientationLocked) return; - mOrientationLocked = false; - Log.d(TAG, "unlock orientation"); - mActivity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_FULL_SENSOR); - } - - private int calculateCurrentScreenOrientation() { - int displayRotation = getDisplayRotation(); - // Display rotation >= 180 means we need to use the REVERSE landscape/portrait - boolean standard = displayRotation < 180; - if (mActivity.getResources().getConfiguration().orientation - == Configuration.ORIENTATION_LANDSCAPE) { - return standard - ? ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE - : ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE; - } else { - if (displayRotation == 90 || displayRotation == 270) { - // If displayRotation = 90 or 270 then we are on a landscape - // device. On landscape devices, portrait is a 90 degree - // clockwise rotation from landscape, so we need - // to flip which portrait we pick as display rotation is counter clockwise - standard = !standard; - } - return standard - ? ActivityInfo.SCREEN_ORIENTATION_PORTRAIT - : ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT; - } - } - - // This listens to the device orientation, so we can update the compensation. - private class MyOrientationEventListener extends OrientationEventListener { - public MyOrientationEventListener(Context context) { - super(context); - } - - @Override - public void onOrientationChanged(int orientation) { - // We keep the last known orientation. So if the user first orient - // the camera then point the camera to floor or sky, we still have - // the correct orientation. - if (orientation == ORIENTATION_UNKNOWN) return; - orientation = roundOrientation(orientation, 0); - } - } - - @Override - public int getDisplayRotation() { - return getDisplayRotation(mActivity); - } - - @Override - public int getCompensation() { - return 0; - } - - private static int roundOrientation(int orientation, int orientationHistory) { - boolean changeOrientation = false; - if (orientationHistory == OrientationEventListener.ORIENTATION_UNKNOWN) { - changeOrientation = true; - } else { - int dist = Math.abs(orientation - orientationHistory); - dist = Math.min(dist, 360 - dist); - changeOrientation = (dist >= 45 + ORIENTATION_HYSTERESIS); - } - if (changeOrientation) { - return ((orientation + 45) / 90 * 90) % 360; - } - return orientationHistory; - } - - private static int getDisplayRotation(Activity activity) { - int rotation = activity.getWindowManager().getDefaultDisplay() - .getRotation(); - switch (rotation) { - case Surface.ROTATION_0: return 0; - case Surface.ROTATION_90: return 90; - case Surface.ROTATION_180: return 180; - case Surface.ROTATION_270: return 270; - } - return 0; - } -} \ No newline at end of file diff --git a/src/com/android/camera/support/app/StitchingChangeListener.java b/src/com/android/camera/support/app/StitchingChangeListener.java deleted file mode 100644 index f67fb3963..000000000 --- a/src/com/android/camera/support/app/StitchingChangeListener.java +++ /dev/null @@ -1,11 +0,0 @@ -package com.android.camera.support.app; - -import android.net.Uri; - -public interface StitchingChangeListener { - public void onStitchingQueued(Uri uri); - - public void onStitchingResult(Uri uri); - - public void onStitchingProgress(Uri uri, int progress); -} diff --git a/src/com/android/camera/support/common/ApiHelper.java b/src/com/android/camera/support/common/ApiHelper.java deleted file mode 100644 index e6c1f5fb6..000000000 --- a/src/com/android/camera/support/common/ApiHelper.java +++ /dev/null @@ -1,219 +0,0 @@ -package com.android.camera.support.common; - - -import android.app.admin.DevicePolicyManager; -import android.content.ComponentName; -import android.hardware.Camera; -import android.os.Build; -import android.provider.MediaStore.MediaColumns; -import android.view.View; -import android.view.WindowManager; - -import java.lang.reflect.Field; - -public class ApiHelper { - public static interface VERSION_CODES { - // These value are copied from Build.VERSION_CODES - public static final int GINGERBREAD_MR1 = 10; - public static final int HONEYCOMB = 11; - public static final int HONEYCOMB_MR1 = 12; - public static final int HONEYCOMB_MR2 = 13; - public static final int ICE_CREAM_SANDWICH = 14; - public static final int ICE_CREAM_SANDWICH_MR1 = 15; - public static final int JELLY_BEAN = 16; - public static final int JELLY_BEAN_MR1 = 17; - public static final int JELLY_BEAN_MR2 = 18; - } - - public static final boolean AT_LEAST_16 = Build.VERSION.SDK_INT >= 16; - - public static final boolean USE_888_PIXEL_FORMAT = - Build.VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN; - - public static final boolean ENABLE_PHOTO_EDITOR = - Build.VERSION.SDK_INT >= VERSION_CODES.ICE_CREAM_SANDWICH; - - public static final boolean HAS_VIEW_SYSTEM_UI_FLAG_LAYOUT_STABLE = - hasField(View.class, "SYSTEM_UI_FLAG_LAYOUT_STABLE"); - - public static final boolean HAS_VIEW_SYSTEM_UI_FLAG_HIDE_NAVIGATION = - hasField(View.class, "SYSTEM_UI_FLAG_HIDE_NAVIGATION"); - - public static final boolean HAS_MEDIA_COLUMNS_WIDTH_AND_HEIGHT = - hasField(MediaColumns.class, "WIDTH"); - - public static final boolean HAS_REUSING_BITMAP_IN_BITMAP_REGION_DECODER = - Build.VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN; - - public static final boolean HAS_REUSING_BITMAP_IN_BITMAP_FACTORY = - Build.VERSION.SDK_INT >= VERSION_CODES.HONEYCOMB; - - public static final boolean HAS_SET_BEAM_PUSH_URIS = - Build.VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN; - - public static final boolean HAS_SET_DEFALT_BUFFER_SIZE = hasMethod( - "android.graphics.SurfaceTexture", "setDefaultBufferSize", - int.class, int.class); - - public static final boolean HAS_RELEASE_SURFACE_TEXTURE = hasMethod( - "android.graphics.SurfaceTexture", "release"); - - public static final boolean HAS_SURFACE_TEXTURE = - Build.VERSION.SDK_INT >= VERSION_CODES.HONEYCOMB; - - public static final boolean HAS_MTP = - Build.VERSION.SDK_INT >= VERSION_CODES.HONEYCOMB_MR1; - - public static final boolean HAS_AUTO_FOCUS_MOVE_CALLBACK = - Build.VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN; - - public static final boolean HAS_REMOTE_VIEWS_SERVICE = - Build.VERSION.SDK_INT >= VERSION_CODES.HONEYCOMB; - - public static final boolean HAS_INTENT_EXTRA_LOCAL_ONLY = - Build.VERSION.SDK_INT >= VERSION_CODES.HONEYCOMB; - - public static final boolean HAS_SET_SYSTEM_UI_VISIBILITY = - hasMethod(View.class, "setSystemUiVisibility", int.class); - - public static final boolean HAS_FACE_DETECTION; - static { - boolean hasFaceDetection = false; - try { - Class listenerClass = Class.forName( - "android.hardware.Camera$FaceDetectionListener"); - hasFaceDetection = - hasMethod(Camera.class, "setFaceDetectionListener", listenerClass) && - hasMethod(Camera.class, "startFaceDetection") && - hasMethod(Camera.class, "stopFaceDetection") && - hasMethod(Camera.Parameters.class, "getMaxNumDetectedFaces"); - } catch (Throwable t) { - } - HAS_FACE_DETECTION = hasFaceDetection; - } - - public static final boolean HAS_GET_CAMERA_DISABLED = - hasMethod(DevicePolicyManager.class, "getCameraDisabled", ComponentName.class); - - public static final boolean HAS_MEDIA_ACTION_SOUND = - Build.VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN; - - public static final boolean HAS_TIME_LAPSE_RECORDING = - Build.VERSION.SDK_INT >= VERSION_CODES.HONEYCOMB; - - public static final boolean HAS_ZOOM_WHEN_RECORDING = - Build.VERSION.SDK_INT >= VERSION_CODES.ICE_CREAM_SANDWICH; - - public static final boolean HAS_CAMERA_FOCUS_AREA = - Build.VERSION.SDK_INT >= VERSION_CODES.ICE_CREAM_SANDWICH; - - public static final boolean HAS_CAMERA_METERING_AREA = - Build.VERSION.SDK_INT >= VERSION_CODES.ICE_CREAM_SANDWICH; - - public static final boolean HAS_MOTION_EVENT_TRANSFORM = - Build.VERSION.SDK_INT >= VERSION_CODES.HONEYCOMB; - - public static final boolean HAS_EFFECTS_RECORDING = false; - - // "Background" filter does not have "context" input port in jelly bean. - public static final boolean HAS_EFFECTS_RECORDING_CONTEXT_INPUT = - Build.VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN_MR1; - - public static final boolean HAS_GET_SUPPORTED_VIDEO_SIZE = - Build.VERSION.SDK_INT >= VERSION_CODES.HONEYCOMB; - - public static final boolean HAS_SET_ICON_ATTRIBUTE = - Build.VERSION.SDK_INT >= VERSION_CODES.HONEYCOMB; - - public static final boolean HAS_MEDIA_PROVIDER_FILES_TABLE = - Build.VERSION.SDK_INT >= VERSION_CODES.HONEYCOMB; - - public static final boolean HAS_SURFACE_TEXTURE_RECORDING = - Build.VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN; - - public static final boolean HAS_ACTION_BAR = - Build.VERSION.SDK_INT >= VERSION_CODES.HONEYCOMB; - - // Ex: View.setTranslationX. - public static final boolean HAS_VIEW_TRANSFORM_PROPERTIES = - Build.VERSION.SDK_INT >= VERSION_CODES.HONEYCOMB; - - public static final boolean HAS_CAMERA_HDR = - Build.VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN_MR1; - - public static final boolean HAS_OPTIONS_IN_MUTABLE = - Build.VERSION.SDK_INT >= VERSION_CODES.HONEYCOMB; - - public static final boolean CAN_START_PREVIEW_IN_JPEG_CALLBACK = - Build.VERSION.SDK_INT >= VERSION_CODES.ICE_CREAM_SANDWICH; - - public static final boolean HAS_VIEW_PROPERTY_ANIMATOR = - Build.VERSION.SDK_INT >= VERSION_CODES.HONEYCOMB_MR1; - - public static final boolean HAS_POST_ON_ANIMATION = - Build.VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN; - - public static final boolean HAS_ANNOUNCE_FOR_ACCESSIBILITY = - Build.VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN; - - public static final boolean HAS_OBJECT_ANIMATION = - Build.VERSION.SDK_INT >= VERSION_CODES.HONEYCOMB; - - public static final boolean HAS_GLES20_REQUIRED = - Build.VERSION.SDK_INT >= VERSION_CODES.HONEYCOMB; - - public static final boolean HAS_ROTATION_ANIMATION = - hasField(WindowManager.LayoutParams.class, "rotationAnimation"); - - public static final boolean HAS_ORIENTATION_LOCK = - Build.VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN_MR2; - - public static final boolean HAS_CANCELLATION_SIGNAL = - Build.VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN; - - public static final boolean HAS_MEDIA_MUXER = - Build.VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN_MR2; - - public static final boolean HAS_DISPLAY_LISTENER = - Build.VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN_MR1; - - public static int getIntFieldIfExists(Class klass, String fieldName, - Class obj, int defaultVal) { - try { - Field f = klass.getDeclaredField(fieldName); - return f.getInt(obj); - } catch (Exception e) { - return defaultVal; - } - } - - private static boolean hasField(Class klass, String fieldName) { - try { - klass.getDeclaredField(fieldName); - return true; - } catch (NoSuchFieldException e) { - return false; - } - } - - private static boolean hasMethod(String className, String methodName, - Class... parameterTypes) { - try { - Class klass = Class.forName(className); - klass.getDeclaredMethod(methodName, parameterTypes); - return true; - } catch (Throwable th) { - return false; - } - } - - private static boolean hasMethod( - Class klass, String methodName, Class ... paramTypes) { - try { - klass.getDeclaredMethod(methodName, paramTypes); - return true; - } catch (NoSuchMethodException e) { - return false; - } - } -} diff --git a/src/com/android/camera/support/common/Utils.java b/src/com/android/camera/support/common/Utils.java deleted file mode 100644 index f36d6adcd..000000000 --- a/src/com/android/camera/support/common/Utils.java +++ /dev/null @@ -1,325 +0,0 @@ -package com.android.camera.support.common; - - -import android.content.Context; -import android.content.pm.PackageInfo; -import android.content.pm.PackageManager.NameNotFoundException; -import android.database.Cursor; -import android.os.Build; -import android.os.ParcelFileDescriptor; -import android.text.TextUtils; -import android.util.Log; - -import java.io.Closeable; -import java.io.IOException; -import java.io.InterruptedIOException; - -public class Utils { - private static final String TAG = "Utils"; - private static final String DEBUG_TAG = "GalleryDebug"; - - private static final long POLY64REV = 0x95AC9329AC4BC9B5L; - private static final long INITIALCRC = 0xFFFFFFFFFFFFFFFFL; - - private static long[] sCrcTable = new long[256]; - - private static final boolean IS_DEBUG_BUILD = - Build.TYPE.equals("eng") || Build.TYPE.equals("userdebug"); - - private static final String MASK_STRING = "********************************"; - - // Throws AssertionError if the input is false. - public static void assertTrue(boolean cond) { - if (!cond) { - throw new AssertionError(); - } - } - - // Throws AssertionError with the message. We had a method having the form - // assertTrue(boolean cond, String message, Object ... args); - // However a call to that method will cause memory allocation even if the - // condition is false (due to autoboxing generated by "Object ... args"), - // so we don't use that anymore. - public static void fail(String message, Object ... args) { - throw new AssertionError( - args.length == 0 ? message : String.format(message, args)); - } - - // Throws NullPointerException if the input is null. - public static T checkNotNull(T object) { - if (object == null) throw new NullPointerException(); - return object; - } - - // Returns true if two input Object are both null or equal - // to each other. - public static boolean equals(Object a, Object b) { - return (a == b) || (a == null ? false : a.equals(b)); - } - - // Returns the next power of two. - // Returns the input if it is already power of 2. - // Throws IllegalArgumentException if the input is <= 0 or - // the answer overflows. - public static int nextPowerOf2(int n) { - if (n <= 0 || n > (1 << 30)) throw new IllegalArgumentException("n is invalid: " + n); - n -= 1; - n |= n >> 16; - n |= n >> 8; - n |= n >> 4; - n |= n >> 2; - n |= n >> 1; - return n + 1; - } - - // Returns the previous power of two. - // Returns the input if it is already power of 2. - // Throws IllegalArgumentException if the input is <= 0 - public static int prevPowerOf2(int n) { - if (n <= 0) throw new IllegalArgumentException(); - return Integer.highestOneBit(n); - } - - // Returns the input value x clamped to the range [min, max]. - public static int clamp(int x, int min, int max) { - if (x > max) return max; - if (x < min) return min; - return x; - } - - // Returns the input value x clamped to the range [min, max]. - public static float clamp(float x, float min, float max) { - if (x > max) return max; - if (x < min) return min; - return x; - } - - // Returns the input value x clamped to the range [min, max]. - public static long clamp(long x, long min, long max) { - if (x > max) return max; - if (x < min) return min; - return x; - } - - public static boolean isOpaque(int color) { - return color >>> 24 == 0xFF; - } - - public static void swap(int[] array, int i, int j) { - int temp = array[i]; - array[i] = array[j]; - array[j] = temp; - } - - /** - * A function thats returns a 64-bit crc for string - * - * @param in input string - * @return a 64-bit crc value - */ - public static final long crc64Long(String in) { - if (in == null || in.length() == 0) { - return 0; - } - return crc64Long(getBytes(in)); - } - - static { - // http://bioinf.cs.ucl.ac.uk/downloads/crc64/crc64.c - long part; - for (int i = 0; i < 256; i++) { - part = i; - for (int j = 0; j < 8; j++) { - long x = ((int) part & 1) != 0 ? POLY64REV : 0; - part = (part >> 1) ^ x; - } - sCrcTable[i] = part; - } - } - - public static final long crc64Long(byte[] buffer) { - long crc = INITIALCRC; - for (int k = 0, n = buffer.length; k < n; ++k) { - crc = sCrcTable[(((int) crc) ^ buffer[k]) & 0xff] ^ (crc >> 8); - } - return crc; - } - - public static byte[] getBytes(String in) { - byte[] result = new byte[in.length() * 2]; - int output = 0; - for (char ch : in.toCharArray()) { - result[output++] = (byte) (ch & 0xFF); - result[output++] = (byte) (ch >> 8); - } - return result; - } - - public static void closeSilently(Closeable c) { - if (c == null) return; - try { - c.close(); - } catch (IOException t) { - Log.w(TAG, "close fail ", t); - } - } - - public static int compare(long a, long b) { - return a < b ? -1 : a == b ? 0 : 1; - } - - public static int ceilLog2(float value) { - int i; - for (i = 0; i < 31; i++) { - if ((1 << i) >= value) break; - } - return i; - } - - public static int floorLog2(float value) { - int i; - for (i = 0; i < 31; i++) { - if ((1 << i) > value) break; - } - return i - 1; - } - - public static void closeSilently(ParcelFileDescriptor fd) { - try { - if (fd != null) fd.close(); - } catch (Throwable t) { - Log.w(TAG, "fail to close", t); - } - } - - public static void closeSilently(Cursor cursor) { - try { - if (cursor != null) cursor.close(); - } catch (Throwable t) { - Log.w(TAG, "fail to close", t); - } - } - - public static float interpolateAngle( - float source, float target, float progress) { - // interpolate the angle from source to target - // We make the difference in the range of [-179, 180], this is the - // shortest path to change source to target. - float diff = target - source; - if (diff < 0) diff += 360f; - if (diff > 180) diff -= 360f; - - float result = source + diff * progress; - return result < 0 ? result + 360f : result; - } - - public static float interpolateScale( - float source, float target, float progress) { - return source + progress * (target - source); - } - - public static String ensureNotNull(String value) { - return value == null ? "" : value; - } - - public static float parseFloatSafely(String content, float defaultValue) { - if (content == null) return defaultValue; - try { - return Float.parseFloat(content); - } catch (NumberFormatException e) { - return defaultValue; - } - } - - public static int parseIntSafely(String content, int defaultValue) { - if (content == null) return defaultValue; - try { - return Integer.parseInt(content); - } catch (NumberFormatException e) { - return defaultValue; - } - } - - public static boolean isNullOrEmpty(String exifMake) { - return TextUtils.isEmpty(exifMake); - } - - public static void waitWithoutInterrupt(Object object) { - try { - object.wait(); - } catch (InterruptedException e) { - Log.w(TAG, "unexpected interrupt: " + object); - } - } - - public static boolean handleInterrruptedException(Throwable e) { - // A helper to deal with the interrupt exception - // If an interrupt detected, we will setup the bit again. - if (e instanceof InterruptedIOException - || e instanceof InterruptedException) { - Thread.currentThread().interrupt(); - return true; - } - return false; - } - - /** - * @return String with special XML characters escaped. - */ - public static String escapeXml(String s) { - StringBuilder sb = new StringBuilder(); - for (int i = 0, len = s.length(); i < len; ++i) { - char c = s.charAt(i); - switch (c) { - case '<': sb.append("<"); break; - case '>': sb.append(">"); break; - case '\"': sb.append("""); break; - case '\'': sb.append("'"); break; - case '&': sb.append("&"); break; - default: sb.append(c); - } - } - return sb.toString(); - } - - public static String getUserAgent(Context context) { - PackageInfo packageInfo; - try { - packageInfo = context.getPackageManager().getPackageInfo(context.getPackageName(), 0); - } catch (NameNotFoundException e) { - throw new IllegalStateException("getPackageInfo failed"); - } - return String.format("%s/%s; %s/%s/%s/%s; %s/%s/%s", - packageInfo.packageName, - packageInfo.versionName, - Build.BRAND, - Build.DEVICE, - Build.MODEL, - Build.ID, - Build.VERSION.SDK_INT, - Build.VERSION.RELEASE, - Build.VERSION.INCREMENTAL); - } - - public static String[] copyOf(String[] source, int newSize) { - String[] result = new String[newSize]; - newSize = Math.min(source.length, newSize); - System.arraycopy(source, 0, result, 0, newSize); - return result; - } - - // Mask information for debugging only. It returns info.toString() directly - // for debugging build (i.e., 'eng' and 'userdebug') and returns a mask ("****") - // in release build to protect the information (e.g. for privacy issue). - public static String maskDebugInfo(Object info) { - if (info == null) return null; - String s = info.toString(); - int length = Math.min(s.length(), MASK_STRING.length()); - return IS_DEBUG_BUILD ? s : MASK_STRING.substring(0, length); - } - - // This method should be ONLY used for debugging. - public static void debug(String message, Object ... args) { - Log.v(DEBUG_TAG, String.format(message, args)); - } -} \ No newline at end of file diff --git a/src/com/android/camera/support/filtershow/crop/CropExtras.java b/src/com/android/camera/support/filtershow/crop/CropExtras.java deleted file mode 100644 index 6250a3573..000000000 --- a/src/com/android/camera/support/filtershow/crop/CropExtras.java +++ /dev/null @@ -1,106 +0,0 @@ -package com.android.camera.support.filtershow.crop; - - -import android.net.Uri; - -public class CropExtras { - - public static final String KEY_CROPPED_RECT = "cropped-rect"; - public static final String KEY_OUTPUT_X = "outputX"; - public static final String KEY_OUTPUT_Y = "outputY"; - public static final String KEY_SCALE = "scale"; - public static final String KEY_SCALE_UP_IF_NEEDED = "scaleUpIfNeeded"; - public static final String KEY_ASPECT_X = "aspectX"; - public static final String KEY_ASPECT_Y = "aspectY"; - public static final String KEY_SET_AS_WALLPAPER = "set-as-wallpaper"; - public static final String KEY_RETURN_DATA = "return-data"; - public static final String KEY_DATA = "data"; - public static final String KEY_SPOTLIGHT_X = "spotlightX"; - public static final String KEY_SPOTLIGHT_Y = "spotlightY"; - public static final String KEY_SHOW_WHEN_LOCKED = "showWhenLocked"; - public static final String KEY_OUTPUT_FORMAT = "outputFormat"; - - private int mOutputX = 0; - private int mOutputY = 0; - private boolean mScaleUp = true; - private int mAspectX = 0; - private int mAspectY = 0; - private boolean mSetAsWallpaper = false; - private boolean mReturnData = false; - private Uri mExtraOutput = null; - private String mOutputFormat = null; - private boolean mShowWhenLocked = false; - private float mSpotlightX = 0; - private float mSpotlightY = 0; - - public CropExtras(int outputX, int outputY, boolean scaleUp, int aspectX, int aspectY, - boolean setAsWallpaper, boolean returnData, Uri extraOutput, String outputFormat, - boolean showWhenLocked, float spotlightX, float spotlightY) { - mOutputX = outputX; - mOutputY = outputY; - mScaleUp = scaleUp; - mAspectX = aspectX; - mAspectY = aspectY; - mSetAsWallpaper = setAsWallpaper; - mReturnData = returnData; - mExtraOutput = extraOutput; - mOutputFormat = outputFormat; - mShowWhenLocked = showWhenLocked; - mSpotlightX = spotlightX; - mSpotlightY = spotlightY; - } - - public CropExtras(CropExtras c) { - this(c.mOutputX, c.mOutputY, c.mScaleUp, c.mAspectX, c.mAspectY, c.mSetAsWallpaper, - c.mReturnData, c.mExtraOutput, c.mOutputFormat, c.mShowWhenLocked, - c.mSpotlightX, c.mSpotlightY); - } - - public int getOutputX() { - return mOutputX; - } - - public int getOutputY() { - return mOutputY; - } - - public boolean getScaleUp() { - return mScaleUp; - } - - public int getAspectX() { - return mAspectX; - } - - public int getAspectY() { - return mAspectY; - } - - public boolean getSetAsWallpaper() { - return mSetAsWallpaper; - } - - public boolean getReturnData() { - return mReturnData; - } - - public Uri getExtraOutput() { - return mExtraOutput; - } - - public String getOutputFormat() { - return mOutputFormat; - } - - public boolean getShowWhenLocked() { - return mShowWhenLocked; - } - - public float getSpotlightX() { - return mSpotlightX; - } - - public float getSpotlightY() { - return mSpotlightY; - } -} \ No newline at end of file diff --git a/src/com/android/camera/support/glrenderer/BasicTexture.java b/src/com/android/camera/support/glrenderer/BasicTexture.java deleted file mode 100644 index 39cc519c2..000000000 --- a/src/com/android/camera/support/glrenderer/BasicTexture.java +++ /dev/null @@ -1,197 +0,0 @@ -package com.android.camera.support.glrenderer; - - -import java.util.WeakHashMap; - -import com.android.camera.support.common.Utils; - -import android.util.Log; - -// BasicTexture is a Texture corresponds to a real GL texture. -// The state of a BasicTexture indicates whether its data is loaded to GL memory. -// If a BasicTexture is loaded into GL memory, it has a GL texture id. -public abstract class BasicTexture implements Texture { - - @SuppressWarnings("unused") - private static final String TAG = "BasicTexture"; - protected static final int UNSPECIFIED = -1; - - protected static final int STATE_UNLOADED = 0; - protected static final int STATE_LOADED = 1; - protected static final int STATE_ERROR = -1; - - // Log a warning if a texture is larger along a dimension - private static final int MAX_TEXTURE_SIZE = 4096; - - protected int mId = -1; - protected int mState; - - protected int mWidth = UNSPECIFIED; - protected int mHeight = UNSPECIFIED; - - protected int mTextureWidth; - protected int mTextureHeight; - - private boolean mHasBorder; - - protected GLCanvas mCanvasRef = null; - private static WeakHashMap sAllTextures - = new WeakHashMap(); - private static ThreadLocal sInFinalizer = new ThreadLocal(); - - protected BasicTexture(GLCanvas canvas, int id, int state) { - setAssociatedCanvas(canvas); - mId = id; - mState = state; - synchronized (sAllTextures) { - sAllTextures.put(this, null); - } - } - - protected BasicTexture() { - this(null, 0, STATE_UNLOADED); - } - - protected void setAssociatedCanvas(GLCanvas canvas) { - mCanvasRef = canvas; - } - - /** - * Sets the content size of this texture. In OpenGL, the actual texture - * size must be of power of 2, the size of the content may be smaller. - */ - public void setSize(int width, int height) { - mWidth = width; - mHeight = height; - mTextureWidth = width > 0 ? Utils.nextPowerOf2(width) : 0; - mTextureHeight = height > 0 ? Utils.nextPowerOf2(height) : 0; - if (mTextureWidth > MAX_TEXTURE_SIZE || mTextureHeight > MAX_TEXTURE_SIZE) { - Log.w(TAG, String.format("texture is too large: %d x %d", - mTextureWidth, mTextureHeight), new Exception()); - } - } - - public boolean isFlippedVertically() { - return false; - } - - public int getId() { - return mId; - } - - @Override - public int getWidth() { - return mWidth; - } - - @Override - public int getHeight() { - return mHeight; - } - - // Returns the width rounded to the next power of 2. - public int getTextureWidth() { - return mTextureWidth; - } - - // Returns the height rounded to the next power of 2. - public int getTextureHeight() { - return mTextureHeight; - } - - // Returns true if the texture has one pixel transparent border around the - // actual content. This is used to avoid jigged edges. - // - // The jigged edges appear because we use GL_CLAMP_TO_EDGE for texture wrap - // mode (GL_CLAMP is not available in OpenGL ES), so a pixel partially - // covered by the texture will use the color of the edge texel. If we add - // the transparent border, the color of the edge texel will be mixed with - // appropriate amount of transparent. - // - // Currently our background is black, so we can draw the thumbnails without - // enabling blending. - public boolean hasBorder() { - return mHasBorder; - } - - protected void setBorder(boolean hasBorder) { - mHasBorder = hasBorder; - } - - @Override - public void draw(GLCanvas canvas, int x, int y) { - canvas.drawTexture(this, x, y, getWidth(), getHeight()); - } - - @Override - public void draw(GLCanvas canvas, int x, int y, int w, int h) { - canvas.drawTexture(this, x, y, w, h); - } - - // onBind is called before GLCanvas binds this texture. - // It should make sure the data is uploaded to GL memory. - abstract protected boolean onBind(GLCanvas canvas); - - // Returns the GL texture target for this texture (e.g. GL_TEXTURE_2D). - abstract protected int getTarget(); - - public boolean isLoaded() { - return mState == STATE_LOADED; - } - - // recycle() is called when the texture will never be used again, - // so it can free all resources. - public void recycle() { - freeResource(); - } - - // yield() is called when the texture will not be used temporarily, - // so it can free some resources. - // The default implementation unloads the texture from GL memory, so - // the subclass should make sure it can reload the texture to GL memory - // later, or it will have to override this method. - public void yield() { - freeResource(); - } - - private void freeResource() { - GLCanvas canvas = mCanvasRef; - if (canvas != null && mId != -1) { - canvas.unloadTexture(this); - mId = -1; // Don't free it again. - } - mState = STATE_UNLOADED; - setAssociatedCanvas(null); - } - - @Override - protected void finalize() { - sInFinalizer.set(BasicTexture.class); - recycle(); - sInFinalizer.set(null); - } - - // This is for deciding if we can call Bitmap's recycle(). - // We cannot call Bitmap's recycle() in finalizer because at that point - // the finalizer of Bitmap may already be called so recycle() will crash. - public static boolean inFinalizer() { - return sInFinalizer.get() != null; - } - - public static void yieldAllTextures() { - synchronized (sAllTextures) { - for (BasicTexture t : sAllTextures.keySet()) { - t.yield(); - } - } - } - - public static void invalidateAllTextures() { - synchronized (sAllTextures) { - for (BasicTexture t : sAllTextures.keySet()) { - t.mState = STATE_UNLOADED; - t.setAssociatedCanvas(null); - } - } - } -} diff --git a/src/com/android/camera/support/glrenderer/BitmapTexture.java b/src/com/android/camera/support/glrenderer/BitmapTexture.java deleted file mode 100644 index b62a44436..000000000 --- a/src/com/android/camera/support/glrenderer/BitmapTexture.java +++ /dev/null @@ -1,38 +0,0 @@ -package com.android.camera.support.glrenderer; - -import android.graphics.Bitmap; - -import junit.framework.Assert; - -// BitmapTexture is a texture whose content is specified by a fixed Bitmap. -// -// The texture does not own the Bitmap. The user should make sure the Bitmap -// is valid during the texture's lifetime. When the texture is recycled, it -// does not free the Bitmap. -public class BitmapTexture extends UploadedTexture { - protected Bitmap mContentBitmap; - - public BitmapTexture(Bitmap bitmap) { - this(bitmap, false); - } - - public BitmapTexture(Bitmap bitmap, boolean hasBorder) { - super(hasBorder); - Assert.assertTrue(bitmap != null && !bitmap.isRecycled()); - mContentBitmap = bitmap; - } - - @Override - protected void onFreeBitmap(Bitmap bitmap) { - // Do nothing. - } - - @Override - protected Bitmap onGetBitmap() { - return mContentBitmap; - } - - public Bitmap getBitmap() { - return mContentBitmap; - } -} diff --git a/src/com/android/camera/support/glrenderer/ExtTexture.java b/src/com/android/camera/support/glrenderer/ExtTexture.java deleted file mode 100644 index 121e17d5f..000000000 --- a/src/com/android/camera/support/glrenderer/ExtTexture.java +++ /dev/null @@ -1,45 +0,0 @@ -package com.android.camera.support.glrenderer; - - -//ExtTexture is a texture whose content comes from a external texture. -//Before drawing, setSize() should be called. -public class ExtTexture extends BasicTexture { - - private int mTarget; - - public ExtTexture(GLCanvas canvas, int target) { - GLId glId = canvas.getGLId(); - mId = glId.generateTexture(); - mTarget = target; - } - - private void uploadToCanvas(GLCanvas canvas) { - canvas.setTextureParameters(this); - setAssociatedCanvas(canvas); - mState = STATE_LOADED; - } - - @Override - protected boolean onBind(GLCanvas canvas) { - if (!isLoaded()) { - uploadToCanvas(canvas); - } - - return true; - } - - @Override - public int getTarget() { - return mTarget; - } - - @Override - public boolean isOpaque() { - return true; - } - - @Override - public void yield() { - // we cannot free the texture because we have no backup. - } -} diff --git a/src/com/android/camera/support/glrenderer/GLCanvas.java b/src/com/android/camera/support/glrenderer/GLCanvas.java deleted file mode 100644 index 1e4ec2f16..000000000 --- a/src/com/android/camera/support/glrenderer/GLCanvas.java +++ /dev/null @@ -1,201 +0,0 @@ -package com.android.camera.support.glrenderer; - -import android.graphics.Bitmap; -import android.graphics.Rect; -import android.graphics.RectF; - -import javax.microedition.khronos.opengles.GL11; - -// -// GLCanvas gives a convenient interface to draw using OpenGL. -// -// When a rectangle is specified in this interface, it means the region -// [x, x+width) * [y, y+height) -// -public interface GLCanvas { - - public GLId getGLId(); - - // Tells GLCanvas the size of the underlying GL surface. This should be - // called before first drawing and when the size of GL surface is changed. - // This is called by GLRoot and should not be called by the clients - // who only want to draw on the GLCanvas. Both width and height must be - // nonnegative. - public abstract void setSize(int width, int height); - - // Clear the drawing buffers. This should only be used by GLRoot. - public abstract void clearBuffer(); - - public abstract void clearBuffer(float[] argb); - - // Sets and gets the current alpha, alpha must be in [0, 1]. - public abstract void setAlpha(float alpha); - - public abstract float getAlpha(); - - // (current alpha) = (current alpha) * alpha - public abstract void multiplyAlpha(float alpha); - - // Change the current transform matrix. - public abstract void translate(float x, float y, float z); - - public abstract void translate(float x, float y); - - public abstract void scale(float sx, float sy, float sz); - - public abstract void rotate(float angle, float x, float y, float z); - - public abstract void multiplyMatrix(float[] mMatrix, int offset); - - // Pushes the configuration state (matrix, and alpha) onto - // a private stack. - public abstract void save(); - - // Same as save(), but only save those specified in saveFlags. - public abstract void save(int saveFlags); - - public static final int SAVE_FLAG_ALL = 0xFFFFFFFF; - public static final int SAVE_FLAG_ALPHA = 0x01; - public static final int SAVE_FLAG_MATRIX = 0x02; - - // Pops from the top of the stack as current configuration state (matrix, - // alpha, and clip). This call balances a previous call to save(), and is - // used to remove all modifications to the configuration state since the - // last save call. - public abstract void restore(); - - // Draws a line using the specified paint from (x1, y1) to (x2, y2). - // (Both end points are included). - public abstract void drawLine(float x1, float y1, float x2, float y2, GLPaint paint); - - // Draws a rectangle using the specified paint from (x1, y1) to (x2, y2). - // (Both end points are included). - public abstract void drawRect(float x1, float y1, float x2, float y2, GLPaint paint); - - // Fills the specified rectangle with the specified color. - public abstract void fillRect(float x, float y, float width, float height, int color); - - // Draws a texture to the specified rectangle. - public abstract void drawTexture( - BasicTexture texture, int x, int y, int width, int height); - - public abstract void drawMesh(BasicTexture tex, int x, int y, int xyBuffer, - int uvBuffer, int indexBuffer, int indexCount); - - // Draws the source rectangle part of the texture to the target rectangle. - public abstract void drawTexture(BasicTexture texture, RectF source, RectF target); - - // Draw a texture with a specified texture transform. - public abstract void drawTexture(BasicTexture texture, float[] mTextureTransform, - int x, int y, int w, int h); - - // Draw two textures to the specified rectangle. The actual texture used is - // from * (1 - ratio) + to * ratio - // The two textures must have the same size. - public abstract void drawMixed(BasicTexture from, int toColor, - float ratio, int x, int y, int w, int h); - - // Draw a region of a texture and a specified color to the specified - // rectangle. The actual color used is from * (1 - ratio) + to * ratio. - // The region of the texture is defined by parameter "src". The target - // rectangle is specified by parameter "target". - public abstract void drawMixed(BasicTexture from, int toColor, - float ratio, RectF src, RectF target); - - // Unloads the specified texture from the canvas. The resource allocated - // to draw the texture will be released. The specified texture will return - // to the unloaded state. This function should be called only from - // BasicTexture or its descendant - public abstract boolean unloadTexture(BasicTexture texture); - - // Delete the specified buffer object, similar to unloadTexture. - public abstract void deleteBuffer(int bufferId); - - // Delete the textures and buffers in GL side. This function should only be - // called in the GL thread. - public abstract void deleteRecycledResources(); - - // Dump statistics information and clear the counters. For debug only. - public abstract void dumpStatisticsAndClear(); - - public abstract void beginRenderTarget(RawTexture texture); - - public abstract void endRenderTarget(); - - /** - * Sets texture parameters to use GL_CLAMP_TO_EDGE for both - * GL_TEXTURE_WRAP_S and GL_TEXTURE_WRAP_T. Sets texture parameters to be - * GL_LINEAR for GL_TEXTURE_MIN_FILTER and GL_TEXTURE_MAG_FILTER. - * bindTexture() must be called prior to this. - * - * @param texture The texture to set parameters on. - */ - public abstract void setTextureParameters(BasicTexture texture); - - /** - * Initializes the texture to a size by calling texImage2D on it. - * - * @param texture The texture to initialize the size. - * @param format The texture format (e.g. GL_RGBA) - * @param type The texture type (e.g. GL_UNSIGNED_BYTE) - */ - public abstract void initializeTextureSize(BasicTexture texture, int format, int type); - - /** - * Initializes the texture to a size by calling texImage2D on it. - * - * @param texture The texture to initialize the size. - * @param bitmap The bitmap to initialize the bitmap with. - */ - public abstract void initializeTexture(BasicTexture texture, Bitmap bitmap); - - /** - * Calls glTexSubImage2D to upload a bitmap to the texture. - * - * @param texture The target texture to write to. - * @param xOffset Specifies a texel offset in the x direction within the - * texture array. - * @param yOffset Specifies a texel offset in the y direction within the - * texture array. - * @param format The texture format (e.g. GL_RGBA) - * @param type The texture type (e.g. GL_UNSIGNED_BYTE) - */ - public abstract void texSubImage2D(BasicTexture texture, int xOffset, int yOffset, - Bitmap bitmap, - int format, int type); - - /** - * Generates buffers and uploads the buffer data. - * - * @param buffer The buffer to upload - * @return The buffer ID that was generated. - */ - public abstract int uploadBuffer(java.nio.FloatBuffer buffer); - - /** - * Generates buffers and uploads the element array buffer data. - * - * @param buffer The buffer to upload - * @return The buffer ID that was generated. - */ - public abstract int uploadBuffer(java.nio.ByteBuffer buffer); - - /** - * After LightCycle makes GL calls, this method is called to restore the GL - * configuration to the one expected by GLCanvas. - */ - public abstract void recoverFromLightCycle(); - - /** - * Gets the bounds given by x, y, width, and height as well as the internal - * matrix state. There is no special handling for non-90-degree rotations. - * It only considers the lower-left and upper-right corners as the bounds. - * - * @param bounds The output bounds to write to. - * @param x The left side of the input rectangle. - * @param y The bottom of the input rectangle. - * @param width The width of the input rectangle. - * @param height The height of the input rectangle. - */ - public abstract void getBounds(Rect bounds, int x, int y, int width, int height); -} \ No newline at end of file diff --git a/src/com/android/camera/support/glrenderer/GLId.java b/src/com/android/camera/support/glrenderer/GLId.java deleted file mode 100644 index 695343b12..000000000 --- a/src/com/android/camera/support/glrenderer/GLId.java +++ /dev/null @@ -1,17 +0,0 @@ -package com.android.camera.support.glrenderer; - -import javax.microedition.khronos.opengles.GL11; -import javax.microedition.khronos.opengles.GL11ExtensionPack; - -// This mimics corresponding GL functions. -public interface GLId { - public int generateTexture(); - - public void glGenBuffers(int n, int[] buffers, int offset); - - public void glDeleteTextures(GL11 gl, int n, int[] textures, int offset); - - public void glDeleteBuffers(GL11 gl, int n, int[] buffers, int offset); - - public void glDeleteFramebuffers(GL11ExtensionPack gl11ep, int n, int[] buffers, int offset); -} diff --git a/src/com/android/camera/support/glrenderer/GLPaint.java b/src/com/android/camera/support/glrenderer/GLPaint.java deleted file mode 100644 index 7c6af73eb..000000000 --- a/src/com/android/camera/support/glrenderer/GLPaint.java +++ /dev/null @@ -1,26 +0,0 @@ -package com.android.camera.support.glrenderer; - - -import junit.framework.Assert; - -public class GLPaint { - private float mLineWidth = 1f; - private int mColor = 0; - - public void setColor(int color) { - mColor = color; - } - - public int getColor() { - return mColor; - } - - public void setLineWidth(float width) { - Assert.assertTrue(width >= 0); - mLineWidth = width; - } - - public float getLineWidth() { - return mLineWidth; - } -} diff --git a/src/com/android/camera/support/glrenderer/NinePatchChunk.java b/src/com/android/camera/support/glrenderer/NinePatchChunk.java deleted file mode 100644 index f7deb1864..000000000 --- a/src/com/android/camera/support/glrenderer/NinePatchChunk.java +++ /dev/null @@ -1,66 +0,0 @@ -package com.android.camera.support.glrenderer; - -import android.graphics.Rect; - -import java.nio.ByteBuffer; -import java.nio.ByteOrder; - -// See "frameworks/base/include/utils/ResourceTypes.h" for the format of -// NinePatch chunk. -class NinePatchChunk { - - public static final int NO_COLOR = 0x00000001; - public static final int TRANSPARENT_COLOR = 0x00000000; - - public Rect mPaddings = new Rect(); - - public int mDivX[]; - public int mDivY[]; - public int mColor[]; - - private static void readIntArray(int[] data, ByteBuffer buffer) { - for (int i = 0, n = data.length; i < n; ++i) { - data[i] = buffer.getInt(); - } - } - - private static void checkDivCount(int length) { - if (length == 0 || (length & 0x01) != 0) { - throw new RuntimeException("invalid nine-patch: " + length); - } - } - - public static NinePatchChunk deserialize(byte[] data) { - ByteBuffer byteBuffer = - ByteBuffer.wrap(data).order(ByteOrder.nativeOrder()); - - byte wasSerialized = byteBuffer.get(); - if (wasSerialized == 0) return null; - - NinePatchChunk chunk = new NinePatchChunk(); - chunk.mDivX = new int[byteBuffer.get()]; - chunk.mDivY = new int[byteBuffer.get()]; - chunk.mColor = new int[byteBuffer.get()]; - - checkDivCount(chunk.mDivX.length); - checkDivCount(chunk.mDivY.length); - - // skip 8 bytes - byteBuffer.getInt(); - byteBuffer.getInt(); - - chunk.mPaddings.left = byteBuffer.getInt(); - chunk.mPaddings.right = byteBuffer.getInt(); - chunk.mPaddings.top = byteBuffer.getInt(); - chunk.mPaddings.bottom = byteBuffer.getInt(); - - // skip 4 bytes - byteBuffer.getInt(); - - readIntArray(chunk.mDivX, byteBuffer); - readIntArray(chunk.mDivY, byteBuffer); - readIntArray(chunk.mColor, byteBuffer); - - return chunk; - } -} \ No newline at end of file diff --git a/src/com/android/camera/support/glrenderer/NinePatchTexture.java b/src/com/android/camera/support/glrenderer/NinePatchTexture.java deleted file mode 100644 index c69afd682..000000000 --- a/src/com/android/camera/support/glrenderer/NinePatchTexture.java +++ /dev/null @@ -1,408 +0,0 @@ -package com.android.camera.support.glrenderer; - -import java.nio.ByteBuffer; -import java.nio.ByteOrder; -import java.nio.FloatBuffer; - -import android.content.Context; -import android.graphics.Bitmap; -import android.graphics.BitmapFactory; -import android.graphics.Rect; - -import com.android.camera.support.common.Utils; - -// NinePatchTexture is a texture backed by a NinePatch resource. -// -// getPaddings() returns paddings specified in the NinePatch. -// getNinePatchChunk() returns the layout data specified in the NinePatch. -// -public class NinePatchTexture extends ResourceTexture { - @SuppressWarnings("unused") - private static final String TAG = "NinePatchTexture"; - private NinePatchChunk mChunk; - private SmallCache mInstanceCache - = new SmallCache(); - - public NinePatchTexture(Context context, int resId) { - super(context, resId); - } - - @Override - protected Bitmap onGetBitmap() { - if (mBitmap != null) return mBitmap; - - BitmapFactory.Options options = new BitmapFactory.Options(); - options.inPreferredConfig = Bitmap.Config.ARGB_8888; - Bitmap bitmap = BitmapFactory.decodeResource( - mContext.getResources(), mResId, options); - mBitmap = bitmap; - setSize(bitmap.getWidth(), bitmap.getHeight()); - byte[] chunkData = bitmap.getNinePatchChunk(); - mChunk = chunkData == null - ? null - : NinePatchChunk.deserialize(bitmap.getNinePatchChunk()); - if (mChunk == null) { - throw new RuntimeException("invalid nine-patch image: " + mResId); - } - return bitmap; - } - - public Rect getPaddings() { - // get the paddings from nine patch - if (mChunk == null) onGetBitmap(); - return mChunk.mPaddings; - } - - public NinePatchChunk getNinePatchChunk() { - if (mChunk == null) onGetBitmap(); - return mChunk; - } - - // This is a simple cache for a small number of things. Linear search - // is used because the cache is small. It also tries to remove less used - // item when the cache is full by moving the often-used items to the front. - private static class SmallCache { - private static final int CACHE_SIZE = 16; - private static final int CACHE_SIZE_START_MOVE = CACHE_SIZE / 2; - private int[] mKey = new int[CACHE_SIZE]; - private V[] mValue = (V[]) new Object[CACHE_SIZE]; - private int mCount; // number of items in this cache - - // Puts a value into the cache. If the cache is full, also returns - // a less used item, otherwise returns null. - public V put(int key, V value) { - if (mCount == CACHE_SIZE) { - V old = mValue[CACHE_SIZE - 1]; // remove the last item - mKey[CACHE_SIZE - 1] = key; - mValue[CACHE_SIZE - 1] = value; - return old; - } else { - mKey[mCount] = key; - mValue[mCount] = value; - mCount++; - return null; - } - } - - public V get(int key) { - for (int i = 0; i < mCount; i++) { - if (mKey[i] == key) { - // Move the accessed item one position to the front, so it - // will less likely to be removed when cache is full. Only - // do this if the cache is starting to get full. - if (mCount > CACHE_SIZE_START_MOVE && i > 0) { - int tmpKey = mKey[i]; - mKey[i] = mKey[i - 1]; - mKey[i - 1] = tmpKey; - - V tmpValue = mValue[i]; - mValue[i] = mValue[i - 1]; - mValue[i - 1] = tmpValue; - } - return mValue[i]; - } - } - return null; - } - - public void clear() { - for (int i = 0; i < mCount; i++) { - mValue[i] = null; // make sure it's can be garbage-collected. - } - mCount = 0; - } - - public int size() { - return mCount; - } - - public V valueAt(int i) { - return mValue[i]; - } - } - - private NinePatchInstance findInstance(GLCanvas canvas, int w, int h) { - int key = w; - key = (key << 16) | h; - NinePatchInstance instance = mInstanceCache.get(key); - - if (instance == null) { - instance = new NinePatchInstance(this, w, h); - NinePatchInstance removed = mInstanceCache.put(key, instance); - if (removed != null) { - removed.recycle(canvas); - } - } - - return instance; - } - - @Override - public void draw(GLCanvas canvas, int x, int y, int w, int h) { - if (!isLoaded()) { - mInstanceCache.clear(); - } - - if (w != 0 && h != 0) { - findInstance(canvas, w, h).draw(canvas, this, x, y); - } - } - - @Override - public void recycle() { - super.recycle(); - GLCanvas canvas = mCanvasRef; - if (canvas == null) return; - int n = mInstanceCache.size(); - for (int i = 0; i < n; i++) { - NinePatchInstance instance = mInstanceCache.valueAt(i); - instance.recycle(canvas); - } - mInstanceCache.clear(); - } -} - -// This keeps data for a specialization of NinePatchTexture with the size -// (width, height). We pre-compute the coordinates for efficiency. -class NinePatchInstance { - - @SuppressWarnings("unused") - private static final String TAG = "NinePatchInstance"; - - // We need 16 vertices for a normal nine-patch image (the 4x4 vertices) - private static final int VERTEX_BUFFER_SIZE = 16 * 2; - - // We need 22 indices for a normal nine-patch image, plus 2 for each - // transparent region. Current there are at most 1 transparent region. - private static final int INDEX_BUFFER_SIZE = 22 + 2; - - private FloatBuffer mXyBuffer; - private FloatBuffer mUvBuffer; - private ByteBuffer mIndexBuffer; - - // Names for buffer names: xy, uv, index. - private int mXyBufferName = -1; - private int mUvBufferName; - private int mIndexBufferName; - - private int mIdxCount; - - public NinePatchInstance(NinePatchTexture tex, int width, int height) { - NinePatchChunk chunk = tex.getNinePatchChunk(); - - if (width <= 0 || height <= 0) { - throw new RuntimeException("invalid dimension"); - } - - // The code should be easily extended to handle the general cases by - // allocating more space for buffers. But let's just handle the only - // use case. - if (chunk.mDivX.length != 2 || chunk.mDivY.length != 2) { - throw new RuntimeException("unsupported nine patch"); - } - - float divX[] = new float[4]; - float divY[] = new float[4]; - float divU[] = new float[4]; - float divV[] = new float[4]; - - int nx = stretch(divX, divU, chunk.mDivX, tex.getWidth(), width); - int ny = stretch(divY, divV, chunk.mDivY, tex.getHeight(), height); - - prepareVertexData(divX, divY, divU, divV, nx, ny, chunk.mColor); - } - - /** - * Stretches the texture according to the nine-patch rules. It will - * linearly distribute the strechy parts defined in the nine-patch chunk to - * the target area. - * - *
-     *                      source
-     *          /--------------^---------------\
-     *         u0    u1       u2  u3     u4   u5
-     * div ---> |fffff|ssssssss|fff|ssssss|ffff| ---> u
-     *          |    div0    div1 div2   div3  |
-     *          |     |       /   /      /    /
-     *          |     |      /   /     /    /
-     *          |     |     /   /    /    /
-     *          |fffff|ssss|fff|sss|ffff| ---> x
-     *         x0    x1   x2  x3  x4   x5
-     *          \----------v------------/
-     *                  target
-     *
-     * f: fixed segment
-     * s: stretchy segment
-     * 
- * - * @param div the stretch parts defined in nine-patch chunk - * @param source the length of the texture - * @param target the length on the drawing plan - * @param u output, the positions of these dividers in the texture - * coordinate - * @param x output, the corresponding position of these dividers on the - * drawing plan - * @return the number of these dividers. - */ - private static int stretch( - float x[], float u[], int div[], int source, int target) { - int textureSize = Utils.nextPowerOf2(source); - float textureBound = (float) source / textureSize; - - float stretch = 0; - for (int i = 0, n = div.length; i < n; i += 2) { - stretch += div[i + 1] - div[i]; - } - - float remaining = target - source + stretch; - - float lastX = 0; - float lastU = 0; - - x[0] = 0; - u[0] = 0; - for (int i = 0, n = div.length; i < n; i += 2) { - // Make the stretchy segment a little smaller to prevent sampling - // on neighboring fixed segments. - // fixed segment - x[i + 1] = lastX + (div[i] - lastU) + 0.5f; - u[i + 1] = Math.min((div[i] + 0.5f) / textureSize, textureBound); - - // stretchy segment - float partU = div[i + 1] - div[i]; - float partX = remaining * partU / stretch; - remaining -= partX; - stretch -= partU; - - lastX = x[i + 1] + partX; - lastU = div[i + 1]; - x[i + 2] = lastX - 0.5f; - u[i + 2] = Math.min((lastU - 0.5f)/ textureSize, textureBound); - } - // the last fixed segment - x[div.length + 1] = target; - u[div.length + 1] = textureBound; - - // remove segments with length 0. - int last = 0; - for (int i = 1, n = div.length + 2; i < n; ++i) { - if ((x[i] - x[last]) < 1f) continue; - x[++last] = x[i]; - u[last] = u[i]; - } - return last + 1; - } - - private void prepareVertexData(float x[], float y[], float u[], float v[], - int nx, int ny, int[] color) { - /* - * Given a 3x3 nine-patch image, the vertex order is defined as the - * following graph: - * - * (0) (1) (2) (3) - * | /| /| /| - * | / | / | / | - * (4) (5) (6) (7) - * | \ | \ | \ | - * | \| \| \| - * (8) (9) (A) (B) - * | /| /| /| - * | / | / | / | - * (C) (D) (E) (F) - * - * And we draw the triangle strip in the following index order: - * - * index: 04152637B6A5948C9DAEBF - */ - int pntCount = 0; - float xy[] = new float[VERTEX_BUFFER_SIZE]; - float uv[] = new float[VERTEX_BUFFER_SIZE]; - for (int j = 0; j < ny; ++j) { - for (int i = 0; i < nx; ++i) { - int xIndex = (pntCount++) << 1; - int yIndex = xIndex + 1; - xy[xIndex] = x[i]; - xy[yIndex] = y[j]; - uv[xIndex] = u[i]; - uv[yIndex] = v[j]; - } - } - - int idxCount = 1; - boolean isForward = false; - byte index[] = new byte[INDEX_BUFFER_SIZE]; - for (int row = 0; row < ny - 1; row++) { - --idxCount; - isForward = !isForward; - - int start, end, inc; - if (isForward) { - start = 0; - end = nx; - inc = 1; - } else { - start = nx - 1; - end = -1; - inc = -1; - } - - for (int col = start; col != end; col += inc) { - int k = row * nx + col; - if (col != start) { - int colorIdx = row * (nx - 1) + col; - if (isForward) colorIdx--; - if (color[colorIdx] == NinePatchChunk.TRANSPARENT_COLOR) { - index[idxCount] = index[idxCount - 1]; - ++idxCount; - index[idxCount++] = (byte) k; - } - } - - index[idxCount++] = (byte) k; - index[idxCount++] = (byte) (k + nx); - } - } - - mIdxCount = idxCount; - - int size = (pntCount * 2) * (Float.SIZE / Byte.SIZE); - mXyBuffer = allocateDirectNativeOrderBuffer(size).asFloatBuffer(); - mUvBuffer = allocateDirectNativeOrderBuffer(size).asFloatBuffer(); - mIndexBuffer = allocateDirectNativeOrderBuffer(mIdxCount); - - mXyBuffer.put(xy, 0, pntCount * 2).position(0); - mUvBuffer.put(uv, 0, pntCount * 2).position(0); - mIndexBuffer.put(index, 0, idxCount).position(0); - } - - private static ByteBuffer allocateDirectNativeOrderBuffer(int size) { - return ByteBuffer.allocateDirect(size).order(ByteOrder.nativeOrder()); - } - - private void prepareBuffers(GLCanvas canvas) { - mXyBufferName = canvas.uploadBuffer(mXyBuffer); - mUvBufferName = canvas.uploadBuffer(mUvBuffer); - mIndexBufferName = canvas.uploadBuffer(mIndexBuffer); - - // These buffers are never used again. - mXyBuffer = null; - mUvBuffer = null; - mIndexBuffer = null; - } - - public void draw(GLCanvas canvas, NinePatchTexture tex, int x, int y) { - if (mXyBufferName == -1) { - prepareBuffers(canvas); - } - canvas.drawMesh(tex, x, y, mXyBufferName, mUvBufferName, mIndexBufferName, mIdxCount); - } - - public void recycle(GLCanvas canvas) { - if (mXyBuffer == null) { - canvas.deleteBuffer(mXyBufferName); - canvas.deleteBuffer(mUvBufferName); - canvas.deleteBuffer(mIndexBufferName); - mXyBufferName = -1; - } - } -} \ No newline at end of file diff --git a/src/com/android/camera/support/glrenderer/RawTexture.java b/src/com/android/camera/support/glrenderer/RawTexture.java deleted file mode 100644 index b86282d0e..000000000 --- a/src/com/android/camera/support/glrenderer/RawTexture.java +++ /dev/null @@ -1,58 +0,0 @@ -package com.android.camera.support.glrenderer; - - -import android.util.Log; - -import javax.microedition.khronos.opengles.GL11; - -public class RawTexture extends BasicTexture { - private static final String TAG = "RawTexture"; - - private final boolean mOpaque; - private boolean mIsFlipped; - - public RawTexture(int width, int height, boolean opaque) { - mOpaque = opaque; - setSize(width, height); - } - - @Override - public boolean isOpaque() { - return mOpaque; - } - - @Override - public boolean isFlippedVertically() { - return mIsFlipped; - } - - public void setIsFlippedVertically(boolean isFlipped) { - mIsFlipped = isFlipped; - } - - protected void prepare(GLCanvas canvas) { - GLId glId = canvas.getGLId(); - mId = glId.generateTexture(); - canvas.initializeTextureSize(this, GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE); - canvas.setTextureParameters(this); - mState = STATE_LOADED; - setAssociatedCanvas(canvas); - } - - @Override - protected boolean onBind(GLCanvas canvas) { - if (isLoaded()) return true; - Log.w(TAG, "lost the content due to context change"); - return false; - } - - @Override - public void yield() { - // we cannot free the texture because we have no backup. - } - - @Override - protected int getTarget() { - return GL11.GL_TEXTURE_2D; - } -} diff --git a/src/com/android/camera/support/glrenderer/ResourceTexture.java b/src/com/android/camera/support/glrenderer/ResourceTexture.java deleted file mode 100644 index 121c184cd..000000000 --- a/src/com/android/camera/support/glrenderer/ResourceTexture.java +++ /dev/null @@ -1,37 +0,0 @@ -package com.android.camera.support.glrenderer; - -import android.content.Context; -import android.graphics.Bitmap; -import android.graphics.BitmapFactory; - -import junit.framework.Assert; - -// ResourceTexture is a texture whose Bitmap is decoded from a resource. -// By default ResourceTexture is not opaque. -public class ResourceTexture extends UploadedTexture { - - protected final Context mContext; - protected final int mResId; - - public ResourceTexture(Context context, int resId) { - Assert.assertNotNull(context); - mContext = context; - mResId = resId; - setOpaque(false); - } - - @Override - protected Bitmap onGetBitmap() { - BitmapFactory.Options options = new BitmapFactory.Options(); - options.inPreferredConfig = Bitmap.Config.ARGB_8888; - return BitmapFactory.decodeResource( - mContext.getResources(), mResId, options); - } - - @Override - protected void onFreeBitmap(Bitmap bitmap) { - if (!inFinalizer()) { - bitmap.recycle(); - } - } -} diff --git a/src/com/android/camera/support/glrenderer/Texture.java b/src/com/android/camera/support/glrenderer/Texture.java deleted file mode 100644 index 4d1a49d2b..000000000 --- a/src/com/android/camera/support/glrenderer/Texture.java +++ /dev/null @@ -1,27 +0,0 @@ -package com.android.camera.support.glrenderer; - -//Texture is a rectangular image which can be drawn on GLCanvas. -//The isOpaque() function gives a hint about whether the texture is opaque, -//so the drawing can be done faster. -// -//This is the current texture hierarchy: -// -//Texture -//-- ColorTexture -//-- FadeInTexture -//-- BasicTexture -// -- UploadedTexture -// -- BitmapTexture -// -- Tile -// -- ResourceTexture -// -- NinePatchTexture -// -- CanvasTexture -// -- StringTexture -// -public interface Texture { - public int getWidth(); - public int getHeight(); - public void draw(GLCanvas canvas, int x, int y); - public void draw(GLCanvas canvas, int x, int y, int w, int h); - public boolean isOpaque(); -} diff --git a/src/com/android/camera/support/glrenderer/UploadedTexture.java b/src/com/android/camera/support/glrenderer/UploadedTexture.java deleted file mode 100644 index 106aa4ef3..000000000 --- a/src/com/android/camera/support/glrenderer/UploadedTexture.java +++ /dev/null @@ -1,283 +0,0 @@ -package com.android.camera.support.glrenderer; - - -import android.graphics.Bitmap; -import android.graphics.Bitmap.Config; -import android.opengl.GLUtils; - -import junit.framework.Assert; - -import java.util.HashMap; - -import javax.microedition.khronos.opengles.GL11; - -// UploadedTextures use a Bitmap for the content of the texture. -// -// Subclasses should implement onGetBitmap() to provide the Bitmap and -// implement onFreeBitmap(mBitmap) which will be called when the Bitmap -// is not needed anymore. -// -// isContentValid() is meaningful only when the isLoaded() returns true. -// It means whether the content needs to be updated. -// -// The user of this class should call recycle() when the texture is not -// needed anymore. -// -// By default an UploadedTexture is opaque (so it can be drawn faster without -// blending). The user or subclass can override it using setOpaque(). -public abstract class UploadedTexture extends BasicTexture { - - // To prevent keeping allocation the borders, we store those used borders here. - // Since the length will be power of two, it won't use too much memory. - private static HashMap sBorderLines = - new HashMap(); - private static BorderKey sBorderKey = new BorderKey(); - - @SuppressWarnings("unused") - private static final String TAG = "Texture"; - private boolean mContentValid = true; - - // indicate this textures is being uploaded in background - private boolean mIsUploading = false; - private boolean mOpaque = true; - private boolean mThrottled = false; - private static int sUploadedCount; - private static final int UPLOAD_LIMIT = 100; - - protected Bitmap mBitmap; - private int mBorder; - - protected UploadedTexture() { - this(false); - } - - protected UploadedTexture(boolean hasBorder) { - super(null, 0, STATE_UNLOADED); - if (hasBorder) { - setBorder(true); - mBorder = 1; - } - } - - protected void setIsUploading(boolean uploading) { - mIsUploading = uploading; - } - - public boolean isUploading() { - return mIsUploading; - } - - private static class BorderKey implements Cloneable { - public boolean vertical; - public Config config; - public int length; - - @Override - public int hashCode() { - int x = config.hashCode() ^ length; - return vertical ? x : -x; - } - - @Override - public boolean equals(Object object) { - if (!(object instanceof BorderKey)) return false; - BorderKey o = (BorderKey) object; - return vertical == o.vertical - && config == o.config && length == o.length; - } - - @Override - public BorderKey clone() { - try { - return (BorderKey) super.clone(); - } catch (CloneNotSupportedException e) { - throw new AssertionError(e); - } - } - } - - protected void setThrottled(boolean throttled) { - mThrottled = throttled; - } - - private static Bitmap getBorderLine( - boolean vertical, Config config, int length) { - BorderKey key = sBorderKey; - key.vertical = vertical; - key.config = config; - key.length = length; - Bitmap bitmap = sBorderLines.get(key); - if (bitmap == null) { - bitmap = vertical - ? Bitmap.createBitmap(1, length, config) - : Bitmap.createBitmap(length, 1, config); - sBorderLines.put(key.clone(), bitmap); - } - return bitmap; - } - - private Bitmap getBitmap() { - if (mBitmap == null) { - mBitmap = onGetBitmap(); - int w = mBitmap.getWidth() + mBorder * 2; - int h = mBitmap.getHeight() + mBorder * 2; - if (mWidth == UNSPECIFIED) { - setSize(w, h); - } - } - return mBitmap; - } - - private void freeBitmap() { - Assert.assertTrue(mBitmap != null); - onFreeBitmap(mBitmap); - mBitmap = null; - } - - @Override - public int getWidth() { - if (mWidth == UNSPECIFIED) getBitmap(); - return mWidth; - } - - @Override - public int getHeight() { - if (mWidth == UNSPECIFIED) getBitmap(); - return mHeight; - } - - protected abstract Bitmap onGetBitmap(); - - protected abstract void onFreeBitmap(Bitmap bitmap); - - protected void invalidateContent() { - if (mBitmap != null) freeBitmap(); - mContentValid = false; - mWidth = UNSPECIFIED; - mHeight = UNSPECIFIED; - } - - /** - * Whether the content on GPU is valid. - */ - public boolean isContentValid() { - return isLoaded() && mContentValid; - } - - /** - * Updates the content on GPU's memory. - * @param canvas - */ - public void updateContent(GLCanvas canvas) { - if (!isLoaded()) { - if (mThrottled && ++sUploadedCount > UPLOAD_LIMIT) { - return; - } - uploadToCanvas(canvas); - } else if (!mContentValid) { - Bitmap bitmap = getBitmap(); - int format = GLUtils.getInternalFormat(bitmap); - int type = GLUtils.getType(bitmap); - canvas.texSubImage2D(this, mBorder, mBorder, bitmap, format, type); - freeBitmap(); - mContentValid = true; - } - } - - public static void resetUploadLimit() { - sUploadedCount = 0; - } - - public static boolean uploadLimitReached() { - return sUploadedCount > UPLOAD_LIMIT; - } - - private void uploadToCanvas(GLCanvas canvas) { - - Bitmap bitmap = getBitmap(); - if (bitmap != null) { - try { - int bWidth = bitmap.getWidth(); - int bHeight = bitmap.getHeight(); - int width = bWidth + mBorder * 2; - int height = bHeight + mBorder * 2; - int texWidth = getTextureWidth(); - int texHeight = getTextureHeight(); - - Assert.assertTrue(bWidth <= texWidth && bHeight <= texHeight); - - // Upload the bitmap to a new texture. - mId = canvas.getGLId().generateTexture(); - canvas.setTextureParameters(this); - - if (bWidth == texWidth && bHeight == texHeight) { - canvas.initializeTexture(this, bitmap); - } else { - int format = GLUtils.getInternalFormat(bitmap); - int type = GLUtils.getType(bitmap); - Config config = bitmap.getConfig(); - - canvas.initializeTextureSize(this, format, type); - canvas.texSubImage2D(this, mBorder, mBorder, bitmap, format, type); - - if (mBorder > 0) { - // Left border - Bitmap line = getBorderLine(true, config, texHeight); - canvas.texSubImage2D(this, 0, 0, line, format, type); - - // Top border - line = getBorderLine(false, config, texWidth); - canvas.texSubImage2D(this, 0, 0, line, format, type); - } - - // Right border - if (mBorder + bWidth < texWidth) { - Bitmap line = getBorderLine(true, config, texHeight); - canvas.texSubImage2D(this, mBorder + bWidth, 0, line, format, type); - } - - // Bottom border - if (mBorder + bHeight < texHeight) { - Bitmap line = getBorderLine(false, config, texWidth); - canvas.texSubImage2D(this, 0, mBorder + bHeight, line, format, type); - } - } - } finally { - freeBitmap(); - } - // Update texture state. - setAssociatedCanvas(canvas); - mState = STATE_LOADED; - mContentValid = true; - } else { - mState = STATE_ERROR; - throw new RuntimeException("Texture load fail, no bitmap"); - } - } - - @Override - protected boolean onBind(GLCanvas canvas) { - updateContent(canvas); - return isContentValid(); - } - - @Override - protected int getTarget() { - return GL11.GL_TEXTURE_2D; - } - - public void setOpaque(boolean isOpaque) { - mOpaque = isOpaque; - } - - @Override - public boolean isOpaque() { - return mOpaque; - } - - @Override - public void recycle() { - super.recycle(); - if (mBitmap != null) freeBitmap(); - } -} \ No newline at end of file diff --git a/src/com/android/camera/support/ui/BitmapScreenNail.java b/src/com/android/camera/support/ui/BitmapScreenNail.java deleted file mode 100644 index a0252b9a5..000000000 --- a/src/com/android/camera/support/ui/BitmapScreenNail.java +++ /dev/null @@ -1,45 +0,0 @@ -package com.android.camera.support.ui; - -import android.graphics.Bitmap; -import android.graphics.RectF; - -import com.android.camera.support.glrenderer.BitmapTexture; -import com.android.camera.support.glrenderer.GLCanvas; - -public class BitmapScreenNail implements ScreenNail { - private final BitmapTexture mBitmapTexture; - - public BitmapScreenNail(Bitmap bitmap) { - mBitmapTexture = new BitmapTexture(bitmap); - } - - @Override - public int getWidth() { - return mBitmapTexture.getWidth(); - } - - @Override - public int getHeight() { - return mBitmapTexture.getHeight(); - } - - @Override - public void draw(GLCanvas canvas, int x, int y, int width, int height) { - mBitmapTexture.draw(canvas, x, y, width, height); - } - - @Override - public void noDraw() { - // do nothing - } - - @Override - public void recycle() { - mBitmapTexture.recycle(); - } - - @Override - public void draw(GLCanvas canvas, RectF source, RectF dest) { - canvas.drawTexture(mBitmapTexture, source, dest); - } -} diff --git a/src/com/android/camera/support/ui/OrientationSource.java b/src/com/android/camera/support/ui/OrientationSource.java deleted file mode 100644 index a2841eec4..000000000 --- a/src/com/android/camera/support/ui/OrientationSource.java +++ /dev/null @@ -1,6 +0,0 @@ -package com.android.camera.support.ui; - -public interface OrientationSource { - public int getDisplayRotation(); - public int getCompensation(); -} diff --git a/src/com/android/camera/support/ui/ScreenNail.java b/src/com/android/camera/support/ui/ScreenNail.java deleted file mode 100644 index 67b2f58c5..000000000 --- a/src/com/android/camera/support/ui/ScreenNail.java +++ /dev/null @@ -1,21 +0,0 @@ -package com.android.camera.support.ui; - - -import android.graphics.RectF; - -import com.android.camera.support.glrenderer.GLCanvas; - -public interface ScreenNail { - public int getWidth(); - public int getHeight(); - public void draw(GLCanvas canvas, int x, int y, int width, int height); - - // We do not need to draw this ScreenNail in this frame. - public void noDraw(); - - // This ScreenNail will not be used anymore. Release related resources. - public void recycle(); - - // This is only used by TileImageView to back up the tiles not yet loaded. - public void draw(GLCanvas canvas, RectF source, RectF dest); -} diff --git a/src/com/android/camera/support/ui/SurfaceTextureScreenNail.java b/src/com/android/camera/support/ui/SurfaceTextureScreenNail.java deleted file mode 100644 index d78b1ba4a..000000000 --- a/src/com/android/camera/support/ui/SurfaceTextureScreenNail.java +++ /dev/null @@ -1,128 +0,0 @@ -package com.android.camera.support.ui; - - -import android.annotation.TargetApi; -import android.graphics.RectF; -import android.graphics.SurfaceTexture; - -import com.android.camera.support.common.ApiHelper; -import com.android.camera.support.glrenderer.ExtTexture; -import com.android.camera.support.glrenderer.GLCanvas; - - -@TargetApi(ApiHelper.VERSION_CODES.HONEYCOMB) -public abstract class SurfaceTextureScreenNail implements ScreenNail, - SurfaceTexture.OnFrameAvailableListener { - @SuppressWarnings("unused") - private static final String TAG = "SurfaceTextureScreenNail"; - // This constant is not available in API level before 15, but it was just an - // oversight. - private static final int GL_TEXTURE_EXTERNAL_OES = 0x8D65; - - protected ExtTexture mExtTexture; - private SurfaceTexture mSurfaceTexture; - private int mWidth, mHeight; - private float[] mTransform = new float[16]; - private boolean mHasTexture = false; - - public SurfaceTextureScreenNail() { - } - - public void acquireSurfaceTexture(GLCanvas canvas) { - mExtTexture = new ExtTexture(canvas, GL_TEXTURE_EXTERNAL_OES); - mExtTexture.setSize(mWidth, mHeight); - mSurfaceTexture = new SurfaceTexture(mExtTexture.getId()); - setDefaultBufferSize(mSurfaceTexture, mWidth, mHeight); - mSurfaceTexture.setOnFrameAvailableListener(this); - synchronized (this) { - mHasTexture = true; - } - } - - @TargetApi(ApiHelper.VERSION_CODES.ICE_CREAM_SANDWICH_MR1) - private static void setDefaultBufferSize(SurfaceTexture st, int width, int height) { - if (ApiHelper.HAS_SET_DEFALT_BUFFER_SIZE) { - st.setDefaultBufferSize(width, height); - } - } - - @TargetApi(ApiHelper.VERSION_CODES.ICE_CREAM_SANDWICH) - private static void releaseSurfaceTexture(SurfaceTexture st) { - st.setOnFrameAvailableListener(null); - if (ApiHelper.HAS_RELEASE_SURFACE_TEXTURE) { - st.release(); - } - } - - public SurfaceTexture getSurfaceTexture() { - return mSurfaceTexture; - } - - public void releaseSurfaceTexture() { - synchronized (this) { - mHasTexture = false; - } - mExtTexture.recycle(); - mExtTexture = null; - releaseSurfaceTexture(mSurfaceTexture); - mSurfaceTexture = null; - } - - public void setSize(int width, int height) { - mWidth = width; - mHeight = height; - } - - public void resizeTexture() { - if (mExtTexture != null) { - mExtTexture.setSize(mWidth, mHeight); - setDefaultBufferSize(mSurfaceTexture, mWidth, mHeight); - } - } - - @Override - public int getWidth() { - return mWidth; - } - - @Override - public int getHeight() { - return mHeight; - } - - @Override - public void draw(GLCanvas canvas, int x, int y, int width, int height) { - synchronized (this) { - if (!mHasTexture) return; - mSurfaceTexture.updateTexImage(); - mSurfaceTexture.getTransformMatrix(mTransform); - - // Flip vertically. - canvas.save(GLCanvas.SAVE_FLAG_MATRIX); - int cx = x + width / 2; - int cy = y + height / 2; - canvas.translate(cx, cy); - canvas.scale(1, -1, 1); - canvas.translate(-cx, -cy); - updateTransformMatrix(mTransform); - canvas.drawTexture(mExtTexture, mTransform, x, y, width, height); - canvas.restore(); - } - } - - @Override - public void draw(GLCanvas canvas, RectF source, RectF dest) { - throw new UnsupportedOperationException(); - } - - protected void updateTransformMatrix(float[] matrix) {} - - @Override - abstract public void noDraw(); - - @Override - abstract public void recycle(); - - @Override - abstract public void onFrameAvailable(SurfaceTexture surfaceTexture); -} \ No newline at end of file diff --git a/src/com/android/camera/support/util/AccessibilityUtils.java b/src/com/android/camera/support/util/AccessibilityUtils.java deleted file mode 100644 index e758e2f7e..000000000 --- a/src/com/android/camera/support/util/AccessibilityUtils.java +++ /dev/null @@ -1,38 +0,0 @@ -package com.android.camera.support.util; - -import android.content.Context; -import android.support.v4.view.accessibility.AccessibilityRecordCompat; -import android.view.View; -import android.view.accessibility.AccessibilityEvent; -import android.view.accessibility.AccessibilityManager; - -import com.android.camera.support.common.ApiHelper; - -/** - * AccessibilityUtils provides functions needed in accessibility mode. All the functions - * in this class are made compatible with gingerbread and later API's -*/ -public class AccessibilityUtils { - public static void makeAnnouncement(View view, CharSequence announcement) { - if (view == null) - return; - if (ApiHelper.HAS_ANNOUNCE_FOR_ACCESSIBILITY) { - view.announceForAccessibility(announcement); - } else { - // For API 15 and earlier, we need to construct an accessibility event - Context ctx = view.getContext(); - AccessibilityManager am = (AccessibilityManager) ctx.getSystemService( - Context.ACCESSIBILITY_SERVICE); - if (!am.isEnabled()) return; - AccessibilityEvent event = AccessibilityEvent.obtain( - AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED); - AccessibilityRecordCompat arc = new AccessibilityRecordCompat(event); - arc.setSource(view); - event.setClassName(view.getClass().getName()); - event.setPackageName(view.getContext().getPackageName()); - event.setEnabled(view.isEnabled()); - event.getText().add(announcement); - am.sendAccessibilityEvent(event); - } - } -} \ No newline at end of file diff --git a/src/com/android/camera/support/util/MotionEventHelper.java b/src/com/android/camera/support/util/MotionEventHelper.java deleted file mode 100644 index 2a3340e28..000000000 --- a/src/com/android/camera/support/util/MotionEventHelper.java +++ /dev/null @@ -1,105 +0,0 @@ -package com.android.camera.support.util; - -import android.annotation.TargetApi; -import android.graphics.Matrix; -import android.util.FloatMath; -import android.view.MotionEvent; -import android.view.MotionEvent.PointerCoords; - -import com.android.gallery3d.common.ApiHelper; - -public final class MotionEventHelper { - private MotionEventHelper() {} - - public static MotionEvent transformEvent(MotionEvent e, Matrix m) { - // We try to use the new transform method if possible because it uses - // less memory. - if (ApiHelper.HAS_MOTION_EVENT_TRANSFORM) { - return transformEventNew(e, m); - } else { - return transformEventOld(e, m); - } - } - - @TargetApi(ApiHelper.VERSION_CODES.HONEYCOMB) - private static MotionEvent transformEventNew(MotionEvent e, Matrix m) { - MotionEvent newEvent = MotionEvent.obtain(e); - newEvent.transform(m); - return newEvent; - } - - // This is copied from Input.cpp in the android framework. - private static MotionEvent transformEventOld(MotionEvent e, Matrix m) { - long downTime = e.getDownTime(); - long eventTime = e.getEventTime(); - int action = e.getAction(); - int pointerCount = e.getPointerCount(); - int[] pointerIds = getPointerIds(e); - PointerCoords[] pointerCoords = getPointerCoords(e); - int metaState = e.getMetaState(); - float xPrecision = e.getXPrecision(); - float yPrecision = e.getYPrecision(); - int deviceId = e.getDeviceId(); - int edgeFlags = e.getEdgeFlags(); - int source = e.getSource(); - int flags = e.getFlags(); - - // Copy the x and y coordinates into an array, map them, and copy back. - float[] xy = new float[pointerCoords.length * 2]; - for (int i = 0; i < pointerCount;i++) { - xy[2 * i] = pointerCoords[i].x; - xy[2 * i + 1] = pointerCoords[i].y; - } - m.mapPoints(xy); - for (int i = 0; i < pointerCount;i++) { - pointerCoords[i].x = xy[2 * i]; - pointerCoords[i].y = xy[2 * i + 1]; - pointerCoords[i].orientation = transformAngle( - m, pointerCoords[i].orientation); - } - - MotionEvent n = MotionEvent.obtain(downTime, eventTime, action, - pointerCount, pointerIds, pointerCoords, metaState, xPrecision, - yPrecision, deviceId, edgeFlags, source, flags); - - return n; - } - - private static int[] getPointerIds(MotionEvent e) { - int n = e.getPointerCount(); - int[] r = new int[n]; - for (int i = 0; i < n; i++) { - r[i] = e.getPointerId(i); - } - return r; - } - - private static PointerCoords[] getPointerCoords(MotionEvent e) { - int n = e.getPointerCount(); - PointerCoords[] r = new PointerCoords[n]; - for (int i = 0; i < n; i++) { - r[i] = new PointerCoords(); - e.getPointerCoords(i, r[i]); - } - return r; - } - - private static float transformAngle(Matrix m, float angleRadians) { - // Construct and transform a vector oriented at the specified clockwise - // angle from vertical. Coordinate system: down is increasing Y, right is - // increasing X. - float[] v = new float[2]; - v[0] = FloatMath.sin(angleRadians); - v[1] = -FloatMath.cos(angleRadians); - m.mapVectors(v); - - // Derive the transformed vector's clockwise angle from vertical. - float result = (float) Math.atan2(v[0], -v[1]); - if (result < -Math.PI / 2) { - result += Math.PI; - } else if (result > Math.PI / 2) { - result -= Math.PI; - } - return result; - } -} diff --git a/src/com/android/camera/ui/CameraRootView.java b/src/com/android/camera/ui/CameraRootView.java index 5186ea24c..48f24e4f2 100644 --- a/src/com/android/camera/ui/CameraRootView.java +++ b/src/com/android/camera/ui/CameraRootView.java @@ -27,8 +27,8 @@ import android.util.AttributeSet; import android.view.View; import android.widget.FrameLayout; -import com.android.camera.Util; -import com.android.camera.support.common.ApiHelper; +import com.android.camera.util.CameraUtil; +import com.android.camera.util.ApiHelper; @SuppressLint("NewApi") public class CameraRootView extends FrameLayout { @@ -110,7 +110,7 @@ public class CameraRootView extends FrameLayout { @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { - int rotation = Util.getDisplayRotation((Activity) getContext()); + int rotation = CameraUtil.getDisplayRotation((Activity) getContext()); // all the layout code assumes camera device orientation to be portrait // adjust rotation for landscape int orientation = getResources().getConfiguration().orientation; diff --git a/src/com/android/camera/ui/CameraSwitcher.java b/src/com/android/camera/ui/CameraSwitcher.java index 4a05622bc..eee68a58c 100644 --- a/src/com/android/camera/ui/CameraSwitcher.java +++ b/src/com/android/camera/ui/CameraSwitcher.java @@ -34,12 +34,12 @@ import android.view.ViewGroup; import android.widget.FrameLayout.LayoutParams; import android.widget.LinearLayout; -import com.android.camera.Util; +import com.android.camera.util.CameraUtil; import com.android.camera.util.PhotoSphereHelper; import com.android.camera.util.RefocusHelper; import com.android.camera.util.UsageStatistics; import com.android.camera2.R; -import com.android.gallery3d.common.ApiHelper; +import com.android.camera.util.ApiHelper; public class CameraSwitcher extends RotateImageView implements OnClickListener, OnTouchListener { @@ -292,7 +292,7 @@ public class CameraSwitcher extends RotateImageView } private void layoutPopup() { - int orientation = Util.getDisplayRotation((Activity) getContext()); + int orientation = CameraUtil.getDisplayRotation((Activity) getContext()); int w = mPopup.getMeasuredWidth(); int h = mPopup.getMeasuredHeight(); if (orientation == 0) { diff --git a/src/com/android/camera/ui/EffectSettingPopup.java b/src/com/android/camera/ui/EffectSettingPopup.java index d51803a83..1ee278d21 100644 --- a/src/com/android/camera/ui/EffectSettingPopup.java +++ b/src/com/android/camera/ui/EffectSettingPopup.java @@ -30,7 +30,7 @@ import android.widget.SimpleAdapter; import com.android.camera.IconListPreference; import com.android.camera2.R; -import com.android.gallery3d.common.ApiHelper; +import com.android.camera.util.ApiHelper; // A popup window that shows video effect setting. It has two grid view. // One shows the goofy face effects. The other shows the background replacer diff --git a/src/com/android/camera/ui/FaceView.java b/src/com/android/camera/ui/FaceView.java index 1a539665e..7ec9b7e54 100644 --- a/src/com/android/camera/ui/FaceView.java +++ b/src/com/android/camera/ui/FaceView.java @@ -31,10 +31,10 @@ import android.util.AttributeSet; import android.util.Log; import android.view.View; +import com.android.camera.util.CameraUtil; import com.android.camera.PhotoUI; -import com.android.camera.Util; import com.android.camera2.R; -import com.android.gallery3d.common.ApiHelper; +import com.android.camera.util.ApiHelper; @TargetApi(ApiHelper.VERSION_CODES.ICE_CREAM_SANDWICH) public class FaceView extends View @@ -197,7 +197,7 @@ public class FaceView extends View rw = rh; rh = temp; } - Util.prepareMatrix(mMatrix, mMirror, mDisplayOrientation, rw, rh); + CameraUtil.prepareMatrix(mMatrix, mMirror, mDisplayOrientation, rw, rh); int dx = (getWidth() - rw) / 2; int dy = (getHeight() - rh) / 2; @@ -212,9 +212,9 @@ public class FaceView extends View // Transform the coordinates. mRect.set(mFaces[i].rect); - if (LOGV) Util.dumpRect(mRect, "Original rect"); + if (LOGV) CameraUtil.dumpRect(mRect, "Original rect"); mMatrix.mapRect(mRect); - if (LOGV) Util.dumpRect(mRect, "Transformed rect"); + if (LOGV) CameraUtil.dumpRect(mRect, "Transformed rect"); mPaint.setColor(mColor); mRect.offset(dx, dy); canvas.drawOval(mRect, mPaint); diff --git a/src/com/android/camera/ui/PreviewSurfaceView.java b/src/com/android/camera/ui/PreviewSurfaceView.java deleted file mode 100644 index 9a428e23c..000000000 --- a/src/com/android/camera/ui/PreviewSurfaceView.java +++ /dev/null @@ -1,50 +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.camera.ui; - -import android.content.Context; -import android.util.AttributeSet; -import android.view.SurfaceHolder; -import android.view.SurfaceView; -import android.view.ViewGroup; - -import com.android.gallery3d.common.ApiHelper; - -public class PreviewSurfaceView extends SurfaceView { - public PreviewSurfaceView(Context context, AttributeSet attrs) { - super(context, attrs); - setZOrderMediaOverlay(true); - getHolder().setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS); - } - - public void shrink() { - setLayoutSize(1); - } - - public void expand() { - setLayoutSize(ViewGroup.LayoutParams.MATCH_PARENT); - } - - private void setLayoutSize(int size) { - ViewGroup.LayoutParams p = getLayoutParams(); - if (p.width != size || p.height != size) { - p.width = size; - p.height = size; - setLayoutParams(p); - } - } -} diff --git a/src/com/android/camera/ui/RotatableLayout.java b/src/com/android/camera/ui/RotatableLayout.java index 965d62a90..e9ee0f358 100644 --- a/src/com/android/camera/ui/RotatableLayout.java +++ b/src/com/android/camera/ui/RotatableLayout.java @@ -25,7 +25,7 @@ import android.view.View; import android.view.ViewGroup; import android.widget.FrameLayout; -import com.android.camera.Util; +import com.android.camera.util.CameraUtil; /* RotatableLayout rotates itself as well as all its children when orientation * changes. Specifically, when going from portrait to landscape, camera @@ -62,7 +62,7 @@ public class RotatableLayout extends FrameLayout { @Override public void onAttachedToWindow() { - mPrevRotation = Util.getDisplayRotation((Activity) getContext()); + mPrevRotation = CameraUtil.getDisplayRotation((Activity) getContext()); // check if there is any rotation before the view is attached to window int currentOrientation = getResources().getConfiguration().orientation; int orientation = getUnifiedRotation(); @@ -89,7 +89,7 @@ public class RotatableLayout extends FrameLayout { // all the layout code assumes camera device orientation to be portrait // adjust rotation for landscape int orientation = getResources().getConfiguration().orientation; - int rotation = Util.getDisplayRotation((Activity) getContext()); + int rotation = CameraUtil.getDisplayRotation((Activity) getContext()); int camOrientation = (rotation % 180 == 0) ? Configuration.ORIENTATION_PORTRAIT : Configuration.ORIENTATION_LANDSCAPE; if (camOrientation != orientation) { @@ -99,7 +99,7 @@ public class RotatableLayout extends FrameLayout { } public void checkLayoutFlip() { - int currentRotation = Util.getDisplayRotation((Activity) getContext()); + int currentRotation = CameraUtil.getDisplayRotation((Activity) getContext()); if ((currentRotation - mPrevRotation + 360) % 360 == 180) { mPrevRotation = currentRotation; flipChildren(); @@ -118,7 +118,7 @@ public class RotatableLayout extends FrameLayout { @Override public void onConfigurationChanged(Configuration config) { super.onConfigurationChanged(config); - int rotation = Util.getDisplayRotation((Activity) getContext()); + int rotation = CameraUtil.getDisplayRotation((Activity) getContext()); int diff = (rotation - mPrevRotation + 360) % 360; if ( diff == 0) { // No rotation diff --git a/src/com/android/camera/ui/RotateLayout.java b/src/com/android/camera/ui/RotateLayout.java index 5d6debb17..044da1cee 100644 --- a/src/com/android/camera/ui/RotateLayout.java +++ b/src/com/android/camera/ui/RotateLayout.java @@ -27,8 +27,8 @@ import android.view.View; import android.view.ViewGroup; import android.view.ViewParent; -import com.android.camera.support.util.MotionEventHelper; -import com.android.gallery3d.common.ApiHelper; +import com.android.camera.util.MotionEventHelper; +import com.android.camera.util.ApiHelper; // A RotateLayout is designed to display a single item and provides the // capabilities to rotate the item. diff --git a/src/com/android/camera/ui/RotateTextToast.java b/src/com/android/camera/ui/RotateTextToast.java index 16382442e..ea1268218 100644 --- a/src/com/android/camera/ui/RotateTextToast.java +++ b/src/com/android/camera/ui/RotateTextToast.java @@ -23,7 +23,7 @@ import android.view.View; import android.view.ViewGroup; import android.widget.TextView; -import com.android.camera.Util; +import com.android.camera.util.CameraUtil; import com.android.camera2.R; public class RotateTextToast { @@ -46,7 +46,7 @@ public class RotateTextToast { private final Runnable mRunnable = new Runnable() { @Override public void run() { - Util.fadeOut(mToast); + CameraUtil.fadeOut(mToast); mLayoutRoot.removeView(mToast); mToast = null; } diff --git a/src/com/android/camera/ui/Switch.java b/src/com/android/camera/ui/Switch.java index 9191242f6..4518dedf4 100644 --- a/src/com/android/camera/ui/Switch.java +++ b/src/com/android/camera/ui/Switch.java @@ -39,7 +39,7 @@ import android.view.accessibility.AccessibilityNodeInfo; import android.widget.CompoundButton; import com.android.camera2.R; -import com.android.gallery3d.common.ApiHelper; +import com.android.camera.util.ApiHelper; /** * A Switch is a two-state toggle switch widget that can select between two diff --git a/src/com/android/camera/util/AccessibilityUtils.java b/src/com/android/camera/util/AccessibilityUtils.java new file mode 100644 index 000000000..543c7ff4d --- /dev/null +++ b/src/com/android/camera/util/AccessibilityUtils.java @@ -0,0 +1,36 @@ +package com.android.camera.util; + +import android.content.Context; +import android.support.v4.view.accessibility.AccessibilityRecordCompat; +import android.view.View; +import android.view.accessibility.AccessibilityEvent; +import android.view.accessibility.AccessibilityManager; + +/** + * AccessibilityUtils provides functions needed in accessibility mode. All the functions + * in this class are made compatible with gingerbread and later API's +*/ +public class AccessibilityUtils { + public static void makeAnnouncement(View view, CharSequence announcement) { + if (view == null) + return; + if (ApiHelper.HAS_ANNOUNCE_FOR_ACCESSIBILITY) { + view.announceForAccessibility(announcement); + } else { + // For API 15 and earlier, we need to construct an accessibility event + Context ctx = view.getContext(); + AccessibilityManager am = (AccessibilityManager) ctx.getSystemService( + Context.ACCESSIBILITY_SERVICE); + if (!am.isEnabled()) return; + AccessibilityEvent event = AccessibilityEvent.obtain( + AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED); + AccessibilityRecordCompat arc = new AccessibilityRecordCompat(event); + arc.setSource(view); + event.setClassName(view.getClass().getName()); + event.setPackageName(view.getContext().getPackageName()); + event.setEnabled(view.isEnabled()); + event.getText().add(announcement); + am.sendAccessibilityEvent(event); + } + } +} \ No newline at end of file diff --git a/src/com/android/camera/util/ApiHelper.java b/src/com/android/camera/util/ApiHelper.java new file mode 100644 index 000000000..6cb303476 --- /dev/null +++ b/src/com/android/camera/util/ApiHelper.java @@ -0,0 +1,234 @@ +/* + * 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.camera.util; + +import android.app.admin.DevicePolicyManager; +import android.content.ComponentName; +import android.hardware.Camera; +import android.os.Build; +import android.provider.MediaStore.MediaColumns; +import android.view.View; +import android.view.WindowManager; + +import java.lang.reflect.Field; + +public class ApiHelper { + public static interface VERSION_CODES { + // These value are copied from Build.VERSION_CODES + public static final int GINGERBREAD_MR1 = 10; + public static final int HONEYCOMB = 11; + public static final int HONEYCOMB_MR1 = 12; + public static final int HONEYCOMB_MR2 = 13; + public static final int ICE_CREAM_SANDWICH = 14; + public static final int ICE_CREAM_SANDWICH_MR1 = 15; + public static final int JELLY_BEAN = 16; + public static final int JELLY_BEAN_MR1 = 17; + public static final int JELLY_BEAN_MR2 = 18; + } + + public static final boolean AT_LEAST_16 = Build.VERSION.SDK_INT >= 16; + + public static final boolean USE_888_PIXEL_FORMAT = + Build.VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN; + + public static final boolean ENABLE_PHOTO_EDITOR = + Build.VERSION.SDK_INT >= VERSION_CODES.ICE_CREAM_SANDWICH; + + public static final boolean HAS_VIEW_SYSTEM_UI_FLAG_LAYOUT_STABLE = + hasField(View.class, "SYSTEM_UI_FLAG_LAYOUT_STABLE"); + + public static final boolean HAS_VIEW_SYSTEM_UI_FLAG_HIDE_NAVIGATION = + hasField(View.class, "SYSTEM_UI_FLAG_HIDE_NAVIGATION"); + + public static final boolean HAS_MEDIA_COLUMNS_WIDTH_AND_HEIGHT = + hasField(MediaColumns.class, "WIDTH"); + + public static final boolean HAS_REUSING_BITMAP_IN_BITMAP_REGION_DECODER = + Build.VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN; + + public static final boolean HAS_REUSING_BITMAP_IN_BITMAP_FACTORY = + Build.VERSION.SDK_INT >= VERSION_CODES.HONEYCOMB; + + public static final boolean HAS_SET_BEAM_PUSH_URIS = + Build.VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN; + + public static final boolean HAS_SET_DEFALT_BUFFER_SIZE = hasMethod( + "android.graphics.SurfaceTexture", "setDefaultBufferSize", + int.class, int.class); + + public static final boolean HAS_RELEASE_SURFACE_TEXTURE = hasMethod( + "android.graphics.SurfaceTexture", "release"); + + public static final boolean HAS_SURFACE_TEXTURE = + Build.VERSION.SDK_INT >= VERSION_CODES.HONEYCOMB; + + public static final boolean HAS_MTP = + Build.VERSION.SDK_INT >= VERSION_CODES.HONEYCOMB_MR1; + + public static final boolean HAS_AUTO_FOCUS_MOVE_CALLBACK = + Build.VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN; + + public static final boolean HAS_REMOTE_VIEWS_SERVICE = + Build.VERSION.SDK_INT >= VERSION_CODES.HONEYCOMB; + + public static final boolean HAS_INTENT_EXTRA_LOCAL_ONLY = + Build.VERSION.SDK_INT >= VERSION_CODES.HONEYCOMB; + + public static final boolean HAS_SET_SYSTEM_UI_VISIBILITY = + hasMethod(View.class, "setSystemUiVisibility", int.class); + + public static final boolean HAS_FACE_DETECTION; + static { + boolean hasFaceDetection = false; + try { + Class listenerClass = Class.forName( + "android.hardware.Camera$FaceDetectionListener"); + hasFaceDetection = + hasMethod(Camera.class, "setFaceDetectionListener", listenerClass) && + hasMethod(Camera.class, "startFaceDetection") && + hasMethod(Camera.class, "stopFaceDetection") && + hasMethod(Camera.Parameters.class, "getMaxNumDetectedFaces"); + } catch (Throwable t) { + } + HAS_FACE_DETECTION = hasFaceDetection; + } + + public static final boolean HAS_GET_CAMERA_DISABLED = + hasMethod(DevicePolicyManager.class, "getCameraDisabled", ComponentName.class); + + public static final boolean HAS_MEDIA_ACTION_SOUND = + Build.VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN; + + public static final boolean HAS_TIME_LAPSE_RECORDING = + Build.VERSION.SDK_INT >= VERSION_CODES.HONEYCOMB; + + public static final boolean HAS_ZOOM_WHEN_RECORDING = + Build.VERSION.SDK_INT >= VERSION_CODES.ICE_CREAM_SANDWICH; + + public static final boolean HAS_CAMERA_FOCUS_AREA = + Build.VERSION.SDK_INT >= VERSION_CODES.ICE_CREAM_SANDWICH; + + public static final boolean HAS_CAMERA_METERING_AREA = + Build.VERSION.SDK_INT >= VERSION_CODES.ICE_CREAM_SANDWICH; + + public static final boolean HAS_MOTION_EVENT_TRANSFORM = + Build.VERSION.SDK_INT >= VERSION_CODES.HONEYCOMB; + + public static final boolean HAS_EFFECTS_RECORDING = false; + + // "Background" filter does not have "context" input port in jelly bean. + public static final boolean HAS_EFFECTS_RECORDING_CONTEXT_INPUT = + Build.VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN_MR1; + + public static final boolean HAS_GET_SUPPORTED_VIDEO_SIZE = + Build.VERSION.SDK_INT >= VERSION_CODES.HONEYCOMB; + + public static final boolean HAS_SET_ICON_ATTRIBUTE = + Build.VERSION.SDK_INT >= VERSION_CODES.HONEYCOMB; + + public static final boolean HAS_MEDIA_PROVIDER_FILES_TABLE = + Build.VERSION.SDK_INT >= VERSION_CODES.HONEYCOMB; + + public static final boolean HAS_SURFACE_TEXTURE_RECORDING = + Build.VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN; + + public static final boolean HAS_ACTION_BAR = + Build.VERSION.SDK_INT >= VERSION_CODES.HONEYCOMB; + + // Ex: View.setTranslationX. + public static final boolean HAS_VIEW_TRANSFORM_PROPERTIES = + Build.VERSION.SDK_INT >= VERSION_CODES.HONEYCOMB; + + public static final boolean HAS_CAMERA_HDR = + Build.VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN_MR1; + + public static final boolean HAS_OPTIONS_IN_MUTABLE = + Build.VERSION.SDK_INT >= VERSION_CODES.HONEYCOMB; + + public static final boolean CAN_START_PREVIEW_IN_JPEG_CALLBACK = + Build.VERSION.SDK_INT >= VERSION_CODES.ICE_CREAM_SANDWICH; + + public static final boolean HAS_VIEW_PROPERTY_ANIMATOR = + Build.VERSION.SDK_INT >= VERSION_CODES.HONEYCOMB_MR1; + + public static final boolean HAS_POST_ON_ANIMATION = + Build.VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN; + + public static final boolean HAS_ANNOUNCE_FOR_ACCESSIBILITY = + Build.VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN; + + public static final boolean HAS_OBJECT_ANIMATION = + Build.VERSION.SDK_INT >= VERSION_CODES.HONEYCOMB; + + public static final boolean HAS_GLES20_REQUIRED = + Build.VERSION.SDK_INT >= VERSION_CODES.HONEYCOMB; + + public static final boolean HAS_ROTATION_ANIMATION = + hasField(WindowManager.LayoutParams.class, "rotationAnimation"); + + public static final boolean HAS_ORIENTATION_LOCK = + Build.VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN_MR2; + + public static final boolean HAS_CANCELLATION_SIGNAL = + Build.VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN; + + public static final boolean HAS_MEDIA_MUXER = + Build.VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN_MR2; + + public static final boolean HAS_DISPLAY_LISTENER = + Build.VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN_MR1; + + public static int getIntFieldIfExists(Class klass, String fieldName, + Class obj, int defaultVal) { + try { + Field f = klass.getDeclaredField(fieldName); + return f.getInt(obj); + } catch (Exception e) { + return defaultVal; + } + } + + private static boolean hasField(Class klass, String fieldName) { + try { + klass.getDeclaredField(fieldName); + return true; + } catch (NoSuchFieldException e) { + return false; + } + } + + private static boolean hasMethod(String className, String methodName, + Class... parameterTypes) { + try { + Class klass = Class.forName(className); + klass.getDeclaredMethod(methodName, parameterTypes); + return true; + } catch (Throwable th) { + return false; + } + } + + private static boolean hasMethod( + Class klass, String methodName, Class ... paramTypes) { + try { + klass.getDeclaredMethod(methodName, paramTypes); + return true; + } catch (NoSuchMethodException e) { + return false; + } + } +} diff --git a/src/com/android/camera/util/CameraUtil.java b/src/com/android/camera/util/CameraUtil.java new file mode 100644 index 000000000..736235bcd --- /dev/null +++ b/src/com/android/camera/util/CameraUtil.java @@ -0,0 +1,825 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.camera.util; + +import java.io.Closeable; +import java.io.IOException; +import java.lang.reflect.Method; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.List; +import java.util.StringTokenizer; + +import android.annotation.TargetApi; +import android.app.Activity; +import android.app.AlertDialog; +import android.app.admin.DevicePolicyManager; +import android.content.ActivityNotFoundException; +import android.content.ContentResolver; +import android.content.Context; +import android.content.DialogInterface; +import android.content.Intent; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.graphics.Matrix; +import android.graphics.Point; +import android.graphics.Rect; +import android.graphics.RectF; +import android.hardware.Camera; +import android.hardware.Camera.CameraInfo; +import android.hardware.Camera.Parameters; +import android.hardware.Camera.Size; +import android.location.Location; +import android.net.Uri; +import android.os.Build; +import android.os.ParcelFileDescriptor; +import android.telephony.TelephonyManager; +import android.util.DisplayMetrics; +import android.util.FloatMath; +import android.util.Log; +import android.util.TypedValue; +import android.view.Display; +import android.view.OrientationEventListener; +import android.view.Surface; +import android.view.View; +import android.view.WindowManager; +import android.view.animation.AlphaAnimation; +import android.view.animation.Animation; +import android.widget.Toast; + +import com.android.camera.CameraDisabledException; +import com.android.camera.CameraHardwareException; +import com.android.camera.CameraHolder; +import com.android.camera.CameraManager; +import com.android.camera2.R; + +/** + * Collection of utility functions used in this package. + */ +public class CameraUtil { + private static final String TAG = "Util"; + + // For creating crop intents. + public static final String KEY_RETURN_DATA = "return-data"; + public static final String KEY_SHOW_WHEN_LOCKED = "showWhenLocked"; + + // Orientation hysteresis amount used in rounding, in degrees + public static final int ORIENTATION_HYSTERESIS = 5; + + public static final String REVIEW_ACTION = "com.android.camera.action.REVIEW"; + // See android.hardware.Camera.ACTION_NEW_PICTURE. + public static final String ACTION_NEW_PICTURE = "android.hardware.action.NEW_PICTURE"; + // See android.hardware.Camera.ACTION_NEW_VIDEO. + public static final String ACTION_NEW_VIDEO = "android.hardware.action.NEW_VIDEO"; + + // Fields from android.hardware.Camera.Parameters + public static final String FOCUS_MODE_CONTINUOUS_PICTURE = "continuous-picture"; + public static final String RECORDING_HINT = "recording-hint"; + private static final String AUTO_EXPOSURE_LOCK_SUPPORTED = "auto-exposure-lock-supported"; + private static final String AUTO_WHITE_BALANCE_LOCK_SUPPORTED = "auto-whitebalance-lock-supported"; + private static final String VIDEO_SNAPSHOT_SUPPORTED = "video-snapshot-supported"; + public static final String SCENE_MODE_HDR = "hdr"; + public static final String TRUE = "true"; + public static final String FALSE = "false"; + + /** Has to be in sync with the receiving MovieActivity. */ + public static final String KEY_TREAT_UP_AS_BACK = "treat-up-as-back"; + + public static boolean isSupported(String value, List supported) { + return supported == null ? false : supported.indexOf(value) >= 0; + } + + public static boolean isAutoExposureLockSupported(Parameters params) { + return TRUE.equals(params.get(AUTO_EXPOSURE_LOCK_SUPPORTED)); + } + + public static boolean isAutoWhiteBalanceLockSupported(Parameters params) { + return TRUE.equals(params.get(AUTO_WHITE_BALANCE_LOCK_SUPPORTED)); + } + + public static boolean isVideoSnapshotSupported(Parameters params) { + return TRUE.equals(params.get(VIDEO_SNAPSHOT_SUPPORTED)); + } + + public static boolean isCameraHdrSupported(Parameters params) { + List supported = params.getSupportedSceneModes(); + return (supported != null) && supported.contains(SCENE_MODE_HDR); + } + + @TargetApi(ApiHelper.VERSION_CODES.ICE_CREAM_SANDWICH) + public static boolean isMeteringAreaSupported(Parameters params) { + if (ApiHelper.HAS_CAMERA_METERING_AREA) { + return params.getMaxNumMeteringAreas() > 0; + } + return false; + } + + @TargetApi(ApiHelper.VERSION_CODES.ICE_CREAM_SANDWICH) + public static boolean isFocusAreaSupported(Parameters params) { + if (ApiHelper.HAS_CAMERA_FOCUS_AREA) { + return (params.getMaxNumFocusAreas() > 0 + && isSupported(Parameters.FOCUS_MODE_AUTO, + params.getSupportedFocusModes())); + } + return false; + } + + // Private intent extras. Test only. + private static final String EXTRAS_CAMERA_FACING = + "android.intent.extras.CAMERA_FACING"; + + private static float sPixelDensity = 1; + private static ImageFileNamer sImageFileNamer; + + private CameraUtil() { + } + + public static void initialize(Context context) { + DisplayMetrics metrics = new DisplayMetrics(); + WindowManager wm = (WindowManager) + context.getSystemService(Context.WINDOW_SERVICE); + wm.getDefaultDisplay().getMetrics(metrics); + sPixelDensity = metrics.density; + sImageFileNamer = new ImageFileNamer( + context.getString(R.string.image_file_name_format)); + } + + public static int dpToPixel(int dp) { + return Math.round(sPixelDensity * dp); + } + + // Rotates the bitmap by the specified degree. + // If a new bitmap is created, the original bitmap is recycled. + public static Bitmap rotate(Bitmap b, int degrees) { + return rotateAndMirror(b, degrees, false); + } + + // Rotates and/or mirrors the bitmap. If a new bitmap is created, the + // original bitmap is recycled. + public static Bitmap rotateAndMirror(Bitmap b, int degrees, boolean mirror) { + if ((degrees != 0 || mirror) && b != null) { + Matrix m = new Matrix(); + // Mirror first. + // horizontal flip + rotation = -rotation + horizontal flip + if (mirror) { + m.postScale(-1, 1); + degrees = (degrees + 360) % 360; + if (degrees == 0 || degrees == 180) { + m.postTranslate(b.getWidth(), 0); + } else if (degrees == 90 || degrees == 270) { + m.postTranslate(b.getHeight(), 0); + } else { + throw new IllegalArgumentException("Invalid degrees=" + degrees); + } + } + if (degrees != 0) { + // clockwise + m.postRotate(degrees, + (float) b.getWidth() / 2, (float) b.getHeight() / 2); + } + + try { + Bitmap b2 = Bitmap.createBitmap( + b, 0, 0, b.getWidth(), b.getHeight(), m, true); + if (b != b2) { + b.recycle(); + b = b2; + } + } catch (OutOfMemoryError ex) { + // We have no memory to rotate. Return the original bitmap. + } + } + return b; + } + + /* + * Compute the sample size as a function of minSideLength + * and maxNumOfPixels. + * minSideLength is used to specify that minimal width or height of a + * bitmap. + * maxNumOfPixels is used to specify the maximal size in pixels that is + * tolerable in terms of memory usage. + * + * The function returns a sample size based on the constraints. + * Both size and minSideLength can be passed in as -1 + * which indicates no care of the corresponding constraint. + * The functions prefers returning a sample size that + * generates a smaller bitmap, unless minSideLength = -1. + * + * Also, the function rounds up the sample size to a power of 2 or multiple + * of 8 because BitmapFactory only honors sample size this way. + * For example, BitmapFactory downsamples an image by 2 even though the + * request is 3. So we round up the sample size to avoid OOM. + */ + public static int computeSampleSize(BitmapFactory.Options options, + int minSideLength, int maxNumOfPixels) { + int initialSize = computeInitialSampleSize(options, minSideLength, + maxNumOfPixels); + + int roundedSize; + if (initialSize <= 8) { + roundedSize = 1; + while (roundedSize < initialSize) { + roundedSize <<= 1; + } + } else { + roundedSize = (initialSize + 7) / 8 * 8; + } + + return roundedSize; + } + + private static int computeInitialSampleSize(BitmapFactory.Options options, + int minSideLength, int maxNumOfPixels) { + double w = options.outWidth; + double h = options.outHeight; + + int lowerBound = (maxNumOfPixels < 0) ? 1 : + (int) Math.ceil(Math.sqrt(w * h / maxNumOfPixels)); + int upperBound = (minSideLength < 0) ? 128 : + (int) Math.min(Math.floor(w / minSideLength), + Math.floor(h / minSideLength)); + + if (upperBound < lowerBound) { + // return the larger one when there is no overlapping zone. + return lowerBound; + } + + if (maxNumOfPixels < 0 && minSideLength < 0) { + return 1; + } else if (minSideLength < 0) { + return lowerBound; + } else { + return upperBound; + } + } + + public static Bitmap makeBitmap(byte[] jpegData, int maxNumOfPixels) { + try { + BitmapFactory.Options options = new BitmapFactory.Options(); + options.inJustDecodeBounds = true; + BitmapFactory.decodeByteArray(jpegData, 0, jpegData.length, + options); + if (options.mCancel || options.outWidth == -1 + || options.outHeight == -1) { + return null; + } + options.inSampleSize = computeSampleSize( + options, -1, maxNumOfPixels); + options.inJustDecodeBounds = false; + + options.inDither = false; + options.inPreferredConfig = Bitmap.Config.ARGB_8888; + return BitmapFactory.decodeByteArray(jpegData, 0, jpegData.length, + options); + } catch (OutOfMemoryError ex) { + Log.e(TAG, "Got oom exception ", ex); + return null; + } + } + + public static void closeSilently(Closeable c) { + if (c == null) return; + try { + c.close(); + } catch (Throwable t) { + // do nothing + } + } + + public static void Assert(boolean cond) { + if (!cond) { + throw new AssertionError(); + } + } + + @TargetApi(ApiHelper.VERSION_CODES.ICE_CREAM_SANDWICH) + private static void throwIfCameraDisabled(Activity activity) throws CameraDisabledException { + // Check if device policy has disabled the camera. + if (ApiHelper.HAS_GET_CAMERA_DISABLED) { + DevicePolicyManager dpm = (DevicePolicyManager) activity.getSystemService( + Context.DEVICE_POLICY_SERVICE); + if (dpm.getCameraDisabled(null)) { + throw new CameraDisabledException(); + } + } + } + + public static CameraManager.CameraProxy openCamera( + Activity activity, int cameraId) + throws CameraHardwareException, CameraDisabledException { + throwIfCameraDisabled(activity); + + try { + return CameraHolder.instance().open(cameraId); + } catch (CameraHardwareException e) { + // In eng build, we throw the exception so that test tool + // can detect it and report it + if ("eng".equals(Build.TYPE)) { + throw new RuntimeException("openCamera failed", e); + } else { + throw e; + } + } + } + + public static void showErrorAndFinish(final Activity activity, int msgId) { + DialogInterface.OnClickListener buttonListener = + new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + activity.finish(); + } + }; + TypedValue out = new TypedValue(); + activity.getTheme().resolveAttribute(android.R.attr.alertDialogIcon, out, true); + new AlertDialog.Builder(activity) + .setCancelable(false) + .setTitle(R.string.camera_error_title) + .setMessage(msgId) + .setNeutralButton(R.string.dialog_ok, buttonListener) + .setIcon(out.resourceId) + .show(); + } + + public static T checkNotNull(T object) { + if (object == null) throw new NullPointerException(); + return object; + } + + public static boolean equals(Object a, Object b) { + return (a == b) || (a == null ? false : a.equals(b)); + } + + public static int nextPowerOf2(int n) { + n -= 1; + n |= n >>> 16; + n |= n >>> 8; + n |= n >>> 4; + n |= n >>> 2; + n |= n >>> 1; + return n + 1; + } + + public static float distance(float x, float y, float sx, float sy) { + float dx = x - sx; + float dy = y - sy; + return FloatMath.sqrt(dx * dx + dy * dy); + } + + public static int clamp(int x, int min, int max) { + if (x > max) return max; + if (x < min) return min; + return x; + } + + public static int getDisplayRotation(Activity activity) { + int rotation = activity.getWindowManager().getDefaultDisplay() + .getRotation(); + switch (rotation) { + case Surface.ROTATION_0: return 0; + case Surface.ROTATION_90: return 90; + case Surface.ROTATION_180: return 180; + case Surface.ROTATION_270: return 270; + } + return 0; + } + + public static int getDisplayOrientation(int degrees, int cameraId) { + // See android.hardware.Camera.setDisplayOrientation for + // documentation. + Camera.CameraInfo info = new Camera.CameraInfo(); + Camera.getCameraInfo(cameraId, info); + int result; + if (info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) { + result = (info.orientation + degrees) % 360; + result = (360 - result) % 360; // compensate the mirror + } else { // back-facing + result = (info.orientation - degrees + 360) % 360; + } + return result; + } + + public static int getCameraOrientation(int cameraId) { + Camera.CameraInfo info = new Camera.CameraInfo(); + Camera.getCameraInfo(cameraId, info); + return info.orientation; + } + + public static int roundOrientation(int orientation, int orientationHistory) { + boolean changeOrientation = false; + if (orientationHistory == OrientationEventListener.ORIENTATION_UNKNOWN) { + changeOrientation = true; + } else { + int dist = Math.abs(orientation - orientationHistory); + dist = Math.min( dist, 360 - dist ); + changeOrientation = ( dist >= 45 + ORIENTATION_HYSTERESIS ); + } + if (changeOrientation) { + return ((orientation + 45) / 90 * 90) % 360; + } + return orientationHistory; + } + + @SuppressWarnings("deprecation") + @TargetApi(Build.VERSION_CODES.HONEYCOMB_MR2) + private static Point getDefaultDisplaySize(Activity activity, Point size) { + Display d = activity.getWindowManager().getDefaultDisplay(); + if (Build.VERSION.SDK_INT >= ApiHelper.VERSION_CODES.HONEYCOMB_MR2) { + d.getSize(size); + } else { + size.set(d.getWidth(), d.getHeight()); + } + return size; + } + + public static Size getOptimalPreviewSize(Activity currentActivity, + List sizes, double targetRatio) { + // Use a very small tolerance because we want an exact match. + final double ASPECT_TOLERANCE = 0.001; + if (sizes == null) return null; + + Size optimalSize = null; + double minDiff = Double.MAX_VALUE; + + // Because of bugs of overlay and layout, we sometimes will try to + // layout the viewfinder in the portrait orientation and thus get the + // wrong size of preview surface. When we change the preview size, the + // new overlay will be created before the old one closed, which causes + // an exception. For now, just get the screen size. + Point point = getDefaultDisplaySize(currentActivity, new Point()); + int targetHeight = Math.min(point.x, point.y); + // Try to find an size match aspect ratio and size + for (Size size : sizes) { + double ratio = (double) size.width / size.height; + if (Math.abs(ratio - targetRatio) > ASPECT_TOLERANCE) continue; + if (Math.abs(size.height - targetHeight) < minDiff) { + optimalSize = size; + minDiff = Math.abs(size.height - targetHeight); + } + } + // Cannot find the one match the aspect ratio. This should not happen. + // Ignore the requirement. + if (optimalSize == null) { + Log.w(TAG, "No preview size match the aspect ratio"); + minDiff = Double.MAX_VALUE; + for (Size size : sizes) { + if (Math.abs(size.height - targetHeight) < minDiff) { + optimalSize = size; + minDiff = Math.abs(size.height - targetHeight); + } + } + } + return optimalSize; + } + + // Returns the largest picture size which matches the given aspect ratio. + public static Size getOptimalVideoSnapshotPictureSize( + List sizes, double targetRatio) { + // Use a very small tolerance because we want an exact match. + final double ASPECT_TOLERANCE = 0.001; + if (sizes == null) return null; + + Size optimalSize = null; + + // Try to find a size matches aspect ratio and has the largest width + for (Size size : sizes) { + double ratio = (double) size.width / size.height; + if (Math.abs(ratio - targetRatio) > ASPECT_TOLERANCE) continue; + if (optimalSize == null || size.width > optimalSize.width) { + optimalSize = size; + } + } + + // Cannot find one that matches the aspect ratio. This should not happen. + // Ignore the requirement. + if (optimalSize == null) { + Log.w(TAG, "No picture size match the aspect ratio"); + for (Size size : sizes) { + if (optimalSize == null || size.width > optimalSize.width) { + optimalSize = size; + } + } + } + return optimalSize; + } + + public static void dumpParameters(Parameters parameters) { + String flattened = parameters.flatten(); + StringTokenizer tokenizer = new StringTokenizer(flattened, ";"); + Log.d(TAG, "Dump all camera parameters:"); + while (tokenizer.hasMoreElements()) { + Log.d(TAG, tokenizer.nextToken()); + } + } + + /** + * Returns whether the device is voice-capable (meaning, it can do MMS). + */ + public static boolean isMmsCapable(Context context) { + TelephonyManager telephonyManager = (TelephonyManager) + context.getSystemService(Context.TELEPHONY_SERVICE); + if (telephonyManager == null) { + return false; + } + + try { + Class partypes[] = new Class[0]; + Method sIsVoiceCapable = TelephonyManager.class.getMethod( + "isVoiceCapable", partypes); + + Object arglist[] = new Object[0]; + Object retobj = sIsVoiceCapable.invoke(telephonyManager, arglist); + return (Boolean) retobj; + } catch (java.lang.reflect.InvocationTargetException ite) { + // Failure, must be another device. + // Assume that it is voice capable. + } catch (IllegalAccessException iae) { + // Failure, must be an other device. + // Assume that it is voice capable. + } catch (NoSuchMethodException nsme) { + } + return true; + } + + // This is for test only. Allow the camera to launch the specific camera. + public static int getCameraFacingIntentExtras(Activity currentActivity) { + int cameraId = -1; + + int intentCameraId = + currentActivity.getIntent().getIntExtra(CameraUtil.EXTRAS_CAMERA_FACING, -1); + + if (isFrontCameraIntent(intentCameraId)) { + // Check if the front camera exist + int frontCameraId = CameraHolder.instance().getFrontCameraId(); + if (frontCameraId != -1) { + cameraId = frontCameraId; + } + } else if (isBackCameraIntent(intentCameraId)) { + // Check if the back camera exist + int backCameraId = CameraHolder.instance().getBackCameraId(); + if (backCameraId != -1) { + cameraId = backCameraId; + } + } + return cameraId; + } + + private static boolean isFrontCameraIntent(int intentCameraId) { + return (intentCameraId == android.hardware.Camera.CameraInfo.CAMERA_FACING_FRONT); + } + + private static boolean isBackCameraIntent(int intentCameraId) { + return (intentCameraId == android.hardware.Camera.CameraInfo.CAMERA_FACING_BACK); + } + + private static int sLocation[] = new int[2]; + + // This method is not thread-safe. + public static boolean pointInView(float x, float y, View v) { + v.getLocationInWindow(sLocation); + return x >= sLocation[0] && x < (sLocation[0] + v.getWidth()) + && y >= sLocation[1] && y < (sLocation[1] + v.getHeight()); + } + + public static int[] getRelativeLocation(View reference, View view) { + reference.getLocationInWindow(sLocation); + int referenceX = sLocation[0]; + int referenceY = sLocation[1]; + view.getLocationInWindow(sLocation); + sLocation[0] -= referenceX; + sLocation[1] -= referenceY; + return sLocation; + } + + public static boolean isUriValid(Uri uri, ContentResolver resolver) { + if (uri == null) return false; + + try { + ParcelFileDescriptor pfd = resolver.openFileDescriptor(uri, "r"); + if (pfd == null) { + Log.e(TAG, "Fail to open URI. URI=" + uri); + return false; + } + pfd.close(); + } catch (IOException ex) { + return false; + } + return true; + } + + public static void viewUri(Uri uri, Context context) { + if (!isUriValid(uri, context.getContentResolver())) { + Log.e(TAG, "Uri invalid. uri=" + uri); + return; + } + + try { + context.startActivity(new Intent(CameraUtil.REVIEW_ACTION, uri)); + } catch (ActivityNotFoundException ex) { + try { + context.startActivity(new Intent(Intent.ACTION_VIEW, uri)); + } catch (ActivityNotFoundException e) { + Log.e(TAG, "review image fail. uri=" + uri, e); + } + } + } + + public static void dumpRect(RectF rect, String msg) { + Log.v(TAG, msg + "=(" + rect.left + "," + rect.top + + "," + rect.right + "," + rect.bottom + ")"); + } + + public static void rectFToRect(RectF rectF, Rect rect) { + rect.left = Math.round(rectF.left); + rect.top = Math.round(rectF.top); + rect.right = Math.round(rectF.right); + rect.bottom = Math.round(rectF.bottom); + } + + public static void prepareMatrix(Matrix matrix, boolean mirror, int displayOrientation, + int viewWidth, int viewHeight) { + // Need mirror for front camera. + matrix.setScale(mirror ? -1 : 1, 1); + // This is the value for android.hardware.Camera.setDisplayOrientation. + matrix.postRotate(displayOrientation); + // Camera driver coordinates range from (-1000, -1000) to (1000, 1000). + // UI coordinates range from (0, 0) to (width, height). + matrix.postScale(viewWidth / 2000f, viewHeight / 2000f); + matrix.postTranslate(viewWidth / 2f, viewHeight / 2f); + } + + public static String createJpegName(long dateTaken) { + synchronized (sImageFileNamer) { + return sImageFileNamer.generateName(dateTaken); + } + } + + public static void broadcastNewPicture(Context context, Uri uri) { + context.sendBroadcast(new Intent(ACTION_NEW_PICTURE, uri)); + // Keep compatibility + context.sendBroadcast(new Intent("com.android.camera.NEW_PICTURE", uri)); + } + + public static void fadeIn(View view, float startAlpha, float endAlpha, long duration) { + if (view.getVisibility() == View.VISIBLE) return; + + view.setVisibility(View.VISIBLE); + Animation animation = new AlphaAnimation(startAlpha, endAlpha); + animation.setDuration(duration); + view.startAnimation(animation); + } + + public static void fadeIn(View view) { + fadeIn(view, 0F, 1F, 400); + + // We disabled the button in fadeOut(), so enable it here. + view.setEnabled(true); + } + + public static void fadeOut(View view) { + if (view.getVisibility() != View.VISIBLE) return; + + // Since the button is still clickable before fade-out animation + // ends, we disable the button first to block click. + view.setEnabled(false); + Animation animation = new AlphaAnimation(1F, 0F); + animation.setDuration(400); + view.startAnimation(animation); + view.setVisibility(View.GONE); + } + + public static int getJpegRotation(int cameraId, int orientation) { + // See android.hardware.Camera.Parameters.setRotation for + // documentation. + int rotation = 0; + if (orientation != OrientationEventListener.ORIENTATION_UNKNOWN) { + CameraInfo info = CameraHolder.instance().getCameraInfo()[cameraId]; + if (info.facing == CameraInfo.CAMERA_FACING_FRONT) { + rotation = (info.orientation - orientation + 360) % 360; + } else { // back-facing camera + rotation = (info.orientation + orientation) % 360; + } + } + return rotation; + } + + /** + * Down-samples a jpeg byte array. + * @param data a byte array of jpeg data + * @param downSampleFactor down-sample factor + * @return decoded and down-sampled bitmap + */ + public static Bitmap downSample(final byte[] data, int downSampleFactor) { + final BitmapFactory.Options opts = new BitmapFactory.Options(); + // Downsample the image + opts.inSampleSize = downSampleFactor; + return BitmapFactory.decodeByteArray(data, 0, data.length, opts); + } + + public static void setGpsParameters(Parameters parameters, Location loc) { + // Clear previous GPS location from the parameters. + parameters.removeGpsData(); + + // We always encode GpsTimeStamp + parameters.setGpsTimestamp(System.currentTimeMillis() / 1000); + + // Set GPS location. + if (loc != null) { + double lat = loc.getLatitude(); + double lon = loc.getLongitude(); + boolean hasLatLon = (lat != 0.0d) || (lon != 0.0d); + + if (hasLatLon) { + Log.d(TAG, "Set gps location"); + parameters.setGpsLatitude(lat); + parameters.setGpsLongitude(lon); + parameters.setGpsProcessingMethod(loc.getProvider().toUpperCase()); + if (loc.hasAltitude()) { + parameters.setGpsAltitude(loc.getAltitude()); + } else { + // for NETWORK_PROVIDER location provider, we may have + // no altitude information, but the driver needs it, so + // we fake one. + parameters.setGpsAltitude(0); + } + if (loc.getTime() != 0) { + // Location.getTime() is UTC in milliseconds. + // gps-timestamp is UTC in seconds. + long utcTimeSeconds = loc.getTime() / 1000; + parameters.setGpsTimestamp(utcTimeSeconds); + } + } else { + loc = null; + } + } + } + + + public static int[] getMaxPreviewFpsRange(Parameters params) { + List frameRates = params.getSupportedPreviewFpsRange(); + if (frameRates != null && frameRates.size() > 0) { + // The list is sorted. Return the last element. + return frameRates.get(frameRates.size() - 1); + } + return new int[0]; + } + + private static class ImageFileNamer { + private SimpleDateFormat mFormat; + + // The date (in milliseconds) used to generate the last name. + private long mLastDate; + + // Number of names generated for the same second. + private int mSameSecondCount; + + public ImageFileNamer(String format) { + mFormat = new SimpleDateFormat(format); + } + + public String generateName(long dateTaken) { + Date date = new Date(dateTaken); + String result = mFormat.format(date); + + // If the last name was generated for the same second, + // we append _1, _2, etc to the name. + if (dateTaken / 1000 == mLastDate / 1000) { + mSameSecondCount++; + result += "_" + mSameSecondCount; + } else { + mLastDate = dateTaken; + mSameSecondCount = 0; + } + + return result; + } + } + + public static void playVideo(Context context, Uri uri, String title) { + try { + Intent intent = new Intent(Intent.ACTION_VIEW) + .setDataAndType(uri, "video/*") + .putExtra(Intent.EXTRA_TITLE, title) + .putExtra(KEY_TREAT_UP_AS_BACK, true); + context.startActivity(intent); + } catch (ActivityNotFoundException e) { + Toast.makeText(context, context.getString(R.string.video_err), + Toast.LENGTH_SHORT).show(); + } + } +} diff --git a/src/com/android/camera/util/MotionEventHelper.java b/src/com/android/camera/util/MotionEventHelper.java new file mode 100644 index 000000000..eabaeab3b --- /dev/null +++ b/src/com/android/camera/util/MotionEventHelper.java @@ -0,0 +1,103 @@ +package com.android.camera.util; + +import android.annotation.TargetApi; +import android.graphics.Matrix; +import android.util.FloatMath; +import android.view.MotionEvent; +import android.view.MotionEvent.PointerCoords; + +public final class MotionEventHelper { + private MotionEventHelper() {} + + public static MotionEvent transformEvent(MotionEvent e, Matrix m) { + // We try to use the new transform method if possible because it uses + // less memory. + if (ApiHelper.HAS_MOTION_EVENT_TRANSFORM) { + return transformEventNew(e, m); + } else { + return transformEventOld(e, m); + } + } + + @TargetApi(ApiHelper.VERSION_CODES.HONEYCOMB) + private static MotionEvent transformEventNew(MotionEvent e, Matrix m) { + MotionEvent newEvent = MotionEvent.obtain(e); + newEvent.transform(m); + return newEvent; + } + + // This is copied from Input.cpp in the android framework. + private static MotionEvent transformEventOld(MotionEvent e, Matrix m) { + long downTime = e.getDownTime(); + long eventTime = e.getEventTime(); + int action = e.getAction(); + int pointerCount = e.getPointerCount(); + int[] pointerIds = getPointerIds(e); + PointerCoords[] pointerCoords = getPointerCoords(e); + int metaState = e.getMetaState(); + float xPrecision = e.getXPrecision(); + float yPrecision = e.getYPrecision(); + int deviceId = e.getDeviceId(); + int edgeFlags = e.getEdgeFlags(); + int source = e.getSource(); + int flags = e.getFlags(); + + // Copy the x and y coordinates into an array, map them, and copy back. + float[] xy = new float[pointerCoords.length * 2]; + for (int i = 0; i < pointerCount;i++) { + xy[2 * i] = pointerCoords[i].x; + xy[2 * i + 1] = pointerCoords[i].y; + } + m.mapPoints(xy); + for (int i = 0; i < pointerCount;i++) { + pointerCoords[i].x = xy[2 * i]; + pointerCoords[i].y = xy[2 * i + 1]; + pointerCoords[i].orientation = transformAngle( + m, pointerCoords[i].orientation); + } + + MotionEvent n = MotionEvent.obtain(downTime, eventTime, action, + pointerCount, pointerIds, pointerCoords, metaState, xPrecision, + yPrecision, deviceId, edgeFlags, source, flags); + + return n; + } + + private static int[] getPointerIds(MotionEvent e) { + int n = e.getPointerCount(); + int[] r = new int[n]; + for (int i = 0; i < n; i++) { + r[i] = e.getPointerId(i); + } + return r; + } + + private static PointerCoords[] getPointerCoords(MotionEvent e) { + int n = e.getPointerCount(); + PointerCoords[] r = new PointerCoords[n]; + for (int i = 0; i < n; i++) { + r[i] = new PointerCoords(); + e.getPointerCoords(i, r[i]); + } + return r; + } + + private static float transformAngle(Matrix m, float angleRadians) { + // Construct and transform a vector oriented at the specified clockwise + // angle from vertical. Coordinate system: down is increasing Y, right is + // increasing X. + float[] v = new float[2]; + v[0] = FloatMath.sin(angleRadians); + v[1] = -FloatMath.cos(angleRadians); + m.mapVectors(v); + + // Derive the transformed vector's clockwise angle from vertical. + float result = (float) Math.atan2(v[0], -v[1]); + if (result < -Math.PI / 2) { + result += Math.PI; + } else if (result > Math.PI / 2) { + result -= Math.PI; + } + return result; + } +} diff --git a/src_pd/com/android/camera/app/StitchingProgressManager.java b/src_pd/com/android/camera/app/StitchingProgressManager.java deleted file mode 100644 index a9b796ad2..000000000 --- a/src_pd/com/android/camera/app/StitchingProgressManager.java +++ /dev/null @@ -1,21 +0,0 @@ -package com.android.camera.app; - -import android.content.Context; -import android.net.Uri; - -import com.android.camera.support.app.StitchingChangeListener; - -public class StitchingProgressManager { - public StitchingProgressManager(Context app) { - } - - public void addChangeListener(StitchingChangeListener l) { - } - - public void removeChangeListener(StitchingChangeListener l) { - } - - public Integer getProgress(Uri uri) { - return null; - } -} diff --git a/src_pd/com/android/camera/util/PhotoSphereHelper.java b/src_pd/com/android/camera/util/PhotoSphereHelper.java index 2b9016d72..c5379103d 100644 --- a/src_pd/com/android/camera/util/PhotoSphereHelper.java +++ b/src_pd/com/android/camera/util/PhotoSphereHelper.java @@ -19,11 +19,9 @@ package com.android.camera.util; import android.app.Activity; import android.content.ContentResolver; import android.content.Context; -import android.content.Intent; import android.net.Uri; import com.android.camera.CameraModule; -import com.android.camera.app.StitchingProgressManager; public class PhotoSphereHelper { public static class PanoramaMetadata { @@ -63,10 +61,6 @@ public class PhotoSphereHelper { public static final PanoramaMetadata NOT_PANORAMA = new PanoramaMetadata(false, false); - public static void setupCaptureIntent(Context context, Intent it, String outputDir) { - /* Do nothing */ - } - public static boolean hasLightCycleCapture(Context context) { return false; } @@ -79,10 +73,6 @@ public class PhotoSphereHelper { return null; } - public static StitchingProgressManager createStitchingManagerInstance(Context context) { - return null; - } - /** * Get the file path from a Media storage URI. */ diff --git a/tests_camera/src/com/android/camera/activity/CameraTestCase.java b/tests_camera/src/com/android/camera/activity/CameraTestCase.java index 27be3c7d3..4aae6b48b 100644 --- a/tests_camera/src/com/android/camera/activity/CameraTestCase.java +++ b/tests_camera/src/com/android/camera/activity/CameraTestCase.java @@ -31,7 +31,7 @@ import android.view.View; import com.android.camera.CameraHolder; import com.android.camera.CameraManager.CameraProxy; -import com.android.camera.Util; +import com.android.camera.util.CameraUtil; import com.android.gallery3d.R; import static com.google.testing.littlemock.LittleMock.mock; @@ -161,7 +161,7 @@ public class CameraTestCase extends ActivityInstrumentationT } catch (IOException e) { // ignore } finally { - Util.closeSilently(ins); + CameraUtil.closeSilently(ins); } mBlankJpeg = outputStream.toByteArray(); } diff --git a/tests_camera/src/com/android/camera/unittest/CameraUnitTest.java b/tests_camera/src/com/android/camera/unittest/CameraUnitTest.java index 0b4fc80f6..70faa5c8d 100644 --- a/tests_camera/src/com/android/camera/unittest/CameraUnitTest.java +++ b/tests_camera/src/com/android/camera/unittest/CameraUnitTest.java @@ -16,7 +16,7 @@ package com.android.camera.unittest; -import com.android.camera.Util; +import com.android.camera.util.CameraUtil; import android.graphics.Matrix; import android.test.suitebuilder.annotation.SmallTest; @@ -26,47 +26,47 @@ import junit.framework.TestCase; @SmallTest public class CameraUnitTest extends TestCase { public void testRoundOrientation() { - int h = Util.ORIENTATION_HYSTERESIS; - assertEquals(0, Util.roundOrientation(0, 0)); - assertEquals(0, Util.roundOrientation(359, 0)); - assertEquals(0, Util.roundOrientation(0 + 44 + h, 0)); - assertEquals(90, Util.roundOrientation(0 + 45 + h, 0)); - assertEquals(0, Util.roundOrientation(360 - 44 - h, 0)); - assertEquals(270, Util.roundOrientation(360 - 45 - h, 0)); - - assertEquals(90, Util.roundOrientation(90, 90)); - assertEquals(90, Util.roundOrientation(90 + 44 + h, 90)); - assertEquals(180, Util.roundOrientation(90 + 45 + h, 90)); - assertEquals(90, Util.roundOrientation(90 - 44 - h, 90)); - assertEquals(0, Util.roundOrientation(90 - 45 - h, 90)); - - assertEquals(180, Util.roundOrientation(180, 180)); - assertEquals(180, Util.roundOrientation(180 + 44 + h, 180)); - assertEquals(270, Util.roundOrientation(180 + 45 + h, 180)); - assertEquals(180, Util.roundOrientation(180 - 44 - h, 180)); - assertEquals(90, Util.roundOrientation(180 - 45 - h, 180)); - - assertEquals(270, Util.roundOrientation(270, 270)); - assertEquals(270, Util.roundOrientation(270 + 44 + h, 270)); - assertEquals(0, Util.roundOrientation(270 + 45 + h, 270)); - assertEquals(270, Util.roundOrientation(270 - 44 - h, 270)); - assertEquals(180, Util.roundOrientation(270 - 45 - h, 270)); - - assertEquals(90, Util.roundOrientation(90, 0)); - assertEquals(180, Util.roundOrientation(180, 0)); - assertEquals(270, Util.roundOrientation(270, 0)); - - assertEquals(0, Util.roundOrientation(0, 90)); - assertEquals(180, Util.roundOrientation(180, 90)); - assertEquals(270, Util.roundOrientation(270, 90)); - - assertEquals(0, Util.roundOrientation(0, 180)); - assertEquals(90, Util.roundOrientation(90, 180)); - assertEquals(270, Util.roundOrientation(270, 180)); - - assertEquals(0, Util.roundOrientation(0, 270)); - assertEquals(90, Util.roundOrientation(90, 270)); - assertEquals(180, Util.roundOrientation(180, 270)); + int h = CameraUtil.ORIENTATION_HYSTERESIS; + assertEquals(0, CameraUtil.roundOrientation(0, 0)); + assertEquals(0, CameraUtil.roundOrientation(359, 0)); + assertEquals(0, CameraUtil.roundOrientation(0 + 44 + h, 0)); + assertEquals(90, CameraUtil.roundOrientation(0 + 45 + h, 0)); + assertEquals(0, CameraUtil.roundOrientation(360 - 44 - h, 0)); + assertEquals(270, CameraUtil.roundOrientation(360 - 45 - h, 0)); + + assertEquals(90, CameraUtil.roundOrientation(90, 90)); + assertEquals(90, CameraUtil.roundOrientation(90 + 44 + h, 90)); + assertEquals(180, CameraUtil.roundOrientation(90 + 45 + h, 90)); + assertEquals(90, CameraUtil.roundOrientation(90 - 44 - h, 90)); + assertEquals(0, CameraUtil.roundOrientation(90 - 45 - h, 90)); + + assertEquals(180, CameraUtil.roundOrientation(180, 180)); + assertEquals(180, CameraUtil.roundOrientation(180 + 44 + h, 180)); + assertEquals(270, CameraUtil.roundOrientation(180 + 45 + h, 180)); + assertEquals(180, CameraUtil.roundOrientation(180 - 44 - h, 180)); + assertEquals(90, CameraUtil.roundOrientation(180 - 45 - h, 180)); + + assertEquals(270, CameraUtil.roundOrientation(270, 270)); + assertEquals(270, CameraUtil.roundOrientation(270 + 44 + h, 270)); + assertEquals(0, CameraUtil.roundOrientation(270 + 45 + h, 270)); + assertEquals(270, CameraUtil.roundOrientation(270 - 44 - h, 270)); + assertEquals(180, CameraUtil.roundOrientation(270 - 45 - h, 270)); + + assertEquals(90, CameraUtil.roundOrientation(90, 0)); + assertEquals(180, CameraUtil.roundOrientation(180, 0)); + assertEquals(270, CameraUtil.roundOrientation(270, 0)); + + assertEquals(0, CameraUtil.roundOrientation(0, 90)); + assertEquals(180, CameraUtil.roundOrientation(180, 90)); + assertEquals(270, CameraUtil.roundOrientation(270, 90)); + + assertEquals(0, CameraUtil.roundOrientation(0, 180)); + assertEquals(90, CameraUtil.roundOrientation(90, 180)); + assertEquals(270, CameraUtil.roundOrientation(270, 180)); + + assertEquals(0, CameraUtil.roundOrientation(0, 270)); + assertEquals(90, CameraUtil.roundOrientation(90, 270)); + assertEquals(180, CameraUtil.roundOrientation(180, 270)); } public void testPrepareMatrix() { @@ -74,25 +74,25 @@ public class CameraUnitTest extends TestCase { float[] points; int[] expected; - Util.prepareMatrix(matrix, false, 0, 800, 480); + CameraUtil.prepareMatrix(matrix, false, 0, 800, 480); points = new float[] {-1000, -1000, 0, 0, 1000, 1000, 0, 1000, -750, 250}; expected = new int[] {0, 0, 400, 240, 800, 480, 400, 480, 100, 300}; matrix.mapPoints(points); assertEquals(expected, points); - Util.prepareMatrix(matrix, false, 90, 800, 480); + CameraUtil.prepareMatrix(matrix, false, 90, 800, 480); points = new float[] {-1000, -1000, 0, 0, 1000, 1000, 0, 1000, -750, 250}; expected = new int[] {800, 0, 400, 240, 0, 480, 0, 240, 300, 60}; matrix.mapPoints(points); assertEquals(expected, points); - Util.prepareMatrix(matrix, false, 180, 800, 480); + CameraUtil.prepareMatrix(matrix, false, 180, 800, 480); points = new float[] {-1000, -1000, 0, 0, 1000, 1000, 0, 1000, -750, 250}; expected = new int[] {800, 480, 400, 240, 0, 0, 400, 0, 700, 180}; matrix.mapPoints(points); assertEquals(expected, points); - Util.prepareMatrix(matrix, true, 180, 800, 480); + CameraUtil.prepareMatrix(matrix, true, 180, 800, 480); points = new float[] {-1000, -1000, 0, 0, 1000, 1000, 0, 1000, -750, 250}; expected = new int[] {0, 480, 400, 240, 800, 0, 400, 0, 100, 180}; matrix.mapPoints(points); -- cgit v1.2.3