1 /* 2 * Copyright (C) 2010 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.camera.ui; 18 19 import android.content.Context; 20 import android.content.res.Configuration; 21 import android.util.AttributeSet; 22 import android.view.MotionEvent; 23 import android.view.View; 24 25 import com.android.camera.IconListPreference; 26 import com.android.camera.PreferenceGroup; 27 import com.android.camera.R; 28 import com.android.camera.Util; 29 30 /** 31 * A view that contains camera setting indicators which are spread over a 32 * vertical bar in preview frame. 33 */ 34 public class SecondLevelIndicatorControlBar extends IndicatorControl implements 35 View.OnClickListener, AbstractIndicatorButton.IndicatorChangeListener { 36 @SuppressWarnings("unused") 37 private static final String TAG = "SecondLevelIndicatorControlBar"; 38 private static int ICON_SPACING = Util.dpToPixel(16); 39 private View mCloseIcon; 40 private View mDivider; // the divider line 41 private View mPopupedIndicator; 42 int mOrientation = 0; 43 int mSelectedIndex = -1; 44 // There are some views in the ViewGroup before adding the indicator buttons, 45 // such as Close icon, divider line and the highlight bar, we need to 46 // remember the count of the non-indicator buttons for getTouchViewIndex(). 47 int mNonIndicatorButtonCount; 48 49 public SecondLevelIndicatorControlBar(Context context, AttributeSet attrs) { 50 super(context, attrs); 51 } 52 53 @Override 54 protected void onFinishInflate() { 55 mDivider = findViewById(R.id.divider); 56 mCloseIcon = findViewById(R.id.back_to_first_level); 57 mCloseIcon.setOnClickListener(this); 58 mNonIndicatorButtonCount = getChildCount(); 59 } 60 61 public void initialize(Context context, PreferenceGroup group, 62 String[] keys, String[] otherSettingKeys) { 63 64 setPreferenceGroup(group); 65 66 // Remove the original setting indicators. This happens when switching 67 // between front and back cameras. 68 int count = getChildCount() - mNonIndicatorButtonCount; 69 if (count > 0) removeControls(mNonIndicatorButtonCount, count); 70 71 addControls(keys, otherSettingKeys); 72 if (mOrientation != 0) setOrientation(mOrientation, false); 73 74 // Do not grey out the icons when taking a picture. 75 setupFilter(mCurrentMode != MODE_CAMERA); 76 } 77 78 @Override 79 public void onClick(View view) { 80 dismissSettingPopup(); 81 mOnIndicatorEventListener.onIndicatorEvent( 82 OnIndicatorEventListener.EVENT_LEAVE_SECOND_LEVEL_INDICATOR_BAR); 83 } 84 85 private int getTouchViewIndex(int touchPosition, boolean isLandscape) { 86 // If the current touch location is on close icon and above. 87 if (isLandscape) { 88 if (touchPosition < mCloseIcon.getBottom()) return indexOfChild(mCloseIcon); 89 } else { 90 if (touchPosition > mCloseIcon.getLeft()) return indexOfChild(mCloseIcon); 91 } 92 93 // Calculate if the touch event is on the indicator buttons. 94 int count = getChildCount(); 95 if (count == mNonIndicatorButtonCount) return -1; 96 // The baseline will be the first indicator button's top minus spacing. 97 View firstIndicatorButton = getChildAt(mNonIndicatorButtonCount); 98 if (isLandscape) { 99 int baseline = firstIndicatorButton.getTop() - (ICON_SPACING / 2); 100 if (touchPosition < baseline) return -1; 101 int iconHeight = firstIndicatorButton.getMeasuredHeight(); 102 int buttonRange = iconHeight + ICON_SPACING; 103 return (mNonIndicatorButtonCount + ((touchPosition - baseline) / buttonRange)); 104 } else { 105 int baseline = firstIndicatorButton.getRight() + (ICON_SPACING / 2); 106 if (touchPosition > baseline) return -1; 107 int iconWidth = firstIndicatorButton.getMeasuredWidth(); 108 int buttonRange = iconWidth + ICON_SPACING; 109 return (mNonIndicatorButtonCount + ((baseline - touchPosition) / buttonRange)); 110 } 111 } 112 113 private void dispatchRelativeTouchEvent(View view, MotionEvent event) { 114 event.offsetLocation(-view.getLeft(), -view.getTop()); 115 view.dispatchTouchEvent(event); 116 event.offsetLocation(view.getLeft(), view.getTop()); 117 } 118 119 @Override 120 public boolean dispatchTouchEvent(MotionEvent event) { 121 if (!onFilterTouchEventForSecurity(event)) return false; 122 123 int action = event.getAction(); 124 if (!isEnabled()) return false; 125 126 int index = 0; 127 boolean isLandscape = getResources().getConfiguration().orientation 128 == Configuration.ORIENTATION_LANDSCAPE; 129 // the X (Y) of touch point for portrait (landscape) orientation 130 int touchPosition = (int) (isLandscape ? event.getY() : event.getX()); 131 // second-level indicator control bar width (height) for portrait 132 // (landscape) orientation 133 int controlBarLength = isLandscape ? getHeight() : getWidth(); 134 if (controlBarLength == 0) return false; // the event is sent before onMeasure() 135 if (touchPosition >= controlBarLength) { 136 touchPosition = controlBarLength - 1; 137 } 138 index = getTouchViewIndex(touchPosition, isLandscape); 139 140 // Cancel the previous target if we moved out of it 141 if ((mSelectedIndex != -1) && (index != mSelectedIndex)) { 142 View p = getChildAt(mSelectedIndex); 143 144 int oldAction = event.getAction(); 145 event.setAction(MotionEvent.ACTION_CANCEL); 146 dispatchRelativeTouchEvent(p, event); 147 event.setAction(oldAction); 148 149 if (p instanceof AbstractIndicatorButton) { 150 AbstractIndicatorButton b = (AbstractIndicatorButton) p; 151 b.dismissPopup(); 152 } 153 } 154 155 // Send event to the target 156 View v = getChildAt(index); 157 if (v == null) return true; 158 159 // Change MOVE to DOWN if this is a new target 160 if (mSelectedIndex != index && action == MotionEvent.ACTION_MOVE) { 161 event.setAction(MotionEvent.ACTION_DOWN); 162 } 163 dispatchRelativeTouchEvent(v, event); 164 mSelectedIndex = index; 165 return true; 166 } 167 168 @Override 169 public IndicatorButton addIndicator(Context context, IconListPreference pref) { 170 IndicatorButton b = super.addIndicator(context, pref); 171 b.setBackgroundResource(R.drawable.bg_pressed); 172 b.setIndicatorChangeListener(this); 173 return b; 174 } 175 176 @Override 177 public OtherSettingIndicatorButton addOtherSettingIndicator(Context context, 178 int resId, String[] keys) { 179 OtherSettingIndicatorButton b = 180 super.addOtherSettingIndicator(context, resId, keys); 181 b.setBackgroundResource(R.drawable.bg_pressed); 182 b.setIndicatorChangeListener(this); 183 b.setId(R.id.other_setting_indicator); 184 return b; 185 } 186 187 @Override 188 public void onShowIndicator(View view, boolean showed) { 189 // Ignore those events if not current popup. 190 if (!showed && (mPopupedIndicator != view)) return; 191 mPopupedIndicator = (showed ? view : null); 192 // Show or dismiss the side indicator highlight. 193 requestLayout(); 194 } 195 196 @Override 197 public void setOrientation(int orientation, boolean animation) { 198 mOrientation = orientation; 199 super.setOrientation(orientation, animation); 200 } 201 202 @Override 203 protected void onLayout( 204 boolean changed, int left, int top, int right, int bottom) { 205 int count = getChildCount(); 206 if (count == 0) return; 207 int width = right - left; 208 int height = bottom - top; 209 210 if (getResources().getConfiguration().orientation 211 == Configuration.ORIENTATION_LANDSCAPE) { 212 int iconSize = mCloseIcon.getMeasuredHeight(); 213 int padding = getPaddingTop(); 214 // The first icon is close button. 215 int offset = padding; 216 mCloseIcon.layout(0, offset, width, (offset + iconSize)); 217 // And layout the divider line. 218 offset += (iconSize + padding); 219 mDivider.layout(padding, offset, (width - padding), 220 (offset + mDivider.getMeasuredHeight())); 221 // Layout from the last icon up. 222 int startY = height - iconSize - padding; 223 int decrement = iconSize + ICON_SPACING; 224 for (int i = count - 1; i >= mNonIndicatorButtonCount; --i) { 225 getChildAt(i).layout(0, startY, width, startY + iconSize); 226 startY -= decrement; 227 } 228 } else { 229 int iconSize = mCloseIcon.getMeasuredWidth(); 230 int padding = getPaddingLeft(); 231 // Layout from the last icon up. 232 int offset = padding; 233 int increment = iconSize + ICON_SPACING; 234 for (int i = count - 1; i >= mNonIndicatorButtonCount; --i) { 235 getChildAt(i).layout(offset, 0, offset + iconSize, height); 236 offset += increment; 237 } 238 // And layout the divider line. 239 offset = width - iconSize - 2 * padding; 240 mDivider.layout(offset, padding, 241 (offset + mDivider.getMeasuredWidth()), (height - padding)); 242 offset = width - iconSize - padding; 243 // The first icon is close button. 244 mCloseIcon.layout(offset, 0, (offset + iconSize), height); 245 } 246 } 247 248 @Override 249 public void setEnabled(boolean enabled) { 250 super.setEnabled(enabled); 251 if (mCurrentMode == MODE_VIDEO) { 252 mCloseIcon.setVisibility(enabled ? View.VISIBLE : View.INVISIBLE); 253 } 254 mCloseIcon.setEnabled(enabled); 255 } 256 } 257