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