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 
17 package androidx.wear.internal.widget.drawer;
18 
19 import android.util.Log;
20 import android.view.LayoutInflater;
21 import android.view.View;
22 import android.view.ViewGroup;
23 import android.widget.ImageView;
24 import android.widget.TextView;
25 
26 import androidx.annotation.RestrictTo;
27 import androidx.annotation.RestrictTo.Scope;
28 import androidx.viewpager.widget.PagerAdapter;
29 import androidx.viewpager.widget.ViewPager;
30 import androidx.wear.R;
31 import androidx.wear.widget.drawer.PageIndicatorView;
32 import androidx.wear.widget.drawer.WearableNavigationDrawerView;
33 import androidx.wear.widget.drawer.WearableNavigationDrawerView.WearableNavigationDrawerAdapter;
34 
35 import org.jspecify.annotations.NonNull;
36 import org.jspecify.annotations.Nullable;
37 
38 /**
39  * Handles view logic for the multi page style {@link WearableNavigationDrawerView}.
40  *
41  */
42 @RestrictTo(Scope.LIBRARY)
43 public class MultiPageUi implements MultiPagePresenter.Ui {
44 
45     private static final String TAG = "MultiPageUi";
46 
47     @SuppressWarnings("WeakerAccess") /* synthetic access */
48     WearableNavigationDrawerPresenter mPresenter;
49 
50     private @Nullable ViewPager mNavigationPager;
51     private @Nullable PageIndicatorView mPageIndicatorView;
52 
53     @Override
initialize( WearableNavigationDrawerView drawer, WearableNavigationDrawerPresenter presenter)54     public void initialize(
55             WearableNavigationDrawerView drawer, WearableNavigationDrawerPresenter presenter) {
56         if (drawer == null) {
57             throw new IllegalArgumentException("Received null drawer.");
58         }
59         if (presenter == null) {
60             throw new IllegalArgumentException("Received null presenter.");
61         }
62         mPresenter = presenter;
63 
64         LayoutInflater inflater = LayoutInflater.from(drawer.getContext());
65         final View content = inflater.inflate(R.layout.ws_navigation_drawer_view, drawer,
66                 false /* attachToRoot */);
67 
68         mNavigationPager = content.findViewById(R.id.ws_navigation_drawer_view_pager);
69         mPageIndicatorView = content.findViewById(R.id.ws_navigation_drawer_page_indicator);
70 
71         drawer.setDrawerContent(content);
72     }
73 
74     @Override
setNavigationPagerAdapter(final WearableNavigationDrawerAdapter adapter)75     public void setNavigationPagerAdapter(final WearableNavigationDrawerAdapter adapter) {
76         if (mNavigationPager == null || mPageIndicatorView == null) {
77             Log.w(TAG, "setNavigationPagerAdapter was called before initialize.");
78             return;
79         }
80 
81         NavigationPagerAdapter navigationPagerAdapter = new NavigationPagerAdapter(adapter);
82         mNavigationPager.setAdapter(navigationPagerAdapter);
83 
84         // Clear out the old page listeners and add a new one for this adapter.
85         mNavigationPager.clearOnPageChangeListeners();
86         mNavigationPager.addOnPageChangeListener(
87                 new ViewPager.SimpleOnPageChangeListener() {
88                     @Override
89                     public void onPageSelected(int position) {
90                         mPresenter.onSelected(position);
91                     }
92                 });
93         // PageIndicatorView adds itself as a page change listener here, so this must come after
94         // they are cleared.
95         mPageIndicatorView.setPager(mNavigationPager);
96     }
97 
98     @Override
notifyPageIndicatorDataChanged()99     public void notifyPageIndicatorDataChanged() {
100         if (mPageIndicatorView != null) {
101             mPageIndicatorView.notifyDataSetChanged();
102         }
103     }
104 
105     @Override
notifyNavigationPagerAdapterDataChanged()106     public void notifyNavigationPagerAdapterDataChanged() {
107         if (mNavigationPager != null) {
108             PagerAdapter adapter = mNavigationPager.getAdapter();
109             if (adapter != null) {
110                 adapter.notifyDataSetChanged();
111             }
112         }
113     }
114 
115     @Override
setNavigationPagerSelectedItem(int index, boolean smoothScrollTo)116     public void setNavigationPagerSelectedItem(int index, boolean smoothScrollTo) {
117         if (mNavigationPager != null) {
118             mNavigationPager.setCurrentItem(index, smoothScrollTo);
119         }
120     }
121 
122     /**
123      * Adapter for {@link ViewPager} used in the multi-page UI.
124      */
125     private static final class NavigationPagerAdapter extends PagerAdapter {
126 
127         private final WearableNavigationDrawerAdapter mAdapter;
128 
NavigationPagerAdapter(WearableNavigationDrawerAdapter adapter)129         NavigationPagerAdapter(WearableNavigationDrawerAdapter adapter) {
130             mAdapter = adapter;
131         }
132 
133         @Override
instantiateItem(@onNull ViewGroup container, int position)134         public @NonNull Object instantiateItem(@NonNull ViewGroup container, int position) {
135             // Do not attach to root in the inflate method. The view needs to returned at the end
136             // of this method. Attaching to root will cause view to point to container instead.
137             final View view =
138                     LayoutInflater.from(container.getContext())
139                             .inflate(R.layout.ws_navigation_drawer_item_view, container, false);
140             container.addView(view);
141             final ImageView iconView =
142                     view.findViewById(R.id.ws_navigation_drawer_item_icon);
143             final TextView textView =
144                     view.findViewById(R.id.ws_navigation_drawer_item_text);
145             iconView.setImageDrawable(mAdapter.getItemDrawable(position));
146             textView.setText(mAdapter.getItemText(position));
147             return view;
148         }
149 
150         @Override
destroyItem(@onNull ViewGroup container, int position, @NonNull Object object)151         public void destroyItem(@NonNull ViewGroup container, int position,
152                 @NonNull Object object) {
153             container.removeView((View) object);
154         }
155 
156         @Override
getCount()157         public int getCount() {
158             return mAdapter.getCount();
159         }
160 
161         @Override
getItemPosition(@onNull Object object)162         public int getItemPosition(@NonNull Object object) {
163             return POSITION_NONE;
164         }
165 
166         @Override
isViewFromObject(@onNull View view, @NonNull Object object)167         public boolean isViewFromObject(@NonNull View view, @NonNull Object object) {
168             return view == object;
169         }
170     }
171 }
172