blob: 77d8e0290f202dcb202b1df940df7202f1744430 [file] [log] [blame]
arangelov38a6fce2019-12-02 18:21:22 +00001/*
2 * Copyright (C) 2019 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 */
16package com.android.internal.app;
Daulet Zhanguzin71339a92019-12-18 14:34:12 +000017
arangelov38a6fce2019-12-02 18:21:22 +000018import android.annotation.IntDef;
arangelova3912cf2019-12-13 14:34:45 +000019import android.annotation.Nullable;
arangelov38a6fce2019-12-02 18:21:22 +000020import android.content.Context;
21import android.os.UserHandle;
22import android.view.View;
23import android.view.ViewGroup;
24
25import com.android.internal.annotations.VisibleForTesting;
26import com.android.internal.widget.PagerAdapter;
arangelov38a6fce2019-12-02 18:21:22 +000027import com.android.internal.widget.ViewPager;
28
arangelovcf268642020-01-15 15:09:51 +000029import java.util.HashSet;
Daulet Zhanguzin71339a92019-12-18 14:34:12 +000030import java.util.Objects;
arangelovcf268642020-01-15 15:09:51 +000031import java.util.Set;
Daulet Zhanguzin71339a92019-12-18 14:34:12 +000032
arangelov38a6fce2019-12-02 18:21:22 +000033/**
34 * Skeletal {@link PagerAdapter} implementation of a work or personal profile page for
35 * intent resolution (including share sheet).
36 */
37public abstract class AbstractMultiProfilePagerAdapter extends PagerAdapter {
38
arangelovcf268642020-01-15 15:09:51 +000039 private static final String TAG = "AbstractMultiProfilePagerAdapter";
arangelov38a6fce2019-12-02 18:21:22 +000040 static final int PROFILE_PERSONAL = 0;
41 static final int PROFILE_WORK = 1;
42 @IntDef({PROFILE_PERSONAL, PROFILE_WORK})
43 @interface Profile {}
44
45 private final Context mContext;
46 private int mCurrentPage;
arangelovcf268642020-01-15 15:09:51 +000047 private OnProfileSelectedListener mOnProfileSelectedListener;
48 private Set<Integer> mLoadedPages;
arangelov38a6fce2019-12-02 18:21:22 +000049
50 AbstractMultiProfilePagerAdapter(Context context, int currentPage) {
Daulet Zhanguzin71339a92019-12-18 14:34:12 +000051 mContext = Objects.requireNonNull(context);
arangelov38a6fce2019-12-02 18:21:22 +000052 mCurrentPage = currentPage;
arangelovcf268642020-01-15 15:09:51 +000053 mLoadedPages = new HashSet<>();
54 }
55
56 void setOnProfileSelectedListener(OnProfileSelectedListener listener) {
57 mOnProfileSelectedListener = listener;
arangelov38a6fce2019-12-02 18:21:22 +000058 }
59
60 Context getContext() {
61 return mContext;
62 }
63
64 /**
65 * Sets this instance of this class as {@link ViewPager}'s {@link PagerAdapter} and sets
66 * an {@link ViewPager.OnPageChangeListener} where it keeps track of the currently displayed
67 * page and rebuilds the list.
68 */
69 void setupViewPager(ViewPager viewPager) {
arangelov38a6fce2019-12-02 18:21:22 +000070 viewPager.setOnPageChangeListener(new ViewPager.SimpleOnPageChangeListener() {
71 @Override
72 public void onPageSelected(int position) {
73 mCurrentPage = position;
arangelovcf268642020-01-15 15:09:51 +000074 if (!mLoadedPages.contains(position)) {
75 getActiveListAdapter().rebuildList();
76 mLoadedPages.add(position);
77 }
78 if (mOnProfileSelectedListener != null) {
79 mOnProfileSelectedListener.onProfileSelected(position);
80 }
arangelov38a6fce2019-12-02 18:21:22 +000081 }
82 });
83 viewPager.setAdapter(this);
arangelovcf268642020-01-15 15:09:51 +000084 viewPager.setCurrentItem(mCurrentPage);
85 mLoadedPages.add(mCurrentPage);
arangelov38a6fce2019-12-02 18:21:22 +000086 }
87
88 @Override
89 public ViewGroup instantiateItem(ViewGroup container, int position) {
90 final ProfileDescriptor profileDescriptor = getItem(position);
91 setupListAdapter(position);
92 container.addView(profileDescriptor.rootView);
93 return profileDescriptor.rootView;
94 }
95
96 @Override
97 public void destroyItem(ViewGroup container, int position, Object view) {
98 container.removeView((View) view);
99 }
100
101 @Override
102 public int getCount() {
103 return getItemCount();
104 }
105
106 protected int getCurrentPage() {
107 return mCurrentPage;
108 }
109
arangelovcf268642020-01-15 15:09:51 +0000110 @VisibleForTesting
111 public UserHandle getCurrentUserHandle() {
arangelova3912cf2019-12-13 14:34:45 +0000112 return getActiveListAdapter().mResolverListController.getUserHandle();
arangelov38a6fce2019-12-02 18:21:22 +0000113 }
114
115 @Override
116 public boolean isViewFromObject(View view, Object object) {
117 return view == object;
118 }
119
120 @Override
121 public CharSequence getPageTitle(int position) {
122 return null;
123 }
124
125 /**
126 * Returns the {@link ProfileDescriptor} relevant to the given <code>pageIndex</code>.
127 * <ul>
128 * <li>For a device with only one user, <code>pageIndex</code> value of
129 * <code>0</code> would return the personal profile {@link ProfileDescriptor}.</li>
130 * <li>For a device with a work profile, <code>pageIndex</code> value of <code>0</code> would
131 * return the personal profile {@link ProfileDescriptor}, and <code>pageIndex</code> value of
132 * <code>1</code> would return the work profile {@link ProfileDescriptor}.</li>
133 * </ul>
134 */
135 abstract ProfileDescriptor getItem(int pageIndex);
136
137 /**
138 * Returns the number of {@link ProfileDescriptor} objects.
139 * <p>For a normal consumer device with only one user returns <code>1</code>.
140 * <p>For a device with a work profile returns <code>2</code>.
141 */
142 abstract int getItemCount();
143
144 /**
145 * Responsible for assigning an adapter to the list view for the relevant page, specified by
146 * <code>pageIndex</code>, and other list view-related initialization procedures.
147 */
148 abstract void setupListAdapter(int pageIndex);
149
150 /**
151 * Returns the adapter of the list view for the relevant page specified by
152 * <code>pageIndex</code>.
153 * <p>This method is meant to be implemented with an implementation-specific return type
154 * depending on the adapter type.
155 */
arangelovcf268642020-01-15 15:09:51 +0000156 @VisibleForTesting
157 public abstract Object getAdapterForIndex(int pageIndex);
arangelov38a6fce2019-12-02 18:21:22 +0000158
arangelov5fc9e7d2020-01-07 17:59:14 +0000159 /**
160 * Returns the {@link ResolverListAdapter} instance of the profile that represents
161 * <code>userHandle</code>. If there is no such adapter for the specified
162 * <code>userHandle</code>, returns {@code null}.
163 * <p>For example, if there is a work profile on the device with user id 10, calling this method
164 * with <code>UserHandle.of(10)</code> returns the work profile {@link ResolverListAdapter}.
165 */
166 @Nullable
167 abstract ResolverListAdapter getListAdapterForUserHandle(UserHandle userHandle);
168
169 /**
170 * Returns the {@link ResolverListAdapter} instance of the profile that is currently visible
171 * to the user.
172 * <p>For example, if the user is viewing the work tab in the share sheet, this method returns
173 * the work profile {@link ResolverListAdapter}.
174 * @see #getInactiveListAdapter()
175 */
arangelov38a6fce2019-12-02 18:21:22 +0000176 @VisibleForTesting
arangelova3912cf2019-12-13 14:34:45 +0000177 public abstract ResolverListAdapter getActiveListAdapter();
178
179 /**
180 * If this is a device with a work profile, returns the {@link ResolverListAdapter} instance
arangelov5fc9e7d2020-01-07 17:59:14 +0000181 * of the profile that is <b><i>not</i></b> currently visible to the user. Otherwise returns
182 * {@code null}.
183 * <p>For example, if the user is viewing the work tab in the share sheet, this method returns
184 * the personal profile {@link ResolverListAdapter}.
arangelova3912cf2019-12-13 14:34:45 +0000185 * @see #getActiveListAdapter()
186 */
187 @VisibleForTesting
188 public abstract @Nullable ResolverListAdapter getInactiveListAdapter();
arangelov38a6fce2019-12-02 18:21:22 +0000189
190 abstract Object getCurrentRootAdapter();
191
arangelovcf268642020-01-15 15:09:51 +0000192 abstract ViewGroup getActiveAdapterView();
193
194 abstract @Nullable ViewGroup getInactiveAdapterView();
arangelov38a6fce2019-12-02 18:21:22 +0000195
196 protected class ProfileDescriptor {
197 final ViewGroup rootView;
198 ProfileDescriptor(ViewGroup rootView) {
199 this.rootView = rootView;
200 }
201 }
arangelovcf268642020-01-15 15:09:51 +0000202
203 public interface OnProfileSelectedListener {
204 /**
205 * Callback for when the user changes the active tab from personal to work or vice versa.
206 * <p>This callback is only called when the intent resolver or share sheet shows
207 * the work and personal profiles.
208 * @param profileIndex {@link #PROFILE_PERSONAL} if the personal profile was selected or
209 * {@link #PROFILE_WORK} if the work profile was selected.
210 */
211 void onProfileSelected(int profileIndex);
212 }
arangelov38a6fce2019-12-02 18:21:22 +0000213}