1 /* 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 */ 16 package com.android.contacts.editor; 17 18 import android.content.Context; 19 import android.os.Bundle; 20 import android.support.annotation.NonNull; 21 import android.support.annotation.StringRes; 22 import android.view.View; 23 import android.widget.AdapterView; 24 import android.widget.ImageView; 25 import android.widget.ListPopupWindow; 26 import android.widget.TextView; 27 28 import com.android.contacts.R; 29 import com.android.contacts.model.account.AccountInfo; 30 import com.android.contacts.model.account.AccountWithDataSet; 31 import com.android.contacts.util.AccountsListAdapter; 32 import com.android.contacts.util.UiClosables; 33 34 import 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 */ 42 public class AccountHeaderPresenter { 43 44 private static final String KEY_SELECTED_ACCOUNT = "accountHeaderSelectedAccount"; 45 46 public interface Observer { onChange(AccountHeaderPresenter sender)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; 57 58 private List<AccountInfo> mAccounts; 59 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 AccountHeaderPresenter(View container)74 public AccountHeaderPresenter(View container) { 75 mContext = container.getContext(); 76 mAccountHeaderContainer = container; 77 // mAccountHeaderType is optional and may not be in the container view in which case 78 // the variable will be null 79 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); 83 } 84 setObserver(Observer observer)85 public void setObserver(Observer observer) { 86 mObserver = observer; 87 } 88 setCurrentAccount(@onNull AccountWithDataSet account)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 setAccounts(List<AccountInfo> accounts)100 public void setAccounts(List<AccountInfo> accounts) { 101 mAccounts = accounts; 102 // If the current account hasn't been set or it has been removed just use the first 103 // account. 104 if (mCurrentAccount == null || !AccountInfo.contains(mAccounts, mCurrentAccount)) { 105 mCurrentAccount = mAccounts.isEmpty() ? null : accounts.get(0).getAccount(); 106 mObserver.onChange(this); 107 } 108 updateDisplayedAccount(); 109 } 110 getCurrentAccount()111 public AccountWithDataSet getCurrentAccount() { 112 return mCurrentAccount != null ? mCurrentAccount : null; 113 } 114 onSaveInstanceState(Bundle outState)115 public void onSaveInstanceState(Bundle outState) { 116 outState.putParcelable(KEY_SELECTED_ACCOUNT, mCurrentAccount); 117 } 118 onRestoreInstanceState(Bundle savedInstanceState)119 public void onRestoreInstanceState(Bundle savedInstanceState) { 120 if (savedInstanceState == null) return; 121 if (mCurrentAccount == null) { 122 mCurrentAccount = savedInstanceState.getParcelable(KEY_SELECTED_ACCOUNT); 123 } 124 updateDisplayedAccount(); 125 } 126 updateDisplayedAccount()127 private void updateDisplayedAccount() { 128 mAccountHeaderContainer.setVisibility(View.GONE); 129 if (mCurrentAccount == null) return; 130 if (mAccounts == null) return; 131 132 final String accountLabel = getAccountLabel(mCurrentAccount); 133 134 if (mAccounts.size() > 1) { 135 addAccountSelector(accountLabel); 136 } else { 137 addAccountHeader(accountLabel); 138 } 139 } 140 addAccountHeader(String accountLabel)141 private void addAccountHeader(String accountLabel) { 142 mAccountHeaderContainer.setVisibility(View.VISIBLE); 143 144 // Set the account name 145 mAccountHeaderName.setVisibility(View.VISIBLE); 146 mAccountHeaderName.setText(accountLabel); 147 148 // Set the account type 149 final String selectorTitle = mContext.getResources().getString(mSelectorTitle); 150 if (mAccountHeaderType != null) { 151 mAccountHeaderType.setText(selectorTitle); 152 } 153 154 final AccountInfo accountInfo = AccountInfo.getAccount(mAccounts, mCurrentAccount); 155 156 // Set the icon 157 mAccountHeaderIcon.setImageDrawable(accountInfo.getIcon()); 158 159 // Set the content description 160 mAccountHeaderContainer.setContentDescription( 161 EditorUiUtils.getAccountInfoContentDescription(accountLabel, 162 selectorTitle)); 163 } 164 addAccountSelector(CharSequence nameLabel)165 private void addAccountSelector(CharSequence nameLabel) { 166 final View.OnClickListener onClickListener = new View.OnClickListener() { 167 @Override 168 public void onClick(View v) { 169 showPopup(); 170 } 171 }; 172 setUpAccountSelector(nameLabel.toString(), onClickListener); 173 } 174 showPopup()175 private void showPopup() { 176 final ListPopupWindow popup = new ListPopupWindow(mContext); 177 final AccountsListAdapter adapter = 178 new AccountsListAdapter(mContext, mAccounts, mCurrentAccount); 179 popup.setWidth(mAccountHeaderContainer.getWidth()); 180 popup.setAnchorView(mAccountHeaderContainer); 181 popup.setAdapter(adapter); 182 popup.setModal(true); 183 popup.setInputMethodMode(ListPopupWindow.INPUT_METHOD_NOT_NEEDED); 184 popup.setOnItemClickListener(new AdapterView.OnItemClickListener() { 185 @Override 186 public void onItemClick(AdapterView<?> parent, View view, int position, 187 long id) { 188 UiClosables.closeQuietly(popup); 189 final AccountWithDataSet newAccount = adapter.getItem(position); 190 setCurrentAccount(newAccount); 191 // Make sure the new selection will be announced once it's changed 192 mAccountHeaderContainer.setAccessibilityLiveRegion( 193 View.ACCESSIBILITY_LIVE_REGION_POLITE); 194 } 195 }); 196 mAccountHeaderContainer.post(new Runnable() { 197 @Override 198 public void run() { 199 popup.show(); 200 } 201 }); 202 } 203 setUpAccountSelector(String nameLabel, View.OnClickListener listener)204 private void setUpAccountSelector(String nameLabel, View.OnClickListener listener) { 205 addAccountHeader(nameLabel); 206 // Add handlers for choosing another account to save to. 207 mAccountHeaderExpanderIcon.setVisibility(View.VISIBLE); 208 // Add the listener to the icon so that it will be announced by talkback as a clickable 209 // element 210 mAccountHeaderExpanderIcon.setOnClickListener(listener); 211 mAccountHeaderContainer.setOnClickListener(listener); 212 } 213 getAccountLabel(AccountWithDataSet account)214 private String getAccountLabel(AccountWithDataSet account) { 215 final AccountInfo accountInfo = AccountInfo.getAccount(mAccounts, account); 216 return accountInfo != null ? accountInfo.getNameLabel().toString() : null; 217 } 218 } 219