LOCAL_OVERRIDES_PACKAGES := Gallery Gallery3D GalleryNew3D
-LOCAL_SDK_VERSION := current
+LOCAL_SDK_VERSION := 16
LOCAL_JNI_SHARED_LIBRARIES := libjni_mosaic libjni_eglfence
<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" />
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)
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;
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);
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 {
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;
- }
-
}
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)
-
#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);
}
android:gravity="center_vertical"
android:paddingLeft="18dp"
android:paddingRight="18dp"
- android:minHeight="?android:attr/listPreferredItemHeightSmall"
+ android:minHeight="?attr/listPreferredItemHeightSmall"
/>
--- /dev/null
+<?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"
+/>
--- /dev/null
+<?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>
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>
<?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.
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>
-->
<resources>
<item type="id" name="action_toggle_full_caching" />
+ <item type="id" name="action_select_all" />
</resources>
-->
<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>
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;
public Intent resultData;
}
+ protected boolean mHapticsEnabled;
+ private ContentResolver mContentResolver;
+
private boolean mDestroyed = false;
private boolean mPlugged = false;
boolean mIsFinishing = false;
void initialize(GalleryActivity activity, Bundle data) {
mActivity = activity;
mData = data;
+ mContentResolver = activity.getAndroidContext().getContentResolver();
}
public Bundle getData() {
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;
switch (mode) {
case SelectionManager.ENTER_SELECTION_MODE: {
mActionMode = mActionModeHandler.startActionMode();
- mVibrator.vibrate(100);
+ if(mHapticsEnabled) mVibrator.vibrate(100);
break;
}
case SelectionManager.LEAVE_SELECTION_MODE: {
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: {
package com.android.gallery3d.data;
+import android.annotation.TargetApi;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.Context;
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;
* 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";
package com.android.gallery3d.data;
+import android.annotation.TargetApi;
import android.content.Context;
import android.media.MediaScannerConnection;
import android.media.MediaScannerConnection.MediaScannerConnectionClient;
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;
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";
package com.android.gallery3d.data;
+import android.annotation.TargetApi;
import android.hardware.usb.UsbDevice;
import android.mtp.MtpConstants;
import android.mtp.MtpObjectInfo;
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";
package com.android.gallery3d.data;
+import android.annotation.TargetApi;
import android.mtp.MtpDeviceInfo;
import android.net.Uri;
import android.os.Handler;
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;
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";
package com.android.gallery3d.data;
+import android.annotation.TargetApi;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapRegionDecoder;
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;
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";
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;
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;
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;
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
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;
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;
}
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() {
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) {
+++ /dev/null
-/*
- * 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;
- }
-}
public boolean switchWithCaptureAnimation(int offset) {
GLRoot root = getGLRoot();
+ if(root == null) return false;
root.lockRenderThread();
try {
return switchWithCaptureAnimationLocked(offset);
--- /dev/null
+// 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;
+ }
+ }
+}
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 {
--- /dev/null
+/*
+ * 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);
+ }
+}