1/* 2 * Copyright (C) 2015 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 * in compliance with the License. You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software distributed under the License 10 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 * or implied. See the License for the specific language governing permissions and limitations under 12 * the License. 13 */ 14package android.support.v17.leanback.widget; 15 16import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP; 17 18import android.content.Context; 19import android.support.annotation.RestrictTo; 20import android.support.v17.leanback.widget.GuidedActionAdapter.EditListener; 21import android.util.Log; 22import android.util.Pair; 23import android.view.View; 24import android.view.inputmethod.InputMethodManager; 25import android.widget.TextView; 26 27import java.util.ArrayList; 28 29/** 30 * Internal implementation manages a group of GuidedActionAdapters, control the next action after 31 * editing finished, maintain the Ime open/close status. 32 * @hide 33 */ 34@RestrictTo(LIBRARY_GROUP) 35public class GuidedActionAdapterGroup { 36 37 private static final String TAG_EDIT = "EditableAction"; 38 private static final boolean DEBUG_EDIT = false; 39 40 ArrayList<Pair<GuidedActionAdapter, GuidedActionAdapter>> mAdapters = 41 new ArrayList<Pair<GuidedActionAdapter, GuidedActionAdapter>>(); 42 private boolean mImeOpened; 43 private EditListener mEditListener; 44 45 public void addAdpter(GuidedActionAdapter adapter1, GuidedActionAdapter adapter2) { 46 mAdapters.add(new Pair<GuidedActionAdapter, GuidedActionAdapter>(adapter1, adapter2)); 47 if (adapter1 != null) { 48 adapter1.mGroup = this; 49 } 50 if (adapter2 != null) { 51 adapter2.mGroup = this; 52 } 53 } 54 55 public GuidedActionAdapter getNextAdapter(GuidedActionAdapter adapter) { 56 for (int i = 0; i < mAdapters.size(); i++) { 57 Pair<GuidedActionAdapter, GuidedActionAdapter> pair = mAdapters.get(i); 58 if (pair.first == adapter) { 59 return pair.second; 60 } 61 } 62 return null; 63 } 64 65 public void setEditListener(EditListener listener) { 66 mEditListener = listener; 67 } 68 69 boolean focusToNextAction(GuidedActionAdapter adapter, GuidedAction action, long nextActionId) { 70 // for ACTION_ID_NEXT, we first find out the matching index in Actions list. 71 int index = 0; 72 if (nextActionId == GuidedAction.ACTION_ID_NEXT) { 73 index = adapter.indexOf(action); 74 if (index < 0) { 75 return false; 76 } 77 // start from next, if reach end, will go next Adapter below 78 index++; 79 } 80 81 do { 82 int size = adapter.getCount(); 83 if (nextActionId == GuidedAction.ACTION_ID_NEXT) { 84 while (index < size && !adapter.getItem(index).isFocusable()) { 85 index++; 86 } 87 } else { 88 while (index < size && adapter.getItem(index).getId() != nextActionId) { 89 index++; 90 } 91 } 92 if (index < size) { 93 GuidedActionsStylist.ViewHolder vh = 94 (GuidedActionsStylist.ViewHolder) adapter.getGuidedActionsStylist() 95 .getActionsGridView().findViewHolderForPosition(index); 96 if (vh != null) { 97 if (vh.getAction().hasTextEditable()) { 98 if (DEBUG_EDIT) Log.v(TAG_EDIT, "openIme of next Action"); 99 // open Ime on next action. 100 openIme(adapter, vh); 101 } else { 102 if (DEBUG_EDIT) Log.v(TAG_EDIT, "closeIme and focus to next Action"); 103 // close IME and focus to next (not editable) action 104 closeIme(vh.itemView); 105 vh.itemView.requestFocus(); 106 } 107 return true; 108 } 109 return false; 110 } 111 // search from index 0 of next Adapter 112 adapter = getNextAdapter(adapter); 113 if (adapter == null) { 114 break; 115 } 116 index = 0; 117 } while (true); 118 return false; 119 } 120 121 public void openIme(GuidedActionAdapter adapter, GuidedActionsStylist.ViewHolder avh) { 122 adapter.getGuidedActionsStylist().setEditingMode(avh, true); 123 View v = avh.getEditingView(); 124 if (v == null || !avh.isInEditingText()) { 125 return; 126 } 127 InputMethodManager mgr = (InputMethodManager) 128 v.getContext().getSystemService(Context.INPUT_METHOD_SERVICE); 129 // Make the TextView focusable during editing, avoid the TextView gets accessibility focus 130 // before editing started. see also GuidedActionEditText where setFocusable(false). 131 v.setFocusable(true); 132 v.requestFocus(); 133 mgr.showSoftInput(v, 0); 134 if (!mImeOpened) { 135 mImeOpened = true; 136 mEditListener.onImeOpen(); 137 } 138 } 139 140 public void closeIme(View v) { 141 if (mImeOpened) { 142 mImeOpened = false; 143 InputMethodManager mgr = (InputMethodManager) 144 v.getContext().getSystemService(Context.INPUT_METHOD_SERVICE); 145 mgr.hideSoftInputFromWindow(v.getWindowToken(), 0); 146 mEditListener.onImeClose(); 147 } 148 } 149 150 public void fillAndStay(GuidedActionAdapter adapter, TextView v) { 151 GuidedActionsStylist.ViewHolder avh = adapter.findSubChildViewHolder(v); 152 updateTextIntoAction(avh, v); 153 mEditListener.onGuidedActionEditCanceled(avh.getAction()); 154 adapter.getGuidedActionsStylist().setEditingMode(avh, false); 155 closeIme(v); 156 avh.itemView.requestFocus(); 157 } 158 159 public void fillAndGoNext(GuidedActionAdapter adapter, TextView v) { 160 boolean handled = false; 161 GuidedActionsStylist.ViewHolder avh = adapter.findSubChildViewHolder(v); 162 updateTextIntoAction(avh, v); 163 adapter.performOnActionClick(avh); 164 long nextActionId = mEditListener.onGuidedActionEditedAndProceed(avh.getAction()); 165 adapter.getGuidedActionsStylist().setEditingMode(avh, false); 166 if (nextActionId != GuidedAction.ACTION_ID_CURRENT 167 && nextActionId != avh.getAction().getId()) { 168 handled = focusToNextAction(adapter, avh.getAction(), nextActionId); 169 } 170 if (!handled) { 171 if (DEBUG_EDIT) Log.v(TAG_EDIT, "closeIme no next action"); 172 handled = true; 173 closeIme(v); 174 avh.itemView.requestFocus(); 175 } 176 } 177 178 private void updateTextIntoAction(GuidedActionsStylist.ViewHolder avh, TextView v) { 179 GuidedAction action = avh.getAction(); 180 if (v == avh.getDescriptionView()) { 181 if (action.getEditDescription() != null) { 182 action.setEditDescription(v.getText()); 183 } else { 184 action.setDescription(v.getText()); 185 } 186 } else if (v == avh.getTitleView()) { 187 if (action.getEditTitle() != null) { 188 action.setEditTitle(v.getText()); 189 } else { 190 action.setTitle(v.getText()); 191 } 192 } 193 } 194 195} 196