1 /* 2 * Copyright (C) 2018 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 com.android.car.settings.accounts; 18 19 import android.car.drivingstate.CarUxRestrictions; 20 import android.content.Context; 21 import android.content.Intent; 22 import android.graphics.drawable.Drawable; 23 import android.os.Handler; 24 25 import androidx.annotation.Nullable; 26 import androidx.annotation.VisibleForTesting; 27 import androidx.collection.ArrayMap; 28 import androidx.preference.PreferenceGroup; 29 30 import com.android.car.settings.common.ActivityResultCallback; 31 import com.android.car.settings.common.FragmentController; 32 import com.android.car.settings.common.PreferenceController; 33 import com.android.car.ui.preference.CarUiPreference; 34 import com.android.settingslib.accounts.AuthenticatorHelper; 35 36 import java.util.ArrayList; 37 import java.util.Collections; 38 import java.util.HashSet; 39 import java.util.List; 40 import java.util.Set; 41 42 /** 43 * Controller for showing the user the list of accounts they can add. 44 * 45 * <p>Largely derived from {@link com.android.settings.accounts.ChooseAccountActivity} 46 */ 47 public class ChooseAccountPreferenceController extends 48 PreferenceController<PreferenceGroup> implements ActivityResultCallback { 49 @VisibleForTesting 50 static final int ADD_ACCOUNT_REQUEST_CODE = 100; 51 52 private AccountTypesHelper mAccountTypesHelper; 53 private ArrayMap<String, AuthenticatorDescriptionPreference> mPreferences = new ArrayMap<>(); 54 private boolean mHasPendingBack = false; 55 ChooseAccountPreferenceController(Context context, String preferenceKey, FragmentController fragmentController, CarUxRestrictions uxRestrictions)56 public ChooseAccountPreferenceController(Context context, String preferenceKey, 57 FragmentController fragmentController, CarUxRestrictions uxRestrictions) { 58 super(context, preferenceKey, fragmentController, uxRestrictions); 59 mAccountTypesHelper = new AccountTypesHelper(context); 60 mAccountTypesHelper.setOnChangeListener(this::forceUpdateAccountsCategory); 61 } 62 63 /** Sets the authorities that the user has. */ setAuthorities(List<String> authorities)64 public void setAuthorities(List<String> authorities) { 65 mAccountTypesHelper.setAuthorities(authorities); 66 } 67 68 /** Sets the filter for accounts that should be shown. */ setAccountTypesFilter(Set<String> accountTypesFilter)69 public void setAccountTypesFilter(Set<String> accountTypesFilter) { 70 mAccountTypesHelper.setAccountTypesFilter(accountTypesFilter); 71 } 72 73 /** Sets the filter for accounts that should NOT be shown. */ setAccountTypesExclusionFilter(Set<String> accountTypesExclusionFilterFilter)74 protected void setAccountTypesExclusionFilter(Set<String> accountTypesExclusionFilterFilter) { 75 mAccountTypesHelper.setAccountTypesExclusionFilter(accountTypesExclusionFilterFilter); 76 } 77 78 @Override getPreferenceType()79 protected Class<PreferenceGroup> getPreferenceType() { 80 return PreferenceGroup.class; 81 } 82 83 @Override updateState(PreferenceGroup preferenceGroup)84 protected void updateState(PreferenceGroup preferenceGroup) { 85 mAccountTypesHelper.forceUpdate(); 86 } 87 88 /** 89 * Registers the account update callback. 90 */ 91 @Override onStartInternal()92 protected void onStartInternal() { 93 mAccountTypesHelper.listenToAccountUpdates(); 94 95 if (mHasPendingBack) { 96 mHasPendingBack = false; 97 98 // Post the fragment navigation because FragmentManager may still be executing 99 // transactions during onStart. 100 new Handler().post(() -> getFragmentController().goBack()); 101 } 102 } 103 104 /** 105 * Unregisters the account update callback. 106 */ 107 @Override onStopInternal()108 protected void onStopInternal() { 109 mAccountTypesHelper.stopListeningToAccountUpdates(); 110 } 111 112 /** Forces a refresh of the authenticator description preferences. */ forceUpdateAccountsCategory()113 private void forceUpdateAccountsCategory() { 114 Set<String> preferencesToRemove = new HashSet<>(mPreferences.keySet()); 115 List<AuthenticatorDescriptionPreference> preferences = 116 getAuthenticatorDescriptionPreferences(preferencesToRemove); 117 // Add all preferences that aren't already shown 118 for (int i = 0; i < preferences.size(); i++) { 119 AuthenticatorDescriptionPreference preference = preferences.get(i); 120 preference.setOrder(i); 121 String key = preference.getKey(); 122 getPreference().addPreference(preference); 123 mPreferences.put(key, preference); 124 } 125 126 // Remove all preferences that should no longer be shown 127 for (String key : preferencesToRemove) { 128 getPreference().removePreference(mPreferences.get(key)); 129 mPreferences.remove(key); 130 } 131 } 132 133 /** 134 * Returns a list of preferences corresponding to the account types the user can add. 135 * 136 * <p> Derived from 137 * {@link com.android.settings.accounts.ChooseAccountActivity#onAuthDescriptionsUpdated} 138 * 139 * @param preferencesToRemove the current preferences shown; will contain the preferences that 140 * need to be removed from the screen after method execution 141 */ getAuthenticatorDescriptionPreferences( Set<String> preferencesToRemove)142 private List<AuthenticatorDescriptionPreference> getAuthenticatorDescriptionPreferences( 143 Set<String> preferencesToRemove) { 144 ArrayList<AuthenticatorDescriptionPreference> authenticatorDescriptionPreferences = 145 new ArrayList<>(); 146 Set<String> authorizedAccountTypes = mAccountTypesHelper.getAuthorizedAccountTypes(); 147 // Create list of account providers to show on page. 148 for (String accountType : authorizedAccountTypes) { 149 CharSequence label = mAccountTypesHelper.getLabelForType(accountType); 150 Drawable icon = mAccountTypesHelper.getDrawableForType(accountType); 151 152 // Add a preference for the provider to the list and remove it from preferencesToRemove. 153 AuthenticatorDescriptionPreference preference = mPreferences.getOrDefault(accountType, 154 new AuthenticatorDescriptionPreference(getContext(), accountType, label, icon)); 155 preference.setOnPreferenceClickListener( 156 pref -> { 157 Intent intent = AddAccountActivity.createAddAccountActivityIntent( 158 getContext(), preference.getAccountType()); 159 getFragmentController().startActivityForResult(intent, 160 ADD_ACCOUNT_REQUEST_CODE, /* callback= */ this); 161 return true; 162 }); 163 authenticatorDescriptionPreferences.add(preference); 164 preferencesToRemove.remove(accountType); 165 } 166 167 Collections.sort(authenticatorDescriptionPreferences); 168 169 return authenticatorDescriptionPreferences; 170 } 171 172 /** Used for testing to trigger an account update. */ 173 @VisibleForTesting getAccountTypesHelper()174 AccountTypesHelper getAccountTypesHelper() { 175 return mAccountTypesHelper; 176 } 177 178 @VisibleForTesting setAuthenticatorHelper(AuthenticatorHelper helper)179 void setAuthenticatorHelper(AuthenticatorHelper helper) { 180 mAccountTypesHelper.setAuthenticatorHelper(helper); 181 } 182 183 @Override processActivityResult(int requestCode, int resultCode, @Nullable Intent data)184 public void processActivityResult(int requestCode, int resultCode, @Nullable Intent data) { 185 if (requestCode == ADD_ACCOUNT_REQUEST_CODE) { 186 if (isStarted()) { 187 getFragmentController().goBack(); 188 } else { 189 mHasPendingBack = true; 190 } 191 } 192 } 193 194 /** Handles adding accounts. */ 195 interface AddAccountListener { 196 /** Handles adding an account. */ addAccount(String accountType)197 void addAccount(String accountType); 198 } 199 200 private static class AuthenticatorDescriptionPreference extends CarUiPreference { 201 private final String mType; 202 AuthenticatorDescriptionPreference(Context context, String accountType, CharSequence label, Drawable icon)203 AuthenticatorDescriptionPreference(Context context, String accountType, CharSequence label, 204 Drawable icon) { 205 super(context); 206 mType = accountType; 207 208 setKey(accountType); 209 setTitle(label); 210 setIcon(icon); 211 setShowChevron(false); 212 } 213 getAccountType()214 private String getAccountType() { 215 return mType; 216 } 217 } 218 } 219