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.Comparator; 39 import java.util.HashSet; 40 import java.util.List; 41 import java.util.Set; 42 43 /** 44 * Controller for showing the user the list of accounts they can add. 45 * 46 * <p>Largely derived from {@link com.android.settings.accounts.ChooseAccountActivity} 47 */ 48 public class ChooseAccountPreferenceController extends 49 PreferenceController<PreferenceGroup> implements ActivityResultCallback { 50 @VisibleForTesting 51 static final int ADD_ACCOUNT_REQUEST_CODE = 100; 52 53 private AccountTypesHelper mAccountTypesHelper; 54 private ArrayMap<String, AuthenticatorDescriptionPreference> mPreferences = new ArrayMap<>(); 55 private boolean mIsStarted = false; 56 private boolean mHasPendingBack = false; 57 ChooseAccountPreferenceController(Context context, String preferenceKey, FragmentController fragmentController, CarUxRestrictions uxRestrictions)58 public ChooseAccountPreferenceController(Context context, String preferenceKey, 59 FragmentController fragmentController, CarUxRestrictions uxRestrictions) { 60 super(context, preferenceKey, fragmentController, uxRestrictions); 61 mAccountTypesHelper = new AccountTypesHelper(context); 62 mAccountTypesHelper.setOnChangeListener(this::forceUpdateAccountsCategory); 63 } 64 65 /** Sets the authorities that the user has. */ setAuthorities(List<String> authorities)66 public void setAuthorities(List<String> authorities) { 67 mAccountTypesHelper.setAuthorities(authorities); 68 } 69 70 /** Sets the filter for accounts that should be shown. */ setAccountTypesFilter(Set<String> accountTypesFilter)71 public void setAccountTypesFilter(Set<String> accountTypesFilter) { 72 mAccountTypesHelper.setAccountTypesFilter(accountTypesFilter); 73 } 74 75 /** Sets the filter for accounts that should NOT be shown. */ setAccountTypesExclusionFilter(Set<String> accountTypesExclusionFilterFilter)76 protected void setAccountTypesExclusionFilter(Set<String> accountTypesExclusionFilterFilter) { 77 mAccountTypesHelper.setAccountTypesExclusionFilter(accountTypesExclusionFilterFilter); 78 } 79 80 @Override getPreferenceType()81 protected Class<PreferenceGroup> getPreferenceType() { 82 return PreferenceGroup.class; 83 } 84 85 @Override updateState(PreferenceGroup preferenceGroup)86 protected void updateState(PreferenceGroup preferenceGroup) { 87 mAccountTypesHelper.forceUpdate(); 88 } 89 90 /** 91 * Registers the account update callback. 92 */ 93 @Override onStartInternal()94 protected void onStartInternal() { 95 mIsStarted = true; 96 mAccountTypesHelper.listenToAccountUpdates(); 97 98 if (mHasPendingBack) { 99 mHasPendingBack = false; 100 101 // Post the fragment navigation because FragmentManager may still be executing 102 // transactions during onStart. 103 new Handler().post(() -> getFragmentController().goBack()); 104 } 105 } 106 107 /** 108 * Unregisters the account update callback. 109 */ 110 @Override onStopInternal()111 protected void onStopInternal() { 112 mAccountTypesHelper.stopListeningToAccountUpdates(); 113 mIsStarted = false; 114 } 115 116 /** Forces a refresh of the authenticator description preferences. */ forceUpdateAccountsCategory()117 private void forceUpdateAccountsCategory() { 118 Set<String> preferencesToRemove = new HashSet<>(mPreferences.keySet()); 119 List<AuthenticatorDescriptionPreference> preferences = 120 getAuthenticatorDescriptionPreferences(preferencesToRemove); 121 // Add all preferences that aren't already shown 122 for (int i = 0; i < preferences.size(); i++) { 123 AuthenticatorDescriptionPreference preference = preferences.get(i); 124 preference.setOrder(i); 125 String key = preference.getKey(); 126 getPreference().addPreference(preference); 127 mPreferences.put(key, preference); 128 } 129 130 // Remove all preferences that should no longer be shown 131 for (String key : preferencesToRemove) { 132 getPreference().removePreference(mPreferences.get(key)); 133 mPreferences.remove(key); 134 } 135 } 136 137 /** 138 * Returns a list of preferences corresponding to the account types the user can add. 139 * 140 * <p> Derived from 141 * {@link com.android.settings.accounts.ChooseAccountActivity#onAuthDescriptionsUpdated} 142 * 143 * @param preferencesToRemove the current preferences shown; will contain the preferences that 144 * need to be removed from the screen after method execution 145 */ getAuthenticatorDescriptionPreferences( Set<String> preferencesToRemove)146 private List<AuthenticatorDescriptionPreference> getAuthenticatorDescriptionPreferences( 147 Set<String> preferencesToRemove) { 148 ArrayList<AuthenticatorDescriptionPreference> authenticatorDescriptionPreferences = 149 new ArrayList<>(); 150 Set<String> authorizedAccountTypes = mAccountTypesHelper.getAuthorizedAccountTypes(); 151 // Create list of account providers to show on page. 152 for (String accountType : authorizedAccountTypes) { 153 CharSequence label = mAccountTypesHelper.getLabelForType(accountType); 154 Drawable icon = mAccountTypesHelper.getDrawableForType(accountType); 155 156 // Add a preference for the provider to the list and remove it from preferencesToRemove. 157 AuthenticatorDescriptionPreference preference = mPreferences.getOrDefault(accountType, 158 new AuthenticatorDescriptionPreference(getContext(), accountType, label, icon)); 159 preference.setOnPreferenceClickListener( 160 pref -> { 161 Intent intent = AddAccountActivity.createAddAccountActivityIntent( 162 getContext(), preference.getAccountType()); 163 getFragmentController().startActivityForResult(intent, 164 ADD_ACCOUNT_REQUEST_CODE, /* callback= */ this); 165 return true; 166 }); 167 authenticatorDescriptionPreferences.add(preference); 168 preferencesToRemove.remove(accountType); 169 } 170 171 Collections.sort(authenticatorDescriptionPreferences, Comparator.comparing( 172 (AuthenticatorDescriptionPreference a) -> a.getTitle().toString())); 173 174 return authenticatorDescriptionPreferences; 175 } 176 177 /** Used for testing to trigger an account update. */ 178 @VisibleForTesting getAuthenticatorHelper()179 AuthenticatorHelper getAuthenticatorHelper() { 180 return mAccountTypesHelper.getAuthenticatorHelper(); 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 (mIsStarted) { 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