1/*
2 * Copyright (C) 2017 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 androidx.wear.internal.widget.drawer;
18
19import android.graphics.drawable.Drawable;
20import android.os.Handler;
21import android.os.Looper;
22import android.view.Gravity;
23import android.view.LayoutInflater;
24import android.view.View;
25import android.widget.TextView;
26import android.widget.Toast;
27
28import androidx.annotation.IdRes;
29import androidx.annotation.LayoutRes;
30import androidx.annotation.Nullable;
31import androidx.annotation.RestrictTo;
32import androidx.annotation.RestrictTo.Scope;
33import androidx.wear.R;
34import androidx.wear.widget.CircledImageView;
35import androidx.wear.widget.drawer.WearableNavigationDrawerView;
36
37/**
38 * Handles view logic for the single page style {@link WearableNavigationDrawerView}.
39 *
40 * @hide
41 */
42@RestrictTo(Scope.LIBRARY)
43public class SinglePageUi implements SinglePagePresenter.Ui {
44
45    @IdRes
46    private static final int[] SINGLE_PAGE_BUTTON_IDS =
47            new int[]{
48                    R.id.ws_nav_drawer_icon_0,
49                    R.id.ws_nav_drawer_icon_1,
50                    R.id.ws_nav_drawer_icon_2,
51                    R.id.ws_nav_drawer_icon_3,
52                    R.id.ws_nav_drawer_icon_4,
53                    R.id.ws_nav_drawer_icon_5,
54                    R.id.ws_nav_drawer_icon_6,
55            };
56
57    @LayoutRes
58    private static final int[] SINGLE_PAGE_LAYOUT_RES =
59            new int[]{
60                    0,
61                    R.layout.ws_single_page_nav_drawer_1_item,
62                    R.layout.ws_single_page_nav_drawer_2_item,
63                    R.layout.ws_single_page_nav_drawer_3_item,
64                    R.layout.ws_single_page_nav_drawer_4_item,
65                    R.layout.ws_single_page_nav_drawer_5_item,
66                    R.layout.ws_single_page_nav_drawer_6_item,
67                    R.layout.ws_single_page_nav_drawer_7_item,
68            };
69
70    private final WearableNavigationDrawerView mDrawer;
71    private final Handler mMainThreadHandler = new Handler(Looper.getMainLooper());
72    private final Runnable mCloseDrawerRunnable =
73            new Runnable() {
74                @Override
75                public void run() {
76                    mDrawer.getController().closeDrawer();
77                }
78            };
79    private WearableNavigationDrawerPresenter mPresenter;
80    private CircledImageView[] mSinglePageImageViews;
81    /**
82     * Indicates currently selected item. {@code null} when the layout lacks space to display it.
83     */
84    @Nullable
85    private TextView mTextView;
86
87    public SinglePageUi(WearableNavigationDrawerView navigationDrawer) {
88        if (navigationDrawer == null) {
89            throw new IllegalArgumentException("Received null navigationDrawer.");
90        }
91        mDrawer = navigationDrawer;
92    }
93
94    @Override
95    public void setPresenter(WearableNavigationDrawerPresenter presenter) {
96        mPresenter = presenter;
97    }
98
99    @Override
100    public void initialize(int count) {
101        if (count < 0 || count >= SINGLE_PAGE_LAYOUT_RES.length
102                || SINGLE_PAGE_LAYOUT_RES[count] == 0) {
103            mDrawer.setDrawerContent(null);
104            return;
105        }
106
107        @LayoutRes int layoutRes = SINGLE_PAGE_LAYOUT_RES[count];
108        LayoutInflater inflater = LayoutInflater.from(mDrawer.getContext());
109        View content = inflater.inflate(layoutRes, mDrawer, false /* attachToRoot */);
110        final View peek =
111                inflater.inflate(
112                        R.layout.ws_single_page_nav_drawer_peek_view, mDrawer,
113                        false /* attachToRoot */);
114
115        mTextView = content.findViewById(R.id.ws_nav_drawer_text);
116        mSinglePageImageViews = new CircledImageView[count];
117        for (int i = 0; i < count; i++) {
118            mSinglePageImageViews[i] = content.findViewById(SINGLE_PAGE_BUTTON_IDS[i]);
119            mSinglePageImageViews[i].setOnClickListener(new OnSelectedClickHandler(i, mPresenter));
120            mSinglePageImageViews[i].setCircleHidden(true);
121        }
122
123        mDrawer.setDrawerContent(content);
124        mDrawer.setPeekContent(peek);
125    }
126
127    @Override
128    public void setIcon(int index, Drawable drawable, CharSequence contentDescription) {
129        mSinglePageImageViews[index].setImageDrawable(drawable);
130        mSinglePageImageViews[index].setContentDescription(contentDescription);
131    }
132
133    @Override
134    public void setText(CharSequence itemText, boolean showToastIfNoTextView) {
135        if (mTextView != null) {
136            mTextView.setText(itemText);
137        } else if (showToastIfNoTextView) {
138            Toast toast = Toast.makeText(mDrawer.getContext(), itemText, Toast.LENGTH_SHORT);
139            toast.setGravity(Gravity.CENTER, 0 /* xOffset */, 0 /* yOffset */);
140            toast.show();
141        }
142    }
143
144    @Override
145    public void selectItem(int index) {
146        mSinglePageImageViews[index].setCircleHidden(false);
147    }
148
149    @Override
150    public void deselectItem(int index) {
151        mSinglePageImageViews[index].setCircleHidden(true);
152    }
153
154    @Override
155    public void closeDrawerDelayed(long delayMs) {
156        mMainThreadHandler.removeCallbacks(mCloseDrawerRunnable);
157        mMainThreadHandler.postDelayed(mCloseDrawerRunnable, delayMs);
158    }
159
160    @Override
161    public void peekDrawer() {
162        mDrawer.getController().peekDrawer();
163    }
164
165    /**
166     * Notifies the {@code presenter} that the item at the given {@code index} has been selected.
167     */
168    private static class OnSelectedClickHandler implements View.OnClickListener {
169
170        private final int mIndex;
171        private final WearableNavigationDrawerPresenter mPresenter;
172
173        private OnSelectedClickHandler(int index, WearableNavigationDrawerPresenter presenter) {
174            mIndex = index;
175            mPresenter = presenter;
176        }
177
178        @Override
179        public void onClick(View v) {
180            mPresenter.onSelected(mIndex);
181        }
182    }
183}
184