OSDN Git Service

Sync with jb-mr1-dev
authorThe Android Open Source Project <initial-contribution@android.com>
Tue, 31 Jul 2012 14:52:25 +0000 (07:52 -0700)
committerThe Android Open Source Project <initial-contribution@android.com>
Tue, 31 Jul 2012 14:52:25 +0000 (07:52 -0700)
Change-Id: I9a32d6562c1db5031763241e8624be666624b3e4

33 files changed:
Android.mk
AndroidManifest.xml
gallerycommon/Android.mk
gallerycommon/src/com/android/gallery3d/common/ApiHelper.java
jni/Android.mk
jni/jni_egl_fence.cpp
res/drawable-hdpi/menu_dropdown_panel_holo_dark.9.png [new file with mode: 0644]
res/drawable-mdpi/menu_dropdown_panel_holo_dark.9.png [new file with mode: 0644]
res/drawable-xhdpi/menu_dropdown_panel_holo_dark.9.png [new file with mode: 0644]
res/layout/action_bar_text.xml
res/layout/popup_list_item.xml [new file with mode: 0644]
res/mipmap-hdpi/ic_launcher_gallery.png
res/mipmap-mdpi/ic_launcher_gallery.png
res/mipmap-xhdpi/ic_launcher_gallery.png
res/values-v14/styles.xml [new file with mode: 0644]
res/values-xlarge/styles.xml
res/values/attrs.xml [moved from res/menu/selection.xml with 73% similarity]
res/values/ids.xml
res/values/styles.xml
src/com/android/gallery3d/app/ActivityState.java
src/com/android/gallery3d/app/AlbumPage.java
src/com/android/gallery3d/app/AlbumSetPage.java
src/com/android/gallery3d/data/MtpClient.java
src/com/android/gallery3d/data/MtpContext.java
src/com/android/gallery3d/data/MtpDevice.java
src/com/android/gallery3d/data/MtpDeviceSet.java
src/com/android/gallery3d/data/MtpImage.java
src/com/android/gallery3d/ui/ActionModeHandler.java
src/com/android/gallery3d/ui/CustomMenu.java [deleted file]
src/com/android/gallery3d/ui/PhotoView.java
src/com/android/gallery3d/ui/PopupList.java [new file with mode: 0644]
src/com/android/gallery3d/ui/ScrollerHelper.java
src/com/android/gallery3d/ui/SelectionMenu.java [new file with mode: 0644]

index b0ebf89..4854a5c 100644 (file)
@@ -18,7 +18,7 @@ LOCAL_PACKAGE_NAME := Gallery2
 
 LOCAL_OVERRIDES_PACKAGES := Gallery Gallery3D GalleryNew3D
 
-LOCAL_SDK_VERSION := current
+LOCAL_SDK_VERSION := 16
 
 LOCAL_JNI_SHARED_LIBRARIES := libjni_mosaic libjni_eglfence
 
index c7a693b..57aa797 100644 (file)
@@ -7,7 +7,7 @@
 
     <original-package android:name="com.android.gallery3d" />
 
-    <uses-sdk android:minSdkVersion="14" android:targetSdkVersion="16" />
+    <uses-sdk android:minSdkVersion="13" android:targetSdkVersion="16" />
 
     <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
     <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
index fb81dd9..1d34147 100644 (file)
@@ -22,6 +22,6 @@ include $(CLEAR_VARS)
 
 LOCAL_MODULE := com.android.gallery3d.common2
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
-LOCAL_SDK_VERSION := current
+LOCAL_SDK_VERSION := 16
 
 include $(BUILD_STATIC_JAVA_LIBRARY)
index 620e1ae..0d349f3 100644 (file)
@@ -20,7 +20,6 @@ import android.app.Activity;
 import android.app.admin.DevicePolicyManager;
 import android.content.ComponentName;
 import android.hardware.Camera;
-import android.hardware.Camera.FaceDetectionListener;
 import android.os.Build;
 import android.provider.MediaStore.MediaColumns;
 import android.view.View;
@@ -82,12 +81,21 @@ public class ApiHelper {
     public static final boolean HAS_SET_SYSTEM_UI_VISIBILITY =
             hasMethod(View.class, "setSystemUiVisibility", int.class);
 
-    public static final boolean HAS_FACE_DETECTION =
-            hasClass(Camera.class, "android.hardware.Camera$FaceDetectionListener") &&
-            hasMethod(Camera.class, "setFaceDetectionListener", FaceDetectionListener.class) &&
-            hasMethod(Camera.class, "startFaceDetection") &&
-            hasMethod(Camera.class, "stopFaceDetection") &&
-            hasMethod(Camera.Parameters.class, "getMaxNumDetectedFaces");
+    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);
@@ -95,6 +103,18 @@ public class ApiHelper {
     public static final boolean HAS_MEDIA_ACTION_SOUND =
             Build.VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN;
 
+    public static final boolean HAS_PANORAMA =
+            Build.VERSION.SDK_INT >= VERSION_CODES.HONEYCOMB;
+
+    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 int getIntFieldIfExists(Class<?> klass, String fieldName,
             Class<?> obj, int defaultVal) {
         try {
@@ -137,15 +157,4 @@ public class ApiHelper {
             return false;
         }
     }
-
-    private static boolean hasClass(Class<?> klass, String className) {
-        Class<?>[] klasses = klass.getClasses();
-        for (int i = 0; i < klasses.length; ++i) {
-            if (klasses[i].getName().equals(className)) {
-                return true;
-            }
-        }
-        return false;
-    }
-
 }
index eedfa0b..593074f 100644 (file)
@@ -6,11 +6,20 @@ LOCAL_CFLAGS += -DEGL_EGLEXT_PROTOTYPES
 
 LOCAL_SRC_FILES := jni_egl_fence.cpp
 
-LOCAL_SHARED_LIBRARIES := libcutils libEGL
+ifeq ($(TARGET_ARCH), arm)
+        LOCAL_NDK_VERSION := 5
+        LOCAL_SDK_VERSION := 9
+endif
+
+ifeq ($(TARGET_ARCH), x86)
+        LOCAL_NDK_VERSION := 6
+        LOCAL_SDK_VERSION := 9
+endif
+
+LOCAL_LDFLAGS :=  -llog -lEGL
 
 LOCAL_MODULE_TAGS := optional
 
 LOCAL_MODULE := libjni_eglfence
 
 include $(BUILD_SHARED_LIBRARY)
-
index 8d557df..cf15e2f 100644 (file)
 
 #include "jni_egl_fence.h"
 
-#include <cutils/log.h>
+#include <android/log.h>
 #include <EGL/egl.h>
 #include <EGL/eglext.h>
+#include <string.h>
+
+#define  ALOGE(...)  __android_log_print(ANDROID_LOG_ERROR,"egl_fence",__VA_ARGS__)
+
+typedef EGLSyncKHR EGLAPIENTRY (*TypeEglCreateSyncKHR)(EGLDisplay dpy,
+    EGLenum type, const EGLint *attrib_list);
+typedef EGLBoolean EGLAPIENTRY (*TypeEglDestroySyncKHR)(EGLDisplay dpy,
+    EGLSyncKHR sync);
+typedef EGLint EGLAPIENTRY (*TypeEglClientWaitSyncKHR)(EGLDisplay dpy,
+    EGLSyncKHR sync, EGLint flags, EGLTimeKHR timeout);
+static TypeEglCreateSyncKHR FuncEglCreateSyncKHR = NULL;
+static TypeEglClientWaitSyncKHR FuncEglClientWaitSyncKHR = NULL;
+static TypeEglDestroySyncKHR FuncEglDestroySyncKHR = NULL;
+static bool initialized = false;
+static bool egl_khr_fence_sync_supported = false;
+
+bool IsEglKHRFenceSyncSupported() {
+  if (!initialized) {
+    EGLDisplay display = eglGetCurrentDisplay();
+    const char* eglExtensions = eglQueryString(eglGetCurrentDisplay(), EGL_EXTENSIONS);
+    if (eglExtensions && strstr(eglExtensions, "EGL_KHR_fence_sync")) {
+      FuncEglCreateSyncKHR = (TypeEglCreateSyncKHR) eglGetProcAddress("eglCreateSyncKHR");
+      FuncEglClientWaitSyncKHR = (TypeEglClientWaitSyncKHR) eglGetProcAddress("eglClientWaitSyncKHR");
+      FuncEglDestroySyncKHR = (TypeEglDestroySyncKHR) eglGetProcAddress("eglDestroySyncKHR");
+      if (FuncEglCreateSyncKHR != NULL && FuncEglClientWaitSyncKHR != NULL
+          && FuncEglDestroySyncKHR != NULL) {
+        egl_khr_fence_sync_supported = true;
+      }
+    }
+    initialized = true;
+  }
+  return egl_khr_fence_sync_supported;
+}
 
 void
 Java_com_android_gallery3d_photoeditor_FilterStack_nativeEglSetFenceAndWait(JNIEnv* env,
                                                                           jobject thiz) {
+  if (!IsEglKHRFenceSyncSupported()) return;
   EGLDisplay display = eglGetCurrentDisplay();
 
   // Create a egl fence and wait for egl to return it.
   // Additional reference on egl fence sync can be found in:
   // http://www.khronos.org/registry/vg/extensions/KHR/EGL_KHR_fence_sync.txt
-  EGLSyncKHR fence = eglCreateSyncKHR(display, EGL_SYNC_FENCE_KHR, NULL);
+  EGLSyncKHR fence = FuncEglCreateSyncKHR(display, EGL_SYNC_FENCE_KHR, NULL);
   if (fence == EGL_NO_SYNC_KHR) {
     return;
   }
 
-  EGLint result = eglClientWaitSyncKHR(display,
+  EGLint result = FuncEglClientWaitSyncKHR(display,
                                        fence,
                                        EGL_SYNC_FLUSH_COMMANDS_BIT_KHR,
                                        EGL_FOREVER_KHR);
   if (result == EGL_FALSE) {
     ALOGE("EGL FENCE: error waiting for fence: %#x", eglGetError());
   }
-  eglDestroySyncKHR(display, fence);
+  FuncEglDestroySyncKHR(display, fence);
 }
diff --git a/res/drawable-hdpi/menu_dropdown_panel_holo_dark.9.png b/res/drawable-hdpi/menu_dropdown_panel_holo_dark.9.png
new file mode 100644 (file)
index 0000000..4d3d208
Binary files /dev/null and b/res/drawable-hdpi/menu_dropdown_panel_holo_dark.9.png differ
diff --git a/res/drawable-mdpi/menu_dropdown_panel_holo_dark.9.png b/res/drawable-mdpi/menu_dropdown_panel_holo_dark.9.png
new file mode 100644 (file)
index 0000000..460ec46
Binary files /dev/null and b/res/drawable-mdpi/menu_dropdown_panel_holo_dark.9.png differ
diff --git a/res/drawable-xhdpi/menu_dropdown_panel_holo_dark.9.png b/res/drawable-xhdpi/menu_dropdown_panel_holo_dark.9.png
new file mode 100644 (file)
index 0000000..e2aff72
Binary files /dev/null and b/res/drawable-xhdpi/menu_dropdown_panel_holo_dark.9.png differ
index 2a1f031..7fccf02 100644 (file)
@@ -23,5 +23,5 @@
     android:gravity="center_vertical"
     android:paddingLeft="18dp"
     android:paddingRight="18dp"
-    android:minHeight="?android:attr/listPreferredItemHeightSmall"
+    android:minHeight="?attr/listPreferredItemHeightSmall"
 />
diff --git a/res/layout/popup_list_item.xml b/res/layout/popup_list_item.xml
new file mode 100644 (file)
index 0000000..3c43e8c
--- /dev/null
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<TextView xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@android:id/text1"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:textAppearance="?android:attr/textAppearanceListItemSmall"
+    android:gravity="center_vertical"
+    android:paddingLeft="16dp"
+    android:paddingRight="16dp"
+    android:minHeight="?attr/listPreferredItemHeightSmall"
+    android:minWidth="196dp"
+/>
index 23ea998..869cf89 100644 (file)
Binary files a/res/mipmap-hdpi/ic_launcher_gallery.png and b/res/mipmap-hdpi/ic_launcher_gallery.png differ
index e1a9949..682ab14 100644 (file)
Binary files a/res/mipmap-mdpi/ic_launcher_gallery.png and b/res/mipmap-mdpi/ic_launcher_gallery.png differ
index 79544a2..dc36096 100644 (file)
Binary files a/res/mipmap-xhdpi/ic_launcher_gallery.png and b/res/mipmap-xhdpi/ic_launcher_gallery.png differ
diff --git a/res/values-v14/styles.xml b/res/values-v14/styles.xml
new file mode 100644 (file)
index 0000000..d14ad08
--- /dev/null
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<resources>
+    <style name="Theme.GalleryBase" parent="android:Theme.Holo">
+        <item name="listPreferredItemHeightSmall">?android:attr/listPreferredItemHeightSmall</item>
+    </style>
+</resources>
index 0bf4d56..7670f61 100644 (file)
@@ -14,7 +14,7 @@
      limitations under the License.
 -->
 <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <style name="Theme.Gallery" parent="android:Theme.Holo">
+    <style name="Theme.Gallery" parent="Theme.GalleryBase">
         <item name="android:displayOptions"></item>
         <item name="android:actionBarStyle">@style/Holo.ActionBar</item>
         <item name="android:windowBackground">@android:color/black</item>
similarity index 73%
rename from res/menu/selection.xml
rename to res/values/attrs.xml
index 18839e4..f9c3d24 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2010 Google Inc.
+<!-- 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.
@@ -13,6 +13,8 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-<menu xmlns:android="http://schemas.android.com/apk/res/android">
-    <item android:id="@+id/action_select_all" android:title="@string/select_all" />
-</menu>
+<resources>
+    <declare-styleable name="Theme.GalleryBase">
+        <attr name="listPreferredItemHeightSmall" format="dimension" />
+    </declare-styleable>
+</resources>
index b6fda47..7babba0 100644 (file)
@@ -18,4 +18,5 @@
 -->
 <resources>
     <item type="id" name="action_toggle_full_caching" />
+    <item type="id" name="action_select_all" />
 </resources>
index 3c32cef..7389318 100644 (file)
 -->
 
 <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <style name="Theme.Gallery" parent="android:Theme.Holo">
+    <style name="Theme.GalleryBase" parent="android:Theme.Holo">
+        <item name="listPreferredItemHeightSmall">48dp</item>
+    </style>
+    <style name="Theme.Gallery" parent="Theme.GalleryBase">
         <item name="android:displayOptions"></item>
         <item name="android:windowContentOverlay">@null</item>
         <item name="android:actionBarStyle">@style/Holo.ActionBar</item>
index c870855..ca0816c 100644 (file)
@@ -19,12 +19,15 @@ package com.android.gallery3d.app;
 import android.app.ActionBar;
 import android.app.Activity;
 import android.content.BroadcastReceiver;
+import android.content.ContentResolver;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.content.res.Configuration;
 import android.os.BatteryManager;
 import android.os.Bundle;
+import android.provider.Settings;
+import android.provider.Settings.SettingNotFoundException;
 import android.view.Menu;
 import android.view.MenuItem;
 import android.view.Window;
@@ -57,6 +60,9 @@ abstract public class ActivityState {
         public Intent resultData;
     }
 
+    protected boolean mHapticsEnabled;
+    private ContentResolver mContentResolver;
+
     private boolean mDestroyed = false;
     private boolean mPlugged = false;
     boolean mIsFinishing = false;
@@ -71,6 +77,7 @@ abstract public class ActivityState {
     void initialize(GalleryActivity activity, Bundle data) {
         mActivity = activity;
         mData = data;
+        mContentResolver = activity.getAndroidContext().getContentResolver();
     }
 
     public Bundle getData() {
@@ -167,6 +174,14 @@ abstract public class ActivityState {
             filter.addAction(Intent.ACTION_BATTERY_CHANGED);
             activity.registerReceiver(mPowerIntentReceiver, filter);
         }
+
+        try {
+            mHapticsEnabled = Settings.System.getInt(mContentResolver,
+                    Settings.System.HAPTIC_FEEDBACK_ENABLED) != 0;
+        } catch (SettingNotFoundException e) {
+            mHapticsEnabled = false;
+        }
+
         onResume();
 
         // the transition store should be cleared after resume;
index e773691..ed88aed 100644 (file)
@@ -613,7 +613,7 @@ public class AlbumPage extends ActivityState implements GalleryActionBar.Cluster
         switch (mode) {
             case SelectionManager.ENTER_SELECTION_MODE: {
                 mActionMode = mActionModeHandler.startActionMode();
-                mVibrator.vibrate(100);
+                if(mHapticsEnabled) mVibrator.vibrate(100);
                 break;
             }
             case SelectionManager.LEAVE_SELECTION_MODE: {
index aa72eb8..b610202 100644 (file)
@@ -533,7 +533,7 @@ public class AlbumSetPage extends ActivityState implements
             case SelectionManager.ENTER_SELECTION_MODE: {
                 mActionBar.disableClusterMenu(true);
                 mActionMode = mActionModeHandler.startActionMode();
-                mVibrator.vibrate(100);
+                if(mHapticsEnabled) mVibrator.vibrate(100);
                 break;
             }
             case SelectionManager.LEAVE_SELECTION_MODE: {
index 2d58df2..dd65f9e 100644 (file)
@@ -16,6 +16,7 @@
 
 package com.android.gallery3d.data;
 
+import android.annotation.TargetApi;
 import android.app.PendingIntent;
 import android.content.BroadcastReceiver;
 import android.content.Context;
@@ -31,6 +32,8 @@ import android.mtp.MtpObjectInfo;
 import android.mtp.MtpStorageInfo;
 import android.util.Log;
 
+import com.android.gallery3d.common.ApiHelper;
+
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.List;
@@ -40,6 +43,7 @@ import java.util.List;
  * It listens for MTP devices being attached and removed from the USB host bus
  * and notifies the application when the MTP device list changes.
  */
+@TargetApi(ApiHelper.VERSION_CODES.HONEYCOMB_MR1)
 public class MtpClient {
 
     private static final String TAG = "MtpClient";
index 0749020..056211c 100644 (file)
@@ -1,5 +1,6 @@
 package com.android.gallery3d.data;
 
+import android.annotation.TargetApi;
 import android.content.Context;
 import android.media.MediaScannerConnection;
 import android.media.MediaScannerConnection.MediaScannerConnectionClient;
@@ -10,6 +11,7 @@ import android.util.Log;
 import android.widget.Toast;
 
 import com.android.gallery3d.R;
+import com.android.gallery3d.common.ApiHelper;
 import com.android.gallery3d.util.BucketNames;
 import com.android.gallery3d.util.GalleryUtils;
 
@@ -17,6 +19,7 @@ import java.io.File;
 import java.util.ArrayList;
 import java.util.List;
 
+@TargetApi(ApiHelper.VERSION_CODES.HONEYCOMB_MR1)
 public class MtpContext implements MtpClient.Listener {
     private static final String TAG = "MtpContext";
 
index f43ae2b..b233b3c 100644 (file)
@@ -16,6 +16,7 @@
 
 package com.android.gallery3d.data;
 
+import android.annotation.TargetApi;
 import android.hardware.usb.UsbDevice;
 import android.mtp.MtpConstants;
 import android.mtp.MtpObjectInfo;
@@ -24,10 +25,12 @@ import android.net.Uri;
 import android.util.Log;
 
 import com.android.gallery3d.app.GalleryApp;
+import com.android.gallery3d.common.ApiHelper;
 
 import java.util.ArrayList;
 import java.util.List;
 
+@TargetApi(ApiHelper.VERSION_CODES.HONEYCOMB_MR1)
 public class MtpDevice extends MediaSet {
     private static final String TAG = "MtpDevice";
 
index 1f26511..bc4bc63 100644 (file)
@@ -16,6 +16,7 @@
 
 package com.android.gallery3d.data;
 
+import android.annotation.TargetApi;
 import android.mtp.MtpDeviceInfo;
 import android.net.Uri;
 import android.os.Handler;
@@ -23,6 +24,7 @@ import android.util.Log;
 
 import com.android.gallery3d.R;
 import com.android.gallery3d.app.GalleryApp;
+import com.android.gallery3d.common.ApiHelper;
 import com.android.gallery3d.util.Future;
 import com.android.gallery3d.util.FutureListener;
 import com.android.gallery3d.util.MediaSetUtils;
@@ -34,6 +36,7 @@ import java.util.Collections;
 import java.util.List;
 
 // MtpDeviceSet -- MtpDevice -- MtpImage
+@TargetApi(ApiHelper.VERSION_CODES.HONEYCOMB_MR1)
 public class MtpDeviceSet extends MediaSet
         implements FutureListener<ArrayList<MediaSet>> {
     private static final String TAG = "MtpDeviceSet";
index 96b4d9f..1d0f88d 100644 (file)
@@ -16,6 +16,7 @@
 
 package com.android.gallery3d.data;
 
+import android.annotation.TargetApi;
 import android.content.Context;
 import android.graphics.Bitmap;
 import android.graphics.BitmapRegionDecoder;
@@ -25,6 +26,7 @@ import android.net.Uri;
 import android.util.Log;
 
 import com.android.gallery3d.app.GalleryApp;
+import com.android.gallery3d.common.ApiHelper;
 import com.android.gallery3d.provider.GalleryProvider;
 import com.android.gallery3d.util.ThreadPool;
 import com.android.gallery3d.util.ThreadPool.Job;
@@ -33,6 +35,7 @@ import com.android.gallery3d.util.ThreadPool.JobContext;
 import java.text.DateFormat;
 import java.util.Date;
 
+@TargetApi(ApiHelper.VERSION_CODES.HONEYCOMB_MR1)
 public class MtpImage extends MediaItem {
     private static final String TAG = "MtpImage";
 
index 190b2b8..80d2500 100644 (file)
@@ -22,7 +22,6 @@ import android.content.Context;
 import android.content.Intent;
 import android.net.Uri;
 import android.nfc.NfcAdapter;
-import android.os.Build;
 import android.os.Handler;
 import android.view.ActionMode;
 import android.view.LayoutInflater;
@@ -31,7 +30,6 @@ import android.view.MenuInflater;
 import android.view.MenuItem;
 import android.view.View;
 import android.widget.Button;
-import android.widget.PopupMenu.OnMenuItemClickListener;
 import android.widget.ShareActionProvider;
 import android.widget.ShareActionProvider.OnShareTargetSelectedListener;
 
@@ -43,7 +41,6 @@ import com.android.gallery3d.common.Utils;
 import com.android.gallery3d.data.DataManager;
 import com.android.gallery3d.data.MediaObject;
 import com.android.gallery3d.data.Path;
-import com.android.gallery3d.ui.CustomMenu.DropDownMenu;
 import com.android.gallery3d.ui.MenuExecutor.ProgressListener;
 import com.android.gallery3d.util.Future;
 import com.android.gallery3d.util.GalleryUtils;
@@ -52,7 +49,8 @@ import com.android.gallery3d.util.ThreadPool.JobContext;
 
 import java.util.ArrayList;
 
-public class ActionModeHandler implements ActionMode.Callback {
+public class ActionModeHandler implements
+        ActionMode.Callback, PopupList.OnPopupItemClickListener {
     private static final String TAG = "ActionModeHandler";
     private static final int SUPPORT_MULTIPLE_MASK = MediaObject.SUPPORT_DELETE
             | MediaObject.SUPPORT_ROTATE | MediaObject.SUPPORT_SHARE
@@ -67,7 +65,7 @@ public class ActionModeHandler implements ActionMode.Callback {
     private final SelectionManager mSelectionManager;
     private final NfcAdapter mNfcAdapter;
     private Menu mMenu;
-    private DropDownMenu mSelectionMenu;
+    private SelectionMenu mSelectionMenu;
     private ActionModeListener mListener;
     private Future<?> mMenuTask;
     private final Handler mMainHandler;
@@ -85,20 +83,13 @@ public class ActionModeHandler implements ActionMode.Callback {
     public ActionMode startActionMode() {
         Activity a = (Activity) mActivity;
         final ActionMode actionMode = a.startActionMode(this);
-        CustomMenu customMenu = new CustomMenu(a);
         View customView = LayoutInflater.from(a).inflate(
                 R.layout.action_mode, null);
         actionMode.setCustomView(customView);
-        mSelectionMenu = customMenu.addDropDownMenu(
-                (Button) customView.findViewById(R.id.selection_menu),
-                R.menu.selection);
+        mSelectionMenu = new SelectionMenu(a,
+                (Button) customView.findViewById(R.id.selection_menu), this);
         updateSelectionMenu();
-        customMenu.setOnMenuItemClickListener(new OnMenuItemClickListener() {
-            @Override
-            public boolean onMenuItemClick(MenuItem item) {
-                return onActionItemClicked(actionMode, item);
-            }
-        });
+
         return actionMode;
     }
 
@@ -137,14 +128,25 @@ public class ActionModeHandler implements ActionMode.Callback {
                         R.plurals.delete_selection, mSelectionManager.getSelectedCount());
             }
             mMenuExecutor.onMenuClicked(item, confirmMsg, listener);
-            if (action == R.id.action_select_all) {
+        } finally {
+            root.unlockRenderThread();
+        }
+        return true;
+    }
+
+    @Override
+    public boolean onPopupItemClick(int itemId) {
+        GLRoot root = mActivity.getGLRoot();
+        root.lockRenderThread();
+        try {
+            if (itemId == R.id.action_select_all) {
                 updateSupportedOperation();
-                updateSelectionMenu();
+                mMenuExecutor.onMenuClicked(itemId, null, false, true);
             }
+            return true;
         } finally {
             root.unlockRenderThread();
         }
-        return true;
     }
 
     private void updateSelectionMenu() {
@@ -153,18 +155,10 @@ public class ActionModeHandler implements ActionMode.Callback {
         String format = mActivity.getResources().getQuantityString(
                 R.plurals.number_of_items_selected, count);
         setTitle(String.format(format, count));
+
         // For clients who call SelectionManager.selectAll() directly, we need to ensure the
         // menu status is consistent with selection manager.
-        MenuItem item = mSelectionMenu.findItem(R.id.action_select_all);
-        if (item != null) {
-            if (mSelectionManager.inSelectAllMode()) {
-                item.setChecked(true);
-                item.setTitle(R.string.deselect_all);
-            } else {
-                item.setChecked(false);
-                item.setTitle(R.string.select_all);
-            }
-        }
+        mSelectionMenu.updateSelectAllMode(mSelectionManager.inSelectAllMode());
     }
 
     public boolean onCreateActionMode(ActionMode mode, Menu menu) {
diff --git a/src/com/android/gallery3d/ui/CustomMenu.java b/src/com/android/gallery3d/ui/CustomMenu.java
deleted file mode 100644 (file)
index 15b3dc2..0000000
+++ /dev/null
@@ -1,89 +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.ui;
-
-import android.content.Context;
-import android.view.Menu;
-import android.view.MenuItem;
-import android.view.View;
-import android.view.View.OnClickListener;
-import android.widget.Button;
-import android.widget.PopupMenu;
-import android.widget.PopupMenu.OnMenuItemClickListener;
-
-import java.util.ArrayList;
-
-public class CustomMenu implements OnMenuItemClickListener {
-    @SuppressWarnings("unused")
-    private static final String TAG = "FilterMenu";
-
-    public static class DropDownMenu {
-        private Button mButton;
-        private PopupMenu mPopupMenu;
-        private Menu mMenu;
-
-        public DropDownMenu(Context context, Button button, int menuId,
-                OnMenuItemClickListener listener) {
-            mButton = button;
-            mPopupMenu = new PopupMenu(context, mButton);
-            mMenu = mPopupMenu.getMenu();
-            mPopupMenu.getMenuInflater().inflate(menuId, mMenu);
-            mPopupMenu.setOnMenuItemClickListener(listener);
-            mButton.setOnClickListener(new OnClickListener() {
-                @Override
-                public void onClick(View v) {
-                    mPopupMenu.show();
-                }
-            });
-        }
-
-        public MenuItem findItem(int id) {
-            return mMenu.findItem(id);
-        }
-
-        public void setTitle(CharSequence title) {
-            mButton.setText(title);
-        }
-    }
-
-    private Context mContext;
-    private ArrayList<DropDownMenu> mMenus;
-    private OnMenuItemClickListener mListener;
-
-    public CustomMenu(Context context) {
-        mContext = context;
-        mMenus = new ArrayList<DropDownMenu>();
-    }
-
-    public DropDownMenu addDropDownMenu(Button button, int menuId) {
-        DropDownMenu menu = new DropDownMenu(mContext, button, menuId, this);
-        mMenus.add(menu);
-        return menu;
-    }
-
-    public void setOnMenuItemClickListener(OnMenuItemClickListener listener) {
-        mListener = listener;
-    }
-
-    @Override
-    public boolean onMenuItemClick(MenuItem item) {
-        if (mListener != null) {
-            return mListener.onMenuItemClick(item);
-        }
-        return false;
-    }
-}
index e44905d..2525433 100644 (file)
@@ -1565,6 +1565,7 @@ public class PhotoView extends GLView {
 
     public boolean switchWithCaptureAnimation(int offset) {
         GLRoot root = getGLRoot();
+        if(root == null) return false;
         root.lockRenderThread();
         try {
             return switchWithCaptureAnimationLocked(offset);
diff --git a/src/com/android/gallery3d/ui/PopupList.java b/src/com/android/gallery3d/ui/PopupList.java
new file mode 100644 (file)
index 0000000..ee02029
--- /dev/null
@@ -0,0 +1,188 @@
+// Copyright 2012 Google Inc. All Rights Reserved.
+
+package com.android.gallery3d.ui;
+
+import android.content.Context;
+import android.graphics.Rect;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.View.MeasureSpec;
+import android.view.ViewGroup;
+import android.view.ViewTreeObserver;
+import android.view.ViewTreeObserver.OnGlobalLayoutListener;
+import android.widget.AdapterView;
+import android.widget.AdapterView.OnItemClickListener;
+import android.widget.BaseAdapter;
+import android.widget.ListView;
+import android.widget.PopupWindow;
+import android.widget.TextView;
+
+import com.android.gallery3d.R;
+import com.android.gallery3d.common.Utils;
+
+import java.util.ArrayList;
+
+public class PopupList {
+
+    public static interface OnPopupItemClickListener {
+        public boolean onPopupItemClick(int itemId);
+    }
+
+    public static class Item {
+        public final int id;
+        public String title;
+
+        public Item(int id, String title) {
+            this.id = id;
+            this.title = title;
+        }
+
+        public void setTitle(String title) {
+            this.title = title;
+        }
+    }
+
+    private final Context mContext;
+    private final View mAnchorView;
+    private final ArrayList<Item> mItems = new ArrayList<Item>();
+    private PopupWindow mPopupWindow;
+    private ListView mContentList;
+    private OnPopupItemClickListener mOnPopupItemClickListener;
+    private int mPopupOffsetX;
+    private int mPopupOffsetY;
+    private int mPopupWidth;
+    private int mPopupHeight;
+
+    public PopupList(Context context, View anchorView) {
+        mContext = context;
+        mAnchorView = anchorView;
+    }
+
+    public void setOnPopupItemClickListener(OnPopupItemClickListener listener) {
+        mOnPopupItemClickListener = listener;
+    }
+
+    public void addItem(int id, String title) {
+        mItems.add(new Item(id, title));
+    }
+
+    private final PopupWindow.OnDismissListener mOnDismissListener =
+            new PopupWindow.OnDismissListener() {
+        @SuppressWarnings("deprecation")
+        @Override
+        public void onDismiss() {
+            if (mPopupWindow == null) return;
+            mPopupWindow = null;
+            ViewTreeObserver observer = mAnchorView.getViewTreeObserver();
+            if (observer.isAlive()) {
+                // We used the deprecated function for backward compatibility
+                // The new "removeOnGlobalLayoutListener" is introduced in API level 16
+                observer.removeGlobalOnLayoutListener(mOnGLobalLayoutListener);
+            }
+        }
+    };
+
+    private final OnItemClickListener mOnItemClickListener =
+            new OnItemClickListener() {
+        @Override
+        public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
+            Utils.debug("onItemClick: %s, %s", position, id);
+            if (mPopupWindow == null) return;
+            mPopupWindow.dismiss();
+            if (mOnPopupItemClickListener != null) {
+                mOnPopupItemClickListener.onPopupItemClick((int) id);
+            }
+        }
+    };
+
+    private final OnGlobalLayoutListener mOnGLobalLayoutListener =
+            new OnGlobalLayoutListener() {
+        @Override
+        public void onGlobalLayout() {
+            if (mPopupWindow == null) return;
+            updatePopupLayoutParams();
+            // Need to update the position of the popup window
+            mPopupWindow.update(mAnchorView,
+                    mPopupOffsetX, mPopupOffsetY, mPopupWidth, mPopupHeight);
+        }
+    };
+
+    public void show() {
+        if (mPopupWindow != null) return;
+        mAnchorView.getViewTreeObserver()
+                .addOnGlobalLayoutListener(mOnGLobalLayoutListener);
+        mPopupWindow = createPopupWindow();
+        updatePopupLayoutParams();
+        mPopupWindow.setWidth(mPopupWidth);
+        mPopupWindow.setHeight(mPopupHeight);
+        mPopupWindow.showAsDropDown(mAnchorView, mPopupOffsetX, mPopupOffsetY);
+    }
+
+    private void updatePopupLayoutParams() {
+        ListView content = mContentList;
+        PopupWindow popup = mPopupWindow;
+
+        Rect p = new Rect();
+        popup.getBackground().getPadding(p);
+
+        int maxHeight = mPopupWindow.getMaxAvailableHeight(mAnchorView) - p.top - p.bottom;
+        mContentList.measure(
+                MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED),
+                MeasureSpec.makeMeasureSpec(maxHeight, MeasureSpec.AT_MOST));
+        mPopupWidth = content.getMeasuredWidth() + p.top + p.bottom;
+        mPopupHeight = Math.min(maxHeight, content.getMeasuredHeight() + p.left + p.right);
+        mPopupOffsetX = -p.left;
+        mPopupOffsetY = -p.top;
+    }
+
+    private PopupWindow createPopupWindow() {
+        PopupWindow popup = new PopupWindow();
+        popup.setOnDismissListener(mOnDismissListener);
+
+        popup.setBackgroundDrawable(mContext.getResources().getDrawable(
+                R.drawable.menu_dropdown_panel_holo_dark));
+        mContentList = new ListView(mContext);
+        mContentList.setAdapter(new ItemDataAdapter());
+        mContentList.setOnItemClickListener(mOnItemClickListener);
+        popup.setContentView(mContentList);
+        popup.setFocusable(true);
+        popup.setOutsideTouchable(true);
+
+        return popup;
+    }
+
+    public Item findItem(int id) {
+        for (Item item : mItems) {
+            if (item.id == id) return item;
+        }
+        return null;
+    }
+
+    private class ItemDataAdapter extends BaseAdapter {
+        @Override
+        public int getCount() {
+            return mItems.size();
+        }
+
+        @Override
+        public Object getItem(int position) {
+            return mItems.get(position);
+        }
+
+        @Override
+        public long getItemId(int position) {
+            return mItems.get(position).id;
+        }
+
+        @Override
+        public View getView(int position, View convertView, ViewGroup parent) {
+            if (convertView == null) {
+                convertView = LayoutInflater.from(mContext)
+                        .inflate(R.layout.popup_list_item, null);
+            }
+            TextView text = (TextView) convertView.findViewById(android.R.id.text1);
+            text.setText(mItems.get(position).title);
+            return convertView;
+        }
+    }
+}
index 1ff633a..aa68d19 100644 (file)
@@ -18,8 +18,8 @@ package com.android.gallery3d.ui;
 
 import android.content.Context;
 import android.view.ViewConfiguration;
-import android.widget.OverScroller;
 
+import com.android.gallery3d.common.OverScroller;
 import com.android.gallery3d.common.Utils;
 
 public class ScrollerHelper {
diff --git a/src/com/android/gallery3d/ui/SelectionMenu.java b/src/com/android/gallery3d/ui/SelectionMenu.java
new file mode 100644 (file)
index 0000000..5b08283
--- /dev/null
@@ -0,0 +1,61 @@
+/*
+ * 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.ui;
+
+import android.content.Context;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.widget.Button;
+
+import com.android.gallery3d.R;
+import com.android.gallery3d.ui.PopupList.OnPopupItemClickListener;
+
+public class SelectionMenu implements OnClickListener {
+    @SuppressWarnings("unused")
+    private static final String TAG = "SelectionMenu";
+
+    private final Context mContext;
+    private final Button mButton;
+    private final PopupList mPopupList;
+
+    public SelectionMenu(Context context, Button button, OnPopupItemClickListener listener) {
+        mContext = context;
+        mButton = button;
+        mPopupList = new PopupList(context, mButton);
+        mPopupList.addItem(R.id.action_select_all,
+                context.getString(R.string.select_all));
+        mPopupList.setOnPopupItemClickListener(listener);
+        mButton.setOnClickListener(this);
+    }
+
+    @Override
+    public void onClick(View v) {
+        mPopupList.show();
+    }
+
+    public void updateSelectAllMode(boolean inSelectAllMode) {
+        PopupList.Item item = mPopupList.findItem(R.id.action_select_all);
+        if (item != null) {
+            item.setTitle(mContext.getString(
+                    inSelectAllMode ? R.string.deselect_all : R.string.select_all));
+        }
+    }
+
+    public void setTitle(CharSequence title) {
+        mButton.setText(title);
+    }
+}