• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2020 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 com.android.car.ui.paintbooth.widescreenime;
18 
19 import static android.view.inputmethod.EditorInfo.IME_FLAG_NO_EXTRACT_UI;
20 
21 import static com.android.car.ui.imewidescreen.CarUiImeWideScreenController.ADD_DESC_TITLE_TO_CONTENT_AREA;
22 import static com.android.car.ui.imewidescreen.CarUiImeWideScreenController.ADD_DESC_TO_CONTENT_AREA;
23 import static com.android.car.ui.imewidescreen.CarUiImeWideScreenController.ADD_ERROR_DESC_TO_INPUT_AREA;
24 import static com.android.car.ui.imewidescreen.CarUiImeWideScreenController.REQUEST_RENDER_CONTENT_AREA;
25 import static com.android.car.ui.imewidescreen.CarUiImeWideScreenController.WIDE_SCREEN_ACTION;
26 import static com.android.car.ui.imewidescreen.CarUiImeWideScreenController.WIDE_SCREEN_EXTRACTED_TEXT_ICON_RES_ID;
27 
28 import android.content.Context;
29 import android.graphics.drawable.Drawable;
30 import android.os.Bundle;
31 import android.util.Log;
32 import android.view.LayoutInflater;
33 import android.view.View;
34 import android.view.View.OnFocusChangeListener;
35 import android.view.ViewGroup;
36 import android.view.inputmethod.InputMethodManager;
37 import android.widget.EditText;
38 import android.widget.Toast;
39 
40 import androidx.annotation.NonNull;
41 import androidx.appcompat.app.AppCompatActivity;
42 import androidx.core.content.ContextCompat;
43 import androidx.recyclerview.widget.RecyclerView;
44 
45 import com.android.car.ui.FocusArea;
46 import com.android.car.ui.baselayout.Insets;
47 import com.android.car.ui.baselayout.InsetsChangedListener;
48 import com.android.car.ui.core.CarUi;
49 import com.android.car.ui.imewidescreen.CarUiImeSearchListItem;
50 import com.android.car.ui.paintbooth.R;
51 import com.android.car.ui.recyclerview.CarUiContentListItem;
52 import com.android.car.ui.recyclerview.CarUiRecyclerView;
53 import com.android.car.ui.toolbar.MenuItem;
54 import com.android.car.ui.toolbar.NavButtonMode;
55 import com.android.car.ui.toolbar.SearchConfig;
56 import com.android.car.ui.toolbar.SearchMode;
57 import com.android.car.ui.toolbar.ToolbarController;
58 
59 import java.util.ArrayList;
60 import java.util.List;
61 
62 /**
63  * Activity that shows different scenarios for wide screen ime.
64  */
65 public class WideScreenImeActivity extends AppCompatActivity implements InsetsChangedListener {
66     private static final String TAG = "PaintBooth";
67 
68     private final List<MenuItem> mMenuItems = new ArrayList<>();
69     private final List<ListElement> mWidescreenItems = new ArrayList<>();
70     private final List<CarUiImeSearchListItem> mSearchItems = new ArrayList<>();
71 
72     private InputMethodManager mInputMethodManager;
73 
74     @Override
onCreate(Bundle savedInstanceState)75     protected void onCreate(Bundle savedInstanceState) {
76         super.onCreate(savedInstanceState);
77         setContentView(R.layout.car_ui_recycler_view_activity);
78 
79         mInputMethodManager = (InputMethodManager)
80                 getSystemService(Context.INPUT_METHOD_SERVICE);
81 
82         ToolbarController toolbar = CarUi.requireToolbar(this);
83         toolbar.setTitle(getTitle());
84         toolbar.setNavButtonMode(NavButtonMode.BACK);
85         toolbar.setLogo(R.drawable.ic_launcher);
86         boolean[] isSearching = new boolean[]{false};
87         toolbar.registerBackListener(
88                 () -> {
89                     if (isSearching[0]) {
90                         toolbar.setSearchMode(SearchMode.DISABLED);
91                         isSearching[0] = false;
92                         return true;
93                     }
94                     return false;
95                 });
96 
97         CarUiContentListItem.OnClickListener mainClickListener = i -> {
98             CharSequence title = i.getTitle() != null
99                     ? i.getTitle().getPreferredText()
100                     : null;
101             Toast.makeText(this, "Item clicked! " + title, Toast.LENGTH_SHORT).show();
102             Log.i(TAG, "Item clicked! " + title);
103         };
104         CarUiContentListItem.OnClickListener secondaryClickListener = i -> {
105             Toast.makeText(this, "Item's secondary action clicked!", Toast.LENGTH_SHORT).show();
106             Log.i(TAG, "Item's secondary action clicked!");
107         };
108 
109         Drawable icon = ContextCompat.getDrawable(this, R.drawable.ic_launcher);
110 
111         for (int i = 1; i <= 100; i++) {
112             CarUiImeSearchListItem item = new CarUiImeSearchListItem(
113                     CarUiContentListItem.Action.ICON);
114             item.setTitle("Title " + i);
115             item.setBody("Sub title " + i);
116             item.setIcon(icon);
117             item.setSupplementalIcon(icon, secondaryClickListener);
118             item.setOnItemClickedListener(mainClickListener);
119 
120             mSearchItems.add(item);
121         }
122 
123         mMenuItems.add(MenuItem.builder(this)
124                 .setToSearch()
125                 .setOnClickListener(i -> {
126                     isSearching[0] = true;
127                     toolbar.setSearchMode(SearchMode.SEARCH);
128                     if (toolbar.getSearchCapabilities().canShowSearchResultItems()) {
129                         toolbar.setSearchConfig(SearchConfig.builder()
130                                 .setSearchResultItems(mSearchItems)
131                                 .setSearchResultsInputViewIcon(ContextCompat.getDrawable(
132                                         this, R.drawable.car_ui_icon_search))
133                                 .build());
134                     }
135                 })
136                 .build());
137 
138         toolbar.setMenuItems(mMenuItems);
139 
140         mWidescreenItems.add(new EditTextElement("Default Input Edit Text field", null));
141         mWidescreenItems.add(
142                 new EditTextElement("Add Desc to content area", this::addDescToContentArea));
143         mWidescreenItems.add(new EditTextElement("Hide the content area", this::hideContentArea));
144         mWidescreenItems.add(new EditTextElement("Hide extraction view", this::hideExtractionView));
145 
146         mWidescreenItems.add(
147                 new EditTextElement("Add icon to extracted view", this::addIconToExtractedView));
148         mWidescreenItems.add(new EditTextElement("Add error message to content area",
149                 this::addErrorDescToContentArea));
150 
151         CarUiRecyclerView recyclerView = requireViewById(R.id.list);
152         recyclerView.setAdapter(mAdapter);
153     }
154 
addIconToExtractedView(View view, boolean hasFocus)155     private void addIconToExtractedView(View view, boolean hasFocus) {
156         if (!hasFocus) {
157             return;
158         }
159 
160         Bundle bundle = new Bundle();
161         bundle.putInt(WIDE_SCREEN_EXTRACTED_TEXT_ICON_RES_ID, R.drawable.car_ui_icon_edit);
162         mInputMethodManager.sendAppPrivateCommand(view, WIDE_SCREEN_ACTION, bundle);
163     }
164 
addErrorDescToContentArea(View view, boolean hasFocus)165     private void addErrorDescToContentArea(View view, boolean hasFocus) {
166         if (!hasFocus) {
167             return;
168         }
169 
170         Bundle bundle = new Bundle();
171         bundle.putString(ADD_ERROR_DESC_TO_INPUT_AREA, "Some error message");
172         bundle.putString(ADD_DESC_TITLE_TO_CONTENT_AREA, "Title");
173         bundle.putString(ADD_DESC_TO_CONTENT_AREA, "Description provided by the application");
174         mInputMethodManager.sendAppPrivateCommand(view, WIDE_SCREEN_ACTION, bundle);
175     }
176 
hideExtractionView(View view, boolean hasFocus)177     private void hideExtractionView(View view, boolean hasFocus) {
178         if (!hasFocus) {
179             return;
180         }
181 
182         EditText editText = (EditText) view;
183         editText.setImeOptions(IME_FLAG_NO_EXTRACT_UI);
184 
185         Bundle bundle = new Bundle();
186         bundle.putBoolean(REQUEST_RENDER_CONTENT_AREA, false);
187         mInputMethodManager.sendAppPrivateCommand(view, WIDE_SCREEN_ACTION, bundle);
188     }
189 
addDescToContentArea(View view, boolean hasFocus)190     private void addDescToContentArea(View view, boolean hasFocus) {
191         if (!hasFocus) {
192             return;
193         }
194 
195         Bundle bundle = new Bundle();
196         bundle.putString(ADD_DESC_TITLE_TO_CONTENT_AREA, "Title");
197         bundle.putString(ADD_DESC_TO_CONTENT_AREA, "Description provided by the application");
198         mInputMethodManager.sendAppPrivateCommand(view, WIDE_SCREEN_ACTION, bundle);
199     }
200 
hideContentArea(View view, boolean hasFocus)201     private void hideContentArea(View view, boolean hasFocus) {
202         if (!hasFocus) {
203             return;
204         }
205 
206         Bundle bundle = new Bundle();
207         bundle.putBoolean(REQUEST_RENDER_CONTENT_AREA, false);
208         mInputMethodManager.sendAppPrivateCommand(view, WIDE_SCREEN_ACTION, bundle);
209     }
210 
211     private abstract static class ViewHolder extends RecyclerView.ViewHolder {
212 
ViewHolder(@onNull View itemView)213         ViewHolder(@NonNull View itemView) {
214             super(itemView);
215         }
216 
bind(ListElement element)217         public abstract void bind(ListElement element);
218     }
219 
220     private static class EditTextViewHolder extends ViewHolder {
221 
222         private final EditText mEditText;
223 
EditTextViewHolder(@onNull View itemView)224         EditTextViewHolder(@NonNull View itemView) {
225             super(itemView);
226             mEditText = itemView.requireViewById(R.id.edit_text);
227         }
228 
229         @Override
bind(ListElement e)230         public void bind(ListElement e) {
231             if (!(e instanceof EditTextElement)) {
232                 throw new IllegalArgumentException("Expected an EditTextElement");
233             }
234             EditTextElement element = (EditTextElement) e;
235             mEditText.setText(element.getText());
236             mEditText.setOnFocusChangeListener(element.getOnFocusChangeListener());
237         }
238     }
239 
240     private final RecyclerView.Adapter<ViewHolder> mAdapter =
241             new RecyclerView.Adapter<ViewHolder>() {
242                 @Override
243                 public int getItemCount() {
244                     return mWidescreenItems.size();
245                 }
246 
247                 @Override
248                 public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
249                     LayoutInflater inflater = LayoutInflater.from(parent.getContext());
250                     if (viewType == ListElement.TYPE_EDIT_TEXT) {
251                         return new EditTextViewHolder(
252                                 inflater.inflate(R.layout.edit_text_list_item, parent, false));
253                     } else {
254                         throw new IllegalArgumentException("Unknown viewType: " + viewType);
255                     }
256                 }
257 
258                 @Override
259                 public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
260                     holder.bind(mWidescreenItems.get(position));
261                 }
262 
263                 @Override
264                 public int getItemViewType(int position) {
265                     return mWidescreenItems.get(position).getType();
266                 }
267             };
268 
269     @Override
onCarUiInsetsChanged(@onNull Insets insets)270     public void onCarUiInsetsChanged(@NonNull Insets insets) {
271         FocusArea focusArea = requireViewById(R.id.focus_area);
272         focusArea.setBoundsOffset(0, insets.getTop(), 0, insets.getBottom());
273         focusArea.setHighlightPadding(0, insets.getTop(), 0, insets.getBottom());
274         requireViewById(R.id.list)
275                 .setPadding(0, insets.getTop(), 0, insets.getBottom());
276         requireViewById(android.R.id.content)
277                 .setPadding(insets.getLeft(), 0, insets.getRight(), 0);
278     }
279 
280     private abstract static class ListElement {
281 
282         static final int TYPE_EDIT_TEXT = 0;
283 
284         private final String mText;
285 
ListElement(String text)286         ListElement(String text) {
287             mText = text;
288         }
289 
getText()290         String getText() {
291             return mText;
292         }
293 
getType()294         abstract int getType();
295     }
296 
297     private static class EditTextElement extends ListElement {
298 
299         private final OnFocusChangeListener mListener;
300 
EditTextElement(String text, OnFocusChangeListener listener)301         EditTextElement(String text, OnFocusChangeListener listener) {
302             super(text);
303             mListener = listener;
304         }
305 
getOnFocusChangeListener()306         OnFocusChangeListener getOnFocusChangeListener() {
307             return mListener;
308         }
309 
310         @Override
getType()311         int getType() {
312             return TYPE_EDIT_TEXT;
313         }
314     }
315 }
316