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