1 package com.android.camera; 2 3 import com.android.gallery.R; 4 5 import android.content.Context; 6 import android.graphics.PixelFormat; 7 import android.os.Handler; 8 import android.os.Message; 9 import android.preference.ListPreference; 10 import android.preference.Preference; 11 import android.preference.PreferenceGroup; 12 import android.preference.PreferenceScreen; 13 import android.view.animation.Animation; 14 import android.view.animation.Animation.AnimationListener; 15 import android.view.Display; 16 import android.view.Gravity; 17 import android.view.KeyEvent; 18 import android.view.LayoutInflater; 19 import android.view.MotionEvent; 20 import android.view.View; 21 import android.view.ViewGroup; 22 import android.view.WindowManager; 23 import android.view.WindowManager.LayoutParams; 24 import android.widget.AdapterView; 25 import android.widget.BaseAdapter; 26 import android.widget.FrameLayout; 27 import android.widget.ImageView; 28 import android.widget.ListView; 29 import android.widget.RadioButton; 30 import android.widget.TextView; 31 import android.widget.AdapterView.OnItemClickListener; 32 33 import java.util.ArrayList; 34 import java.util.HashMap; 35 36 // Please reference to {@link android.widget.ZoomButtonsController} for detail 37 // information about adding window to WindowManager. 38 public class OnScreenSettings { 39 @SuppressWarnings("unused") 40 private static final String TAG = "OnScreenSettings"; 41 private static final int MSG_POST_SET_VISIBLE = 1; 42 43 public interface OnVisibilityChangedListener { onVisibilityChanged(boolean visibility)44 public void onVisibilityChanged(boolean visibility); 45 } 46 47 private LayoutParams mContainerLayoutParams; 48 private final Context mContext; 49 private final Container mContainer; 50 private final WindowManager mWindowManager; 51 private final View mOwnerView; 52 private ListView mMainMenu; 53 private ListView mSubMenu; 54 private View mMainPanel; 55 private boolean mIsVisible = false; 56 private OnVisibilityChangedListener mVisibilityListener; 57 private MainMenuAdapter mMainAdapter; 58 59 private final LayoutInflater mInflater; 60 61 // We store the override values here. For a given preference, 62 // if the mapping value of the preference key is not null, we will 63 // use the value in this map instead of the value read from the preference 64 // 65 // This is design for the scene mode, for example, in the scene mode 66 // "Action", the focus mode will become "infinite" no matter what in the 67 // preference settings. So, we need to put a {pref_camera_focusmode_key, 68 // "infinite"} entry in this map. 69 private HashMap<String, String> mOverride = new HashMap<String, String>(); 70 71 private final Handler mHandler = new Handler() { 72 @Override 73 public void handleMessage(Message msg) { 74 switch (msg.what) { 75 case MSG_POST_SET_VISIBLE: 76 setVisible(true); 77 break; 78 } 79 } 80 }; 81 OnScreenSettings(View ownerView)82 public OnScreenSettings(View ownerView) { 83 mContext = ownerView.getContext(); 84 mInflater = (LayoutInflater) 85 mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE); 86 87 mWindowManager = (WindowManager) 88 mContext.getSystemService(Context.WINDOW_SERVICE); 89 mOwnerView = ownerView; 90 mContainer = createContainer(); 91 } 92 isVisible()93 public boolean isVisible() { 94 return mIsVisible; 95 } 96 setOnVisibilityChangedListener( OnVisibilityChangedListener listener)97 public void setOnVisibilityChangedListener( 98 OnVisibilityChangedListener listener) { 99 mVisibilityListener = listener; 100 } 101 setVisible(boolean visible)102 public void setVisible(boolean visible) { 103 mHandler.removeMessages(MSG_POST_SET_VISIBLE); 104 if (visible) { 105 if (mOwnerView.getWindowToken() == null) { 106 /* 107 * We need a window token to show ourselves, maybe the owner's 108 * window hasn't been created yet but it will have been by the 109 * time the looper is idle, so post the setVisible(true) call. 110 */ 111 mHandler.sendEmptyMessage(MSG_POST_SET_VISIBLE); 112 return; 113 } 114 } 115 116 if (mIsVisible == visible) { 117 return; 118 } 119 mIsVisible = visible; 120 121 if (visible) { 122 // Update main adapter before show up 123 if (mMainAdapter != null) mMainAdapter.notifyDataSetChanged(); 124 if (mContainerLayoutParams.token == null) { 125 mContainerLayoutParams.token = mOwnerView.getWindowToken(); 126 } 127 mSubMenu.setVisibility(View.INVISIBLE); 128 mMainMenu.setVisibility(View.VISIBLE); 129 mWindowManager.addView(mContainer, mContainerLayoutParams); 130 updateLayout(); 131 } else { 132 // Reset the two menus 133 134 mWindowManager.removeView(mContainer); 135 } 136 if (mVisibilityListener != null) { 137 mVisibilityListener.onVisibilityChanged(mIsVisible); 138 } 139 } 140 141 // Override the preference settings, if value == null, then disable the 142 // override. overrideSettings(String key, String value)143 public void overrideSettings(String key, String value) { 144 if (value == null) { 145 if (mOverride.remove(key) != null && mMainAdapter != null) { 146 mMainAdapter.notifyDataSetChanged(); 147 } 148 } else { 149 if (mOverride.put(key, value) == null && mMainAdapter != null) { 150 mMainAdapter.notifyDataSetChanged(); 151 } 152 } 153 } 154 updateLayout()155 public void updateLayout() { 156 // if the mOwnerView is detached from window then skip. 157 if (mOwnerView.getWindowToken() == null) return; 158 Display display = mWindowManager.getDefaultDisplay(); 159 160 mContainerLayoutParams.x = 0; 161 mContainerLayoutParams.y = 0; 162 163 mContainerLayoutParams.width = display.getWidth() / 2; 164 mContainerLayoutParams.height = display.getHeight(); 165 166 if (mIsVisible) { 167 mWindowManager.updateViewLayout(mContainer, mContainerLayoutParams); 168 } 169 } 170 showSubMenu()171 private void showSubMenu() { 172 Util.slideOut(mMainMenu, Util.DIRECTION_LEFT); 173 Util.slideIn(mSubMenu, Util.DIRECTION_RIGHT); 174 mSubMenu.requestFocus(); 175 } 176 closeSubMenu()177 private void closeSubMenu() { 178 Util.slideOut(mSubMenu, Util.DIRECTION_RIGHT); 179 Util.slideIn(mMainMenu, Util.DIRECTION_LEFT); 180 } 181 createContainer()182 private Container createContainer() { 183 LayoutParams lp = new LayoutParams( 184 LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT); 185 lp.flags = LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH; 186 lp.gravity = Gravity.TOP | Gravity.LEFT; 187 lp.height = LayoutParams.WRAP_CONTENT; 188 lp.width = LayoutParams.WRAP_CONTENT; 189 lp.type = LayoutParams.TYPE_APPLICATION_PANEL; 190 lp.format = PixelFormat.OPAQUE; 191 lp.windowAnimations = R.style.Animation_OnScreenMenu; 192 193 mContainerLayoutParams = lp; 194 195 Container container = new Container(mContext); 196 container.setLayoutParams(lp); 197 198 mInflater.inflate(R.layout.on_screen_menu, container); 199 200 mMainPanel = container.findViewById(R.id.main_panel); 201 mMainMenu = (ListView) container.findViewById(R.id.menu_view); 202 mSubMenu = (ListView) container.findViewById(R.id.sub_menu); 203 204 container.findViewById(R.id.btn_gripper) 205 .setOnTouchListener(new GripperTouchListener()); 206 207 return container; 208 } 209 210 private class GripperTouchListener implements View.OnTouchListener { onTouch(View view, MotionEvent event)211 public boolean onTouch(View view, MotionEvent event) { 212 switch (event.getAction()) { 213 case MotionEvent.ACTION_DOWN: 214 return true; 215 case MotionEvent.ACTION_UP: 216 setVisible(false); 217 return true; 218 } 219 return false; 220 } 221 } 222 onContainerKey(KeyEvent event)223 private boolean onContainerKey(KeyEvent event) { 224 switch (event.getKeyCode()) { 225 case KeyEvent.KEYCODE_CAMERA: 226 case KeyEvent.KEYCODE_FOCUS: 227 case KeyEvent.KEYCODE_BACK: 228 case KeyEvent.KEYCODE_MENU: 229 if (event.getAction() == KeyEvent.ACTION_UP) { 230 setVisible(false); 231 return true; 232 } 233 } 234 return false; 235 } 236 237 // Add the preference and it's children recursively to the given list. So 238 // that we can show the preference (and it's children) in the list view. addPreference( Preference preference, ArrayList<Preference> list)239 private static void addPreference( 240 Preference preference, ArrayList<Preference> list) { 241 list.add(preference); 242 if (preference instanceof PreferenceGroup) { 243 PreferenceGroup group = (PreferenceGroup) preference; 244 for (int i = 0, n = group.getPreferenceCount(); i < n; ++i) { 245 Preference child = group.getPreference(i); 246 addPreference(child, list); 247 } 248 } 249 } 250 setPreferenceScreen(PreferenceScreen screen)251 public void setPreferenceScreen(PreferenceScreen screen) { 252 ArrayList<Preference> list = new ArrayList<Preference>(); 253 254 // We don't want the screen add to the list, we add the first level 255 // preference here. 256 for (int i = 0, n = screen.getPreferenceCount(); i < n; ++i) { 257 addPreference(screen.getPreference(i), list); 258 } 259 mMainAdapter = new MainMenuAdapter(mContext, list); 260 mMainMenu.setAdapter(mMainAdapter); 261 mMainMenu.setOnItemClickListener(mMainAdapter); 262 } 263 inflateIfNeed( View view, int resource, ViewGroup root, boolean attachToRoot)264 private View inflateIfNeed( 265 View view, int resource, ViewGroup root, boolean attachToRoot) { 266 if (view != null) return view; 267 return mInflater.inflate(resource, root, attachToRoot); 268 } 269 270 private class MainMenuAdapter extends BaseAdapter 271 implements OnItemClickListener { 272 private final ArrayList<Preference> mPreferences; 273 MainMenuAdapter( Context context, ArrayList<Preference> preferences)274 public MainMenuAdapter( 275 Context context, ArrayList<Preference> preferences) { 276 mPreferences = preferences; 277 } 278 onItemClick( AdapterView<?> parent, View view, int position, long id)279 public void onItemClick( 280 AdapterView<?> parent, View view, int position, long id) { 281 Preference preference = mPreferences.get(position); 282 SubMenuAdapter adapter = new SubMenuAdapter( 283 mContext, (ListPreference) preference); 284 mSubMenu.setAdapter(adapter); 285 mSubMenu.setOnItemClickListener(adapter); 286 showSubMenu(); 287 } 288 getView(int position, View convertView, ViewGroup parent)289 public View getView(int position, View convertView, ViewGroup parent) { 290 Preference preference = mPreferences.get(position); 291 292 if (preference instanceof PreferenceGroup) { 293 convertView = inflateIfNeed(convertView, 294 R.layout.on_screen_menu_header, parent, false); 295 PreferenceGroup group = (PreferenceGroup) preference; 296 ((TextView) convertView.findViewById( 297 R.id.title)).setText(group.getTitle()); 298 } else { 299 convertView = inflateIfNeed(convertView, 300 R.layout.on_screen_menu_list_item, parent, false); 301 302 String override = mOverride.get(preference.getKey()); 303 TextView title = (TextView) 304 convertView.findViewById(R.id.title); 305 title.setText(preference.getTitle()); 306 title.setEnabled(override == null); 307 308 TextView summary = (TextView) 309 convertView.findViewById(R.id.summary); 310 summary.setText(override == null 311 ? ((ListPreference) preference).getEntry() 312 : override); 313 summary.setEnabled(override == null); 314 315 // A little trick here, making the view focusable will eat 316 // both touch/key events on the view and thus make it looks 317 // like disabled. 318 convertView.setFocusable(override != null); 319 } 320 return convertView; 321 } 322 323 @Override areAllItemsEnabled()324 public boolean areAllItemsEnabled() { 325 return false; 326 } 327 328 @Override isEnabled(int position)329 public boolean isEnabled(int position) { 330 Preference preference = mPreferences.get(position); 331 return !(preference instanceof PreferenceGroup); 332 } 333 getCount()334 public int getCount() { 335 return mPreferences.size(); 336 } 337 getItem(int position)338 public Object getItem(int position) { 339 return null; 340 } 341 getItemId(int position)342 public long getItemId(int position) { 343 return position; 344 } 345 346 @Override getItemViewType(int position)347 public int getItemViewType(int position) { 348 Preference pref = mPreferences.get(position); 349 if (pref instanceof PreferenceGroup) return 0; 350 if (pref instanceof ListPreference) return 1; 351 throw new IllegalStateException(); 352 } 353 354 @Override getViewTypeCount()355 public int getViewTypeCount() { 356 // we have two types, see getItemViewType() 357 return 2; 358 } 359 360 @Override hasStableIds()361 public boolean hasStableIds() { 362 return true; 363 } 364 365 @Override isEmpty()366 public boolean isEmpty() { 367 return mPreferences.isEmpty(); 368 } 369 } 370 371 private class SubMenuAdapter extends BaseAdapter 372 implements OnItemClickListener { 373 private final ListPreference mPreference; 374 private final IconListPreference mIconPreference; 375 SubMenuAdapter(Context context, ListPreference preference)376 public SubMenuAdapter(Context context, ListPreference preference) { 377 mPreference = preference; 378 mIconPreference = (preference instanceof IconListPreference) 379 ? (IconListPreference) preference 380 : null; 381 } 382 getView(int position, View convertView, ViewGroup parent)383 public View getView(int position, View convertView, ViewGroup parent) { 384 CharSequence entry[] = mPreference.getEntries(); 385 if (position == 0) { 386 convertView = inflateIfNeed(convertView, 387 R.layout.on_screen_menu_header, parent, false); 388 ((TextView) convertView.findViewById( 389 R.id.title)).setText(mPreference.getDialogTitle()); 390 } else { 391 int index = position - 1; 392 convertView = inflateIfNeed(convertView, 393 R.layout.on_screen_submenu_item, parent, false); 394 boolean checked = mPreference.getValue().equals( 395 mPreference.getEntryValues()[index]); 396 ((TextView) convertView.findViewById( 397 R.id.title)).setText(entry[index]); 398 ((RadioButton) convertView.findViewById( 399 R.id.radio_button)).setChecked(checked); 400 ImageView icon = (ImageView) 401 convertView.findViewById(R.id.icon); 402 if (mIconPreference != null) { 403 icon.setVisibility(View.VISIBLE); 404 icon.setImageDrawable( 405 mIconPreference.getIcons()[position-1]); 406 } else { 407 icon.setVisibility(View.GONE); 408 } 409 } 410 return convertView; 411 } 412 413 @Override areAllItemsEnabled()414 public boolean areAllItemsEnabled() { 415 return false; 416 } 417 418 @Override isEnabled(int position)419 public boolean isEnabled(int position) { 420 return getItemViewType(position) != 0; 421 } 422 getCount()423 public int getCount() { 424 // add one for the header 425 return mPreference.getEntries().length + 1; 426 } 427 getItem(int position)428 public Object getItem(int position) { 429 return null; 430 } 431 getItemId(int position)432 public long getItemId(int position) { 433 return position; 434 } 435 436 @Override getItemViewType(int position)437 public int getItemViewType(int position) { 438 return position == 0 ? 0 : 1; 439 } 440 441 @Override getViewTypeCount()442 public int getViewTypeCount() { 443 return 2; 444 } 445 446 @Override hasStableIds()447 public boolean hasStableIds() { 448 return true; 449 } 450 onItemClick( AdapterView<?> parent, View view, int position, long id)451 public void onItemClick( 452 AdapterView<?> parent, View view, int position, long id) { 453 CharSequence values[] = mPreference.getEntryValues(); 454 int idx = mPreference.findIndexOfValue(mPreference.getValue()); 455 if (idx != position - 1) { 456 mPreference.setValueIndex(position - 1); 457 notifyDataSetChanged(); 458 mMainAdapter.notifyDataSetChanged(); 459 return; 460 } 461 462 // Close the sub menu when user presses the original option. 463 closeSubMenu(); 464 } 465 } 466 467 private class Container extends FrameLayout { Container(Context context)468 public Container(Context context) { 469 super(context); 470 } 471 472 @Override onTouchEvent(MotionEvent event)473 public boolean onTouchEvent(MotionEvent event) { 474 if (super.onTouchEvent(event)) return true; 475 if (event.getAction() == MotionEvent.ACTION_DOWN) { 476 setVisible(false); 477 return true; 478 } 479 return false; 480 } 481 482 /* 483 * Need to override this to intercept the key events. Otherwise, we 484 * would attach a key listener to the container but its superclass 485 * ViewGroup gives it to the focused View instead of calling the key 486 * listener, and so we wouldn't get the events. 487 */ 488 @Override dispatchKeyEvent(KeyEvent event)489 public boolean dispatchKeyEvent(KeyEvent event) { 490 return onContainerKey(event) 491 ? true 492 : super.dispatchKeyEvent(event); 493 } 494 } 495 } 496