1/*
2 * Copyright (C) 2015 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.car.app.menu;
18
19import android.content.Context;
20import android.graphics.Bitmap;
21import android.graphics.drawable.Drawable;
22import android.os.Bundle;
23import android.os.Handler;
24import android.support.annotation.LayoutRes;
25import android.support.car.Car;
26import android.support.car.app.CarFragmentActivity;
27import android.support.car.app.menu.compat.CarMenuConstantsComapt.MenuItemConstants;
28import android.support.car.input.CarInputManager;
29import android.support.v4.app.Fragment;
30import android.util.DisplayMetrics;
31import android.view.LayoutInflater;
32import android.view.View;
33import android.view.ViewGroup;
34import android.view.WindowManager;
35import android.widget.EditText;
36
37/**
38 * Base class for a car app which wants to use a drawer.
39 * @hide
40 */
41public abstract class CarDrawerActivity extends CarFragmentActivity {
42    private static final String TAG = "CarDrawerActivity";
43
44    private static final String KEY_DRAWERSHOWING =
45            "android.support.car.app.CarDrawerActivity.DRAWER_SHOWING";
46    private static final String KEY_INPUTSHOWING =
47            "android.support.car.app.CarDrawerActivity.INPUT_SHOWING";
48    private static final String KEY_SEARCHBOXENABLED =
49            "android.support.car.app.CarDrawerActivity.SEARCH_BOX_ENABLED";
50
51    private final Handler mHandler = new Handler();
52    private final CarUiController mUiController;
53
54    private CarMenuCallbacks mMenuCallbacks;
55    private OnMenuClickListener mMenuClickListener;
56    private boolean mDrawerShowing;
57    private boolean mShowingSearchBox;
58    private boolean mSearchBoxEnabled;
59    private boolean mOnCreateCalled = false;
60    private View.OnClickListener mSearchBoxOnClickListener;
61
62    private CarInputManager mInputManager;
63    private EditText mSearchBoxView;
64
65    public interface OnMenuClickListener {
66        /**
67         * Called when the menu button is clicked.
68         *
69         * @return True if event was handled. This will prevent the drawer from executing its
70         *         default action (opening/closing/going back). False if the event was not handled
71         *         so the drawer will execute the default action.
72         */
73        boolean onClicked();
74    }
75
76    public CarDrawerActivity(Proxy proxy, Context context, Car car) {
77        super(proxy, context, car);
78        mUiController = createCarUiController();
79    }
80
81    /**
82     * Create a {@link android.support.car.app.menu.CarUiController}.
83     *
84     * Derived class can override this function to return a customized ui controller.
85     */
86    protected CarUiController createCarUiController() {
87        return CarUiController.createCarUiController(this);
88    }
89
90    @Override
91    public void setContentView(View view) {
92        ViewGroup parent = (ViewGroup) findViewById(mUiController.getFragmentContainerId());
93        parent.addView(view);
94    }
95
96    @Override
97    public void setContentView(@LayoutRes int resourceId) {
98        ViewGroup parent = (ViewGroup) findViewById(mUiController.getFragmentContainerId());
99        LayoutInflater inflater = getLayoutInflater();
100        inflater.inflate(resourceId, parent, true);
101    }
102
103    @Override
104    public View findViewById(@LayoutRes int id) {
105        return super.findViewById(mUiController.getFragmentContainerId()).findViewById(id);
106    }
107
108    @Override
109    protected void onCreate(Bundle savedInstanceState) {
110        super.onCreate(savedInstanceState);
111        super.setContentView(mUiController.getContentView());
112        mInputManager = getInputManager();
113        mHandler.post(new Runnable() {
114            @Override
115            public void run() {
116                if (mMenuCallbacks != null) {
117                    mMenuCallbacks.registerOnChildrenChangedListener(mMenuListener);
118                }
119                mOnCreateCalled = true;
120            }
121        });
122    }
123
124    @Override
125    protected void onDestroy() {
126        super.onDestroy();
127        mHandler.post(new Runnable() {
128            @Override
129            public void run() {
130                if (mMenuCallbacks != null) {
131                    mMenuCallbacks.unregisterOnChildrenChangedListener(mMenuListener);
132                    mMenuCallbacks = null;
133                }
134            }
135        });
136    }
137
138    @Override
139    protected void onRestoreInstanceState(Bundle savedInstanceState) {
140        super.onRestoreInstanceState(savedInstanceState);
141        mDrawerShowing = savedInstanceState.getBoolean(KEY_DRAWERSHOWING);
142        mUiController.onRestoreInstanceState(savedInstanceState);
143    }
144
145    @Override
146    protected void onSaveInstanceState(Bundle outState) {
147        super.onSaveInstanceState(outState);
148        outState.putBoolean(KEY_DRAWERSHOWING, mDrawerShowing);
149        mUiController.onSaveInstanceState(outState);
150    }
151
152    @Override
153    protected void onStart() {
154        super.onStart();
155        getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN);
156        mUiController.onStart();
157    }
158
159    @Override
160    protected void onResume() {
161        super.onResume();
162        mUiController.onResume();
163    }
164
165    @Override
166    protected void onPause() {
167        super.onPause();
168        mUiController.onPause();
169    }
170
171    @Override
172    protected void onStop() {
173        super.onStop();
174        mUiController.onStop();
175    }
176
177    /**
178     * Set the fragment in the main fragment container.
179     */
180    public void setContentFragment(Fragment fragment) {
181        super.setContentFragment(fragment, mUiController.getFragmentContainerId());
182    }
183
184    /**
185     * Return the main fragment container id for the app.
186     */
187    public int getFragmentContainerId() {
188        return mUiController.getFragmentContainerId();
189    }
190
191    /**
192     * Set the callbacks for car menu interactions.
193     */
194    public void setCarMenuCallbacks(final CarMenuCallbacks callbacks) {
195        if (mOnCreateCalled) {
196            throw new IllegalStateException(
197                    "Cannot call setCarMenuCallbacks after onCreate has been called.");
198        }
199        mMenuCallbacks = callbacks;
200        mUiController.registerCarMenuCallbacks(callbacks);
201    }
202
203    /**
204     * Listener that listens for when the menu button is pressed.
205     *
206     * @param listener {@link OnMenuClickListener} that will listen for menu button clicks.
207     */
208    public void setOnMenuClickedListener(OnMenuClickListener listener) {
209        mMenuClickListener = listener;
210    }
211
212    /**
213     * Restore the menu button drawable
214     */
215    public void restoreMenuButtonDrawable() {
216        mUiController.restoreMenuButtonDrawable();
217    }
218
219    /**
220     * Sets the menu button bitmap
221     *
222     * @param bitmap Bitmap to the menu button to.
223     */
224    public void setMenuButtonBitmap(Bitmap bitmap) {
225        mUiController.setMenuButtonBitmap(bitmap);
226    }
227
228    /**
229     * Set the title of the menu.
230     */
231    public void setTitle(CharSequence title) {
232        mUiController.setTitle(title);
233    }
234
235    /**
236     * Set the System UI to be light.
237     */
238    public void setLightMode() {
239        mUiController.setLightMode();
240    }
241
242    /**
243     * Set the System UI to be dark.
244     */
245    public void setDarkMode() {
246        mUiController.setDarkMode();
247    }
248
249    /**
250     * Set the System UI to be dark during day mode and light during night mode.
251     */
252    public void setAutoLightDarkMode() {
253        mUiController.setAutoLightDarkMode();
254    }
255
256    /**
257     * Sets the application background to the given {@link android.graphics.Bitmap}.
258     *
259     * @param bitmap to use as background.
260     */
261    public void setBackground(Bitmap bitmap) {
262        mUiController.setBackground(bitmap);
263    }
264
265    /**
266     * Sets the color of the scrim to the right of the car menu drawer.
267     */
268    public void setScrimColor(int color) {
269        mUiController.setScrimColor(color);
270    }
271
272    /**
273     * Show the menu associated with the given id in the drawer.
274     *
275     * @param id Id of the menu to link to.
276     * @param title Title that should be displayed.
277     */
278    public void showMenu(String id, String title) {
279        mUiController.showMenu(id, title);
280    }
281
282    public boolean onMenuClicked() {
283        if (mMenuClickListener != null) {
284            return mMenuClickListener.onClicked();
285        }
286        return false;
287    }
288
289    public void restoreSearchBox() {
290        if (isSearchBoxEnabled()) {
291            mUiController.showSearchBox(mSearchBoxOnClickListener);
292            mShowingSearchBox = true;
293        }
294    }
295
296    private final CarMenuCallbacks.OnChildrenChangedListener mMenuListener =
297            new CarMenuCallbacks.OnChildrenChangedListener() {
298                @Override
299                public void onChildrenChanged(String parentId) {
300                    if (mOnCreateCalled) {
301                        mUiController.onChildrenChanged(parentId);
302                    }
303                }
304
305                @Override
306                public void onChildChanged(String parentId, Bundle item,
307                        Drawable leftIcon, Drawable rightIcon) {
308                    DisplayMetrics metrics = getResources().getDisplayMetrics();
309                    if (leftIcon != null) {
310                        item.putParcelable(MenuItemConstants.KEY_LEFTICON,
311                                Utils.snapshot(metrics, leftIcon));
312                    }
313
314                    if (rightIcon != null) {
315                        item.putParcelable(MenuItemConstants.KEY_RIGHTICON,
316                                Utils.snapshot(metrics, rightIcon));
317                    }
318                    if (mOnCreateCalled) {
319                        mUiController.onChildChanged(parentId, item);
320                    }
321                }
322            };
323
324    public void closeDrawer() {
325        mUiController.closeDrawer();
326    }
327
328    public void openDrawer() {
329        mUiController.openDrawer();
330    }
331
332    public boolean isDrawerShowing() {
333        return mDrawerShowing;
334    }
335
336    public void setDrawerShowing(boolean showing) {
337        mDrawerShowing = showing;
338    }
339
340    public boolean isSearchBoxEnabled() {
341        return mSearchBoxEnabled;
342    }
343
344    public boolean isShowingSearchBox() {
345        return mShowingSearchBox;
346    }
347
348    /**
349     * Shows a small clickable {@link android.widget.EditText}.
350     *
351     * {@link View} will be {@code null} in {@link View.OnClickListener#onClick(View)}.
352     *
353     * @param listener {@link View.OnClickListener} that is called when user selects the
354     *                 {@link android.widget.EditText}.
355     */
356    public void showSearchBox(View.OnClickListener listener) {
357        if (!isDrawerShowing()) {
358            mUiController.showSearchBox(listener);
359            mShowingSearchBox = true;
360        }
361        mSearchBoxEnabled = true;
362        mSearchBoxOnClickListener = listener;
363    }
364
365    public void showSearchBox() {
366        showSearchBox(mSearchBoxOnClickListener);
367    }
368
369    public void hideSearchBox() {
370        if (isShowingSearchBox()) {
371            stopInput();
372        }
373        mSearchBoxEnabled = false;
374    }
375
376    public void setSearchBoxEditListener(SearchBoxEditListener listener) {
377        mUiController.setSearchBoxEditListener(listener);
378    }
379
380    public void stopInput() {
381        // STOPSHIP: sometimes focus is lost and we are not able to hide the keyboard.
382        // properly fix this before we ship.
383        if (mSearchBoxView != null) {
384            mSearchBoxView.requestFocusFromTouch();
385        }
386        mUiController.stopInput();
387        mInputManager.stopInput();
388        mShowingSearchBox = false;
389    }
390
391    /**
392     * Start input on the search box that is provided by a car ui provider.
393     * TODO: Migrate to use the new input/search api once it becomes stable (b/27108311).
394     * @param hint Search hint
395     */
396    public void startInput(String hint) {
397        startInput(hint, mSearchBoxOnClickListener);
398    }
399
400    /**
401     * Start input on the search box that is provided by a car ui provider.
402     * TODO: Migrate to use the new input/search api once it becomes stable (b/27108311).
403     * @param hint Search hint
404     * @param onClickListener Listener for the search box clicks.
405     */
406    public void startInput(final String hint, final View.OnClickListener onClickListener) {
407        mInputManager = getInputManager();
408        EditText inputView = mUiController.startInput(hint, onClickListener);
409        getInputManager().startInput(inputView);
410        mSearchBoxView = inputView;
411        mShowingSearchBox = true;
412    }
413
414    public void setSearchBoxColors(int backgroundColor, int searchLogoColor, int textColor,
415            int hintTextColor) {
416        mUiController.setSearchBoxColors(backgroundColor, searchLogoColor,
417                textColor, hintTextColor);
418    }
419
420    public void setSearchBoxEndView(View endView) {
421        mUiController.setSearchBoxEndView(endView);
422    }
423
424    public void showToast(String text, int duration) {
425        mUiController.showToast(text, duration);
426    }
427}
428