1/* 2 * Copyright (C) 2013 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 17package android.support.v7.internal.view.menu; 18 19import android.app.AlertDialog; 20import android.app.Dialog; 21import android.content.DialogInterface; 22import android.os.IBinder; 23import android.support.v7.appcompat.R; 24import android.view.KeyEvent; 25import android.view.View; 26import android.view.Window; 27import android.view.WindowManager; 28 29/** 30 * Helper for menus that appear as Dialogs (context and submenus). 31 * 32 * @hide 33 */ 34public class MenuDialogHelper implements DialogInterface.OnKeyListener, 35 DialogInterface.OnClickListener, 36 DialogInterface.OnDismissListener, 37 MenuPresenter.Callback { 38 private MenuBuilder mMenu; 39 private AlertDialog mDialog; 40 ListMenuPresenter mPresenter; 41 private MenuPresenter.Callback mPresenterCallback; 42 43 public MenuDialogHelper(MenuBuilder menu) { 44 mMenu = menu; 45 } 46 47 /** 48 * Shows menu as a dialog. 49 * 50 * @param windowToken Optional token to assign to the window. 51 */ 52 public void show(IBinder windowToken) { 53 // Many references to mMenu, create local reference 54 final MenuBuilder menu = mMenu; 55 56 // Get the builder for the dialog 57 final AlertDialog.Builder builder = new AlertDialog.Builder(menu.getContext()); 58 59 // Need to force Light Menu theme as list_menu_item_layout is usually against a dark bg and 60 // AlertDialog's bg is white 61 mPresenter = new ListMenuPresenter(R.layout.abc_list_menu_item_layout, 62 R.style.Theme_AppCompat_CompactMenu_Dialog); 63 64 mPresenter.setCallback(this); 65 mMenu.addMenuPresenter(mPresenter); 66 builder.setAdapter(mPresenter.getAdapter(), this); 67 68 // Set the title 69 final View headerView = menu.getHeaderView(); 70 if (headerView != null) { 71 // Menu's client has given a custom header view, use it 72 builder.setCustomTitle(headerView); 73 } else { 74 // Otherwise use the (text) title and icon 75 builder.setIcon(menu.getHeaderIcon()).setTitle(menu.getHeaderTitle()); 76 } 77 78 // Set the key listener 79 builder.setOnKeyListener(this); 80 81 // Show the menu 82 mDialog = builder.create(); 83 mDialog.setOnDismissListener(this); 84 85 WindowManager.LayoutParams lp = mDialog.getWindow().getAttributes(); 86 lp.type = WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG; 87 if (windowToken != null) { 88 lp.token = windowToken; 89 } 90 lp.flags |= WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM; 91 92 mDialog.show(); 93 } 94 95 public boolean onKey(DialogInterface dialog, int keyCode, KeyEvent event) { 96 if (keyCode == KeyEvent.KEYCODE_MENU || keyCode == KeyEvent.KEYCODE_BACK) { 97 if (event.getAction() == KeyEvent.ACTION_DOWN 98 && event.getRepeatCount() == 0) { 99 Window win = mDialog.getWindow(); 100 if (win != null) { 101 View decor = win.getDecorView(); 102 if (decor != null) { 103 KeyEvent.DispatcherState ds = decor.getKeyDispatcherState(); 104 if (ds != null) { 105 ds.startTracking(event, this); 106 return true; 107 } 108 } 109 } 110 } else if (event.getAction() == KeyEvent.ACTION_UP && !event.isCanceled()) { 111 Window win = mDialog.getWindow(); 112 if (win != null) { 113 View decor = win.getDecorView(); 114 if (decor != null) { 115 KeyEvent.DispatcherState ds = decor.getKeyDispatcherState(); 116 if (ds != null && ds.isTracking(event)) { 117 mMenu.close(true); 118 dialog.dismiss(); 119 return true; 120 } 121 } 122 } 123 } 124 } 125 126 // Menu shortcut matching 127 return mMenu.performShortcut(keyCode, event, 0); 128 129 } 130 131 public void setPresenterCallback(MenuPresenter.Callback cb) { 132 mPresenterCallback = cb; 133 } 134 135 /** 136 * Dismisses the menu's dialog. 137 * 138 * @see Dialog#dismiss() 139 */ 140 public void dismiss() { 141 if (mDialog != null) { 142 mDialog.dismiss(); 143 } 144 } 145 146 @Override 147 public void onDismiss(DialogInterface dialog) { 148 mPresenter.onCloseMenu(mMenu, true); 149 } 150 151 @Override 152 public void onCloseMenu(MenuBuilder menu, boolean allMenusAreClosing) { 153 if (allMenusAreClosing || menu == mMenu) { 154 dismiss(); 155 } 156 if (mPresenterCallback != null) { 157 mPresenterCallback.onCloseMenu(menu, allMenusAreClosing); 158 } 159 } 160 161 @Override 162 public boolean onOpenSubMenu(MenuBuilder subMenu) { 163 if (mPresenterCallback != null) { 164 return mPresenterCallback.onOpenSubMenu(subMenu); 165 } 166 return false; 167 } 168 169 public void onClick(DialogInterface dialog, int which) { 170 mMenu.performItemAction((MenuItemImpl) mPresenter.getAdapter().getItem(which), 0); 171 } 172} 173