blob: 819fde7ea3fc523c1dcd6dd841e19fe4cb5496df [file] [log] [blame]
package com.android.camera.ui;
import static com.android.camera.ui.GLRootView.dpToPixel;
import java.util.ArrayList;
import android.content.Context;
import android.content.SharedPreferences;
import android.content.SharedPreferences.Editor;
import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
import android.graphics.Rect;
import android.hardware.Camera.Parameters;
import android.os.Handler;
import android.os.Message;
import android.preference.PreferenceManager;
import android.util.DisplayMetrics;
import android.view.MotionEvent;
import android.view.View.MeasureSpec;
import android.view.animation.AlphaAnimation;
import android.view.animation.Animation;
import com.android.camera.CameraSettings;
import com.android.camera.IconListPreference;
import com.android.camera.ListPreference;
import com.android.camera.PreferenceGroup;
import com.android.camera.R;
// This is the UI for the on-screen settings. Since the rendering is run in the
// GL thread. If any values will be changed in the main thread, it needs to
// synchronize on the <code>GLRootView</code> instance.
public class HeadUpDisplay extends GLView {
private static final int INDICATOR_BAR_TIMEOUT = 5500;
private static final int POPUP_WINDOW_TIMEOUT = 5000;
private static final int INDICATOR_BAR_RIGHT_MARGIN = 10;
private static final int POPUP_WINDOW_OVERLAP = 20;
private static final int POPUP_TRIANGLE_OFFSET = 16;
private static final float MAX_HEIGHT_RATIO = 0.8f;
private static final float MAX_WIDTH_RATIO = 0.8f;
private static final int DESELECT_INDICATOR = 0;
private static final int DEACTIVATE_INDICATOR_BAR = 1;
private static int sIndicatorBarRightMargin = -1;
private static int sPopupWindowOverlap;
private static int sPopupTriangleOffset;
private static final String TAG = "HeadUpDisplay";
protected IndicatorBar mIndicatorBar;
private SharedPreferences mSharedPrefs;
private PreferenceGroup mPreferenceGroup;
private PopupWindow mPopupWindow;
private GLView mAnchorView;
private int mOrientation = 0;
private boolean mEnabled = true;
protected Listener mListener;
private Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
GLRootView root = getGLRootView();
if (root != null) {
synchronized (root) {
handleMessageLocked(msg);
}
} else {
handleMessageLocked(msg);
}
}
private void handleMessageLocked(Message msg) {
switch(msg.what) {
case DESELECT_INDICATOR:
mIndicatorBar.setSelectedIndex(IndicatorBar.INDEX_NONE);
break;
case DEACTIVATE_INDICATOR_BAR:
if (mIndicatorBar != null) {
mIndicatorBar.setActivated(false);
}
break;
}
}
};
private final OnSharedPreferenceChangeListener mSharedPreferenceChangeListener =
new OnSharedPreferenceChangeListener() {
public void onSharedPreferenceChanged(
SharedPreferences sharedPreferences, String key) {
if (mListener != null) {
mListener.onSharedPreferencesChanged();
}
}
};
public HeadUpDisplay(Context context) {
initializeStaticVariables(context);
}
private static void initializeStaticVariables(Context context) {
if (sIndicatorBarRightMargin >= 0) return;
sIndicatorBarRightMargin = dpToPixel(context, INDICATOR_BAR_RIGHT_MARGIN);
sPopupWindowOverlap = dpToPixel(context, POPUP_WINDOW_OVERLAP);
sPopupTriangleOffset = dpToPixel(context, POPUP_TRIANGLE_OFFSET);
}
/**
* The callback interface. All the callbacks will be called from the
* GLThread.
*/
static public interface Listener {
public void onPopupWindowVisibilityChanged(int visibility);
public void onRestorePreferencesClicked();
public void onSharedPreferencesChanged();
}
public void overrideSettings(final String ... keyvalues) {
GLRootView root = getGLRootView();
if (root != null) {
synchronized (root) {
overrideSettingsLocked(keyvalues);
}
} else {
overrideSettingsLocked(keyvalues);
}
}
public void overrideSettingsLocked(final String ... keyvalues) {
if (keyvalues.length % 2 != 0) {
throw new IllegalArgumentException();
}
for (int i = 0, n = keyvalues.length; i < n; i += 2) {
mIndicatorBar.overrideSettings(keyvalues[i], keyvalues[i + 1]);
}
}
@Override
protected void onLayout(
boolean changed, int left, int top, int right, int bottom) {
int width = right - left;
int height = bottom - top;
mIndicatorBar.measure(
MeasureSpec.makeMeasureSpec(width / 3, MeasureSpec.AT_MOST),
MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY));
DisplayMetrics metrics = getGLRootView().getDisplayMetrics();
int rightMargin = (int) (metrics.density * INDICATOR_BAR_RIGHT_MARGIN);
mIndicatorBar.layout(
width - mIndicatorBar.getMeasuredWidth() - rightMargin, 0,
width - rightMargin, height);
if(mPopupWindow != null
&& mPopupWindow.getVisibility() == GLView.VISIBLE) {
layoutPopupWindow(mAnchorView);
}
}
public void initialize(Context context, PreferenceGroup preferenceGroup) {
mPreferenceGroup = preferenceGroup;
mSharedPrefs = PreferenceManager.getDefaultSharedPreferences(context);
mSharedPrefs.registerOnSharedPreferenceChangeListener(
mSharedPreferenceChangeListener);
initializeIndicatorBar(context, preferenceGroup);
}
private void layoutPopupWindow(GLView anchorView) {
mAnchorView = anchorView;
Rect rect = new Rect();
getBoundsOf(anchorView, rect);
int anchorX = rect.left + sPopupWindowOverlap;
int anchorY = (rect.top + rect.bottom) / 2;
int width = (int) (getWidth() * MAX_WIDTH_RATIO + .5);
int height = (int) (getHeight() * MAX_HEIGHT_RATIO + .5);
mPopupWindow.measure(
MeasureSpec.makeMeasureSpec(width, MeasureSpec.AT_MOST),
MeasureSpec.makeMeasureSpec(height, MeasureSpec.AT_MOST));
width = mPopupWindow.getMeasuredWidth();
height = mPopupWindow.getMeasuredHeight();
int xoffset = Math.max(anchorX - width, 0);
int yoffset = Math.max(0, anchorY - height / 2);
if (yoffset + height > getHeight()) {
yoffset = getHeight() - height;
}
mPopupWindow.setAnchorPosition(anchorY - yoffset);
mPopupWindow.layout(
xoffset, yoffset, xoffset + width, yoffset + height);
}
private void showPopupWindow(GLView anchorView) {
layoutPopupWindow(anchorView);
mPopupWindow.popup();
mSharedPrefs.registerOnSharedPreferenceChangeListener(
mSharedPreferenceChangeListener);
if (mListener != null) {
mListener.onPopupWindowVisibilityChanged(GLView.VISIBLE);
}
}
private void hidePopupWindow() {
mPopupWindow.popoff();
mSharedPrefs.unregisterOnSharedPreferenceChangeListener(
mSharedPreferenceChangeListener);
if (mListener != null) {
mListener.onPopupWindowVisibilityChanged(GLView.INVISIBLE);
}
}
private void scheduleDeactiviateIndicatorBar() {
mHandler.removeMessages(DESELECT_INDICATOR);
mHandler.sendEmptyMessageDelayed(
DESELECT_INDICATOR, POPUP_WINDOW_TIMEOUT);
mHandler.removeMessages(DEACTIVATE_INDICATOR_BAR);
mHandler.sendEmptyMessageDelayed(
DEACTIVATE_INDICATOR_BAR, INDICATOR_BAR_TIMEOUT);
}
public void setOrientation(int orientation) {
GLRootView root = getGLRootView();
if (root != null) {
synchronized (root) {
setOrientationLocked(orientation);
}
} else {
setOrientationLocked(orientation);
}
}
private void setOrientationLocked(int orientation) {
mOrientation = orientation;
mIndicatorBar.setOrientation(orientation);
if (mPopupWindow == null) return;
if (mPopupWindow.getVisibility() == GLView.VISIBLE) {
Animation alpha = new AlphaAnimation(0.2f, 1);
alpha.setDuration(250);
mPopupWindow.startAnimation(alpha);
scheduleDeactiviateIndicatorBar();
}
mPopupWindow.setOrientation(orientation);
}
private void initializePopupWindow(Context context) {
mPopupWindow = new PopupWindow();
mPopupWindow.setBackground(
new NinePatchTexture(context, R.drawable.menu_popup));
mPopupWindow.setAnchor(new ResourceTexture(
context, R.drawable.menu_popup_triangle), sPopupTriangleOffset);
mPopupWindow.setVisibility(GLView.INVISIBLE);
mPopupWindow.setOrientation(mOrientation);
addComponent(mPopupWindow);
}
@Override
protected boolean dispatchTouchEvent(MotionEvent event) {
if (mEnabled && super.dispatchTouchEvent(event)) {
scheduleDeactiviateIndicatorBar();
return true;
}
return false;
}
public void setEnabled(boolean enabled) {
// The mEnabled variable is not related to the rendering thread, so we
// don't need to synchronize on the GLRootView.
if (mEnabled == enabled) return;
mEnabled = enabled;
}
@Override
protected boolean onTouch(MotionEvent event) {
if (mPopupWindow == null
|| mPopupWindow.getVisibility() == GLView.INVISIBLE) {
return false;
}
switch (event.getAction()) {
case MotionEvent.ACTION_UP:
hidePopupWindow();
mIndicatorBar.setSelectedIndex(IndicatorBar.INDEX_NONE);
mIndicatorBar.setActivated(false);
break;
}
return true;
}
protected static ListPreference[] getListPreferences(
PreferenceGroup group, String ... prefKeys) {
ArrayList<ListPreference> list = new ArrayList<ListPreference>();
for (String key : prefKeys) {
ListPreference pref = group.findPreference(key);
if (pref != null && pref.getEntries().length > 0) {
list.add(pref);
}
}
return list.toArray(new ListPreference[list.size()]);
}
protected BasicIndicator addIndicator(
Context context, PreferenceGroup group, String key) {
IconListPreference iconPref =
(IconListPreference) group.findPreference(key);
if (iconPref == null) return null;
BasicIndicator indicator = new BasicIndicator(context, group, iconPref);
mIndicatorBar.addComponent(indicator);
return indicator;
}
protected void initializeIndicatorBar(
Context context, PreferenceGroup group) {
mIndicatorBar = new IndicatorBar();
mIndicatorBar.setBackground(new NinePatchTexture(
context, R.drawable.ic_viewfinder_iconbar));
mIndicatorBar.setHighlight(new NinePatchTexture(
context, R.drawable.ic_viewfinder_iconbar_highlight));
addComponent(mIndicatorBar);
mIndicatorBar.setOnItemSelectedListener(new IndicatorBarListener());
}
private class IndicatorBarListener
implements IndicatorBar.OnItemSelectedListener {
public void onItemSelected(GLView view, int position) {
AbstractIndicator indicator = (AbstractIndicator) view;
if (mPopupWindow == null) {
initializePopupWindow(getGLRootView().getContext());
}
mPopupWindow.setContent(indicator.getPopupContent());
if (mPopupWindow.getVisibility() == GLView.VISIBLE) {
layoutPopupWindow(indicator);
} else {
showPopupWindow(indicator);
}
}
public void onNothingSelected() {
hidePopupWindow();
}
}
public boolean collapse() {
// We don't need to synchronize on GLRootView, since both the
// <code>isActivated()</code> and rendering thread are read-only to
// the variables inside.
if (!mIndicatorBar.isActivated()) return false;
mHandler.removeMessages(DESELECT_INDICATOR);
mHandler.removeMessages(DEACTIVATE_INDICATOR_BAR);
GLRootView root = getGLRootView();
if (root != null) {
synchronized (root) {
mIndicatorBar.setSelectedIndex(IndicatorBar.INDEX_NONE);
mIndicatorBar.setActivated(false);
}
} else {
mIndicatorBar.setSelectedIndex(IndicatorBar.INDEX_NONE);
mIndicatorBar.setActivated(false);
}
return true;
}
public void setListener(Listener listener) {
// No synchronization: mListener won't be accessed in rendering thread
mListener = listener;
}
public void restorePreferences(final Parameters param) {
// Do synchronization in "reloadPreferences()"
OnSharedPreferenceChangeListener l =
mSharedPreferenceChangeListener;
// Unregister the listener since "upgrade preference" will
// change bunch of preferences. We can handle them with one
// onSharedPreferencesChanged();
mSharedPrefs.unregisterOnSharedPreferenceChangeListener(l);
Context context = getGLRootView().getContext();
Editor editor = mSharedPrefs.edit();
editor.clear();
editor.commit();
CameraSettings.upgradePreferences(mSharedPrefs);
CameraSettings.initialCameraPictureSize(context, param);
reloadPreferences();
if (mListener != null) {
mListener.onSharedPreferencesChanged();
}
mSharedPrefs.registerOnSharedPreferenceChangeListener(l);
}
public void reloadPreferences() {
GLRootView root = getGLRootView();
if (root != null) {
synchronized (root) {
mPreferenceGroup.reloadValue();
mIndicatorBar.reloadPreferences();
}
} else {
mPreferenceGroup.reloadValue();
mIndicatorBar.reloadPreferences();
}
}
}