blob: e7e8e50211d7b2434706d393512f60f8e66e9010 [file] [log] [blame]
Marcus Hagerott819214d2016-09-29 14:58:27 -07001/*
2 * Copyright (C) 2016 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.contacts.editor;
17
18import android.content.Context;
Marcus Hagerott73b283f2016-10-21 15:42:00 -070019import android.os.Bundle;
Marcus Hagerott819214d2016-09-29 14:58:27 -070020import android.support.annotation.NonNull;
21import android.support.annotation.StringRes;
22import android.view.View;
23import android.widget.AdapterView;
24import android.widget.ImageView;
25import android.widget.ListPopupWindow;
26import android.widget.TextView;
27
28import com.android.contacts.R;
Marcus Hagerott75895e72016-12-12 17:21:57 -080029import com.android.contacts.model.account.AccountInfo;
Gary Mai69c182a2016-12-05 13:07:03 -080030import com.android.contacts.model.account.AccountWithDataSet;
31import com.android.contacts.util.AccountsListAdapter;
Marcus Hagerott819214d2016-09-29 14:58:27 -070032import com.android.contacts.util.UiClosables;
33
34import java.util.List;
35
36/**
37 * Controls the display of an account selector or header.
38 *
39 * TODO: This was mostly copied from {@link RawContactEditorView}. The code in that class
40 * should probably be modified to use this instead of leaving it duplicated.
41 */
42public class AccountHeaderPresenter {
43
Marcus Hagerott73b283f2016-10-21 15:42:00 -070044 private static final String KEY_SELECTED_ACCOUNT = "accountHeaderSelectedAccount";
45
Marcus Hagerott819214d2016-09-29 14:58:27 -070046 public interface Observer {
47 void onChange(AccountHeaderPresenter sender);
48
49 public static final Observer NONE = new Observer() {
50 @Override
51 public void onChange(AccountHeaderPresenter sender) {
52 }
53 };
54 }
55
56 private final Context mContext;
Marcus Hagerott819214d2016-09-29 14:58:27 -070057
Marcus Hagerott75895e72016-12-12 17:21:57 -080058 private List<AccountInfo> mAccounts;
Marcus Hagerott819214d2016-09-29 14:58:27 -070059 private AccountWithDataSet mCurrentAccount;
60
61 // Account header
62 private final View mAccountHeaderContainer;
63 private TextView mAccountHeaderType;
64 private TextView mAccountHeaderName;
65 private ImageView mAccountHeaderIcon;
66 private ImageView mAccountHeaderExpanderIcon;
67
68 // This would be different if the account was readonly
69 @StringRes
70 private int mSelectorTitle = R.string.editor_account_selector_title;
71
72 private Observer mObserver = Observer.NONE;
73
74 public AccountHeaderPresenter(View container) {
75 mContext = container.getContext();
76 mAccountHeaderContainer = container;
Marcus Hagerott7217e692016-11-10 10:18:28 -080077 // mAccountHeaderType is optional and may not be in the container view in which case
78 // the variable will be null
Marcus Hagerott819214d2016-09-29 14:58:27 -070079 mAccountHeaderType = (TextView) container.findViewById(R.id.account_type);
80 mAccountHeaderName = (TextView) container.findViewById(R.id.account_name);
81 mAccountHeaderIcon = (ImageView) container.findViewById(R.id.account_type_icon);
82 mAccountHeaderExpanderIcon = (ImageView) container.findViewById(R.id.account_expander_icon);
Marcus Hagerott819214d2016-09-29 14:58:27 -070083 }
84
85 public void setObserver(Observer observer) {
86 mObserver = observer;
87 }
88
89 public void setCurrentAccount(@NonNull AccountWithDataSet account) {
90 if (mCurrentAccount != null && mCurrentAccount.equals(account)) {
91 return;
92 }
93 mCurrentAccount = account;
94 if (mObserver != null) {
95 mObserver.onChange(this);
96 }
97 updateDisplayedAccount();
98 }
99
Marcus Hagerott75895e72016-12-12 17:21:57 -0800100 public void setAccounts(List<AccountInfo> accounts) {
Marcus Hagerotte7a71cb2016-12-09 16:26:14 -0800101 mAccounts = accounts;
Marcus Hagerotte7a71cb2016-12-09 16:26:14 -0800102 // If the current account was removed just switch to the next one in the list.
Marcus Hagerott75895e72016-12-12 17:21:57 -0800103 if (mCurrentAccount != null && !AccountInfo.contains(mAccounts, mCurrentAccount)) {
104 mCurrentAccount = mAccounts.isEmpty() ? null : accounts.get(0).getAccount();
Marcus Hagerotte7a71cb2016-12-09 16:26:14 -0800105 mObserver.onChange(this);
106 }
107 updateDisplayedAccount();
108 }
109
Marcus Hagerott819214d2016-09-29 14:58:27 -0700110 public AccountWithDataSet getCurrentAccount() {
Marcus Hagerott75895e72016-12-12 17:21:57 -0800111 return mCurrentAccount != null ? mCurrentAccount : null;
Marcus Hagerott819214d2016-09-29 14:58:27 -0700112 }
113
Marcus Hagerott73b283f2016-10-21 15:42:00 -0700114 public void onSaveInstanceState(Bundle outState) {
115 outState.putParcelable(KEY_SELECTED_ACCOUNT, mCurrentAccount);
116 }
117
118 public void onRestoreInstanceState(Bundle savedInstanceState) {
119 if (savedInstanceState == null) return;
120 if (mCurrentAccount == null) {
121 mCurrentAccount = savedInstanceState.getParcelable(KEY_SELECTED_ACCOUNT);
122 }
123 updateDisplayedAccount();
124 }
125
Marcus Hagerott819214d2016-09-29 14:58:27 -0700126 private void updateDisplayedAccount() {
127 mAccountHeaderContainer.setVisibility(View.GONE);
128 if (mCurrentAccount == null) return;
Marcus Hagerotte7a71cb2016-12-09 16:26:14 -0800129 if (mAccounts == null) return;
Marcus Hagerott819214d2016-09-29 14:58:27 -0700130
Marcus Hagerott75895e72016-12-12 17:21:57 -0800131 final String accountLabel = getAccountLabel(mCurrentAccount);
Marcus Hagerott819214d2016-09-29 14:58:27 -0700132
Marcus Hagerottc2093f32016-12-12 10:18:12 -0800133 if (mAccounts.size() > 1) {
Marcus Hagerott819214d2016-09-29 14:58:27 -0700134 addAccountSelector(accountLabel);
135 } else {
136 addAccountHeader(accountLabel);
137 }
138 }
139
140 private void addAccountHeader(String accountLabel) {
141 mAccountHeaderContainer.setVisibility(View.VISIBLE);
142
143 // Set the account name
144 mAccountHeaderName.setVisibility(View.VISIBLE);
145 mAccountHeaderName.setText(accountLabel);
146
147 // Set the account type
148 final String selectorTitle = mContext.getResources().getString(mSelectorTitle);
Marcus Hagerott7217e692016-11-10 10:18:28 -0800149 if (mAccountHeaderType != null) {
150 mAccountHeaderType.setText(selectorTitle);
151 }
Marcus Hagerott819214d2016-09-29 14:58:27 -0700152
Marcus Hagerott75895e72016-12-12 17:21:57 -0800153 final AccountInfo accountInfo = AccountInfo.getAccount(mAccounts, mCurrentAccount);
154
Marcus Hagerott819214d2016-09-29 14:58:27 -0700155 // Set the icon
Marcus Hagerott75895e72016-12-12 17:21:57 -0800156 mAccountHeaderIcon.setImageDrawable(accountInfo.getIcon());
Marcus Hagerott819214d2016-09-29 14:58:27 -0700157
158 // Set the content description
159 mAccountHeaderContainer.setContentDescription(
160 EditorUiUtils.getAccountInfoContentDescription(accountLabel,
161 selectorTitle));
162 }
163
164 private void addAccountSelector(CharSequence nameLabel) {
165 final View.OnClickListener onClickListener = new View.OnClickListener() {
166 @Override
167 public void onClick(View v) {
Marcus Hagerott73b283f2016-10-21 15:42:00 -0700168 showPopup();
Marcus Hagerott819214d2016-09-29 14:58:27 -0700169 }
170 };
171 setUpAccountSelector(nameLabel.toString(), onClickListener);
172 }
173
Marcus Hagerott73b283f2016-10-21 15:42:00 -0700174 private void showPopup() {
175 final ListPopupWindow popup = new ListPopupWindow(mContext);
176 final AccountsListAdapter adapter =
Marcus Hagerottc2093f32016-12-12 10:18:12 -0800177 new AccountsListAdapter(mContext, mAccounts, mCurrentAccount);
Marcus Hagerott73b283f2016-10-21 15:42:00 -0700178 popup.setWidth(mAccountHeaderContainer.getWidth());
179 popup.setAnchorView(mAccountHeaderContainer);
180 popup.setAdapter(adapter);
181 popup.setModal(true);
182 popup.setInputMethodMode(ListPopupWindow.INPUT_METHOD_NOT_NEEDED);
183 popup.setOnItemClickListener(new AdapterView.OnItemClickListener() {
184 @Override
185 public void onItemClick(AdapterView<?> parent, View view, int position,
186 long id) {
187 UiClosables.closeQuietly(popup);
188 final AccountWithDataSet newAccount = adapter.getItem(position);
189 setCurrentAccount(newAccount);
Marcus Hagerottd3869c72016-11-21 11:13:44 -0800190 // Make sure the new selection will be announced once it's changed
191 mAccountHeaderContainer.setAccessibilityLiveRegion(
192 View.ACCESSIBILITY_LIVE_REGION_POLITE);
Marcus Hagerott73b283f2016-10-21 15:42:00 -0700193 }
194 });
195 mAccountHeaderContainer.post(new Runnable() {
196 @Override
197 public void run() {
198 popup.show();
199 }
200 });
201 }
202
Marcus Hagerott819214d2016-09-29 14:58:27 -0700203 private void setUpAccountSelector(String nameLabel, View.OnClickListener listener) {
204 addAccountHeader(nameLabel);
205 // Add handlers for choosing another account to save to.
206 mAccountHeaderExpanderIcon.setVisibility(View.VISIBLE);
Marcus Hagerott4b112322016-11-18 10:09:32 -0800207 // Add the listener to the icon so that it will be announced by talkback as a clickable
208 // element
209 mAccountHeaderExpanderIcon.setOnClickListener(listener);
Marcus Hagerott819214d2016-09-29 14:58:27 -0700210 mAccountHeaderContainer.setOnClickListener(listener);
211 }
212
Marcus Hagerott75895e72016-12-12 17:21:57 -0800213 private String getAccountLabel(AccountWithDataSet account) {
214 final AccountInfo accountInfo = AccountInfo.getAccount(mAccounts, account);
215 return accountInfo != null ? accountInfo.getNameLabel().toString() : null;
Marcus Hagerott819214d2016-09-29 14:58:27 -0700216 }
217}