1 /* 2 * Copyright (C) 2022 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.providers.media.photopicker.ui.settings; 18 19 import static com.android.providers.media.MediaApplication.getConfigStore; 20 21 import static java.util.Objects.requireNonNull; 22 23 import android.content.Context; 24 import android.os.Bundle; 25 import android.os.UserHandle; 26 import android.text.TextUtils; 27 import android.widget.Toast; 28 29 import androidx.annotation.NonNull; 30 import androidx.annotation.UiThread; 31 import androidx.lifecycle.ViewModel; 32 import androidx.lifecycle.ViewModelProvider; 33 import androidx.preference.Preference; 34 import androidx.preference.PreferenceFragmentCompat; 35 import androidx.preference.PreferenceScreen; 36 37 import com.android.providers.media.R; 38 import com.android.providers.media.photopicker.data.model.UserId; 39 import com.android.settingslib.widget.SelectorWithWidgetPreference; 40 41 /** 42 * This fragment will display a list of available cloud providers for the profile selected. 43 * Users can view or change the preferred cloud provider media app. 44 */ 45 public class SettingsCloudMediaSelectFragment extends PreferenceFragmentCompat 46 implements SelectorWithWidgetPreference.OnClickListener { 47 public static final String EXTRA_TAB_USER_ID = "user_id"; 48 private static final String TAG = "SettingsCMSelectFgmt"; 49 50 @NonNull 51 private UserId mUserId; 52 @NonNull 53 private Context mContext; 54 @NonNull 55 private SettingsCloudMediaViewModel mSettingsCloudMediaViewModel; 56 57 @Override onAttach(@onNull Context context)58 public void onAttach(@NonNull Context context) { 59 super.onAttach(context); 60 61 mContext = requireNonNull(context); 62 mUserId = requireNonNull(getUserId()); 63 mSettingsCloudMediaViewModel = createViewModel(); 64 } 65 66 @Override onResume()67 public void onResume() { 68 super.onResume(); 69 70 mSettingsCloudMediaViewModel.loadAccountNameAsync(); 71 } 72 73 @UiThread 74 @Override onRadioButtonClicked(@onNull SelectorWithWidgetPreference selectedPref)75 public void onRadioButtonClicked(@NonNull SelectorWithWidgetPreference selectedPref) { 76 final String selectedProviderKey = selectedPref.getKey(); 77 // Check if current provider is different from the selected provider. 78 if (!TextUtils.equals(selectedProviderKey, 79 mSettingsCloudMediaViewModel.getSelectedPreferenceKey())) { 80 final boolean success = 81 mSettingsCloudMediaViewModel.updateSelectedProvider(selectedProviderKey); 82 if (success) { 83 updateSelectedRadioButton(); 84 } else { 85 Toast.makeText(getContext(), 86 R.string.picker_settings_toast_error, Toast.LENGTH_SHORT).show(); 87 } 88 } 89 } 90 91 @Override onCreatePreferences(Bundle savedInstanceState, String rootKey)92 public void onCreatePreferences(Bundle savedInstanceState, String rootKey) { 93 super.addPreferencesFromResource(R.xml.pref_screen_picker_settings); 94 95 mSettingsCloudMediaViewModel.loadData(getConfigStore()); 96 observeAccountNameChanges(); 97 refreshUI(); 98 } 99 100 @UiThread refreshUI()101 private void refreshUI() { 102 final PreferenceScreen screen = getPreferenceScreen(); 103 resetPreferenceScreen(screen); 104 105 screen.addPreference(buildTitlePreference()); 106 for (CloudMediaProviderOption provider : 107 mSettingsCloudMediaViewModel.getProviderOptions()) { 108 screen.addPreference(buildProviderOptionPreference(provider)); 109 } 110 111 updateSelectedRadioButton(); 112 } 113 observeAccountNameChanges()114 private void observeAccountNameChanges() { 115 mSettingsCloudMediaViewModel.getCurrentProviderAccount() 116 .observe(this, accountDetails -> { 117 // Only update current account name on the UI if cloud provider linked to the 118 // account name matches the current provider. 119 if (accountDetails != null 120 && accountDetails.getCloudProviderAuthority() 121 .equals(mSettingsCloudMediaViewModel.getSelectedProviderAuthority())) { 122 final Preference selectedPref = findPreference( 123 mSettingsCloudMediaViewModel.getSelectedPreferenceKey()); 124 // TODO(b/262002538): {@code selectedPref} could be null if the selected 125 // cloud provider is not in the allowed list. This is not something a 126 // typical user will encounter. 127 if (selectedPref != null) { 128 selectedPref.setSummary(accountDetails.getCloudProviderAccountName()); 129 } 130 } 131 }); 132 } 133 134 @UiThread updateSelectedRadioButton()135 private void updateSelectedRadioButton() { 136 final String selectedPreferenceKey = 137 mSettingsCloudMediaViewModel.getSelectedPreferenceKey(); 138 for (CloudMediaProviderOption providerOption 139 : mSettingsCloudMediaViewModel.getProviderOptions()) { 140 final Preference pref = findPreference(providerOption.getKey()); 141 if (pref instanceof SelectorWithWidgetPreference) { 142 final SelectorWithWidgetPreference providerPref = 143 (SelectorWithWidgetPreference) pref; 144 145 final boolean newSelectionState = 146 TextUtils.equals(providerPref.getKey(), selectedPreferenceKey); 147 providerPref.setChecked(newSelectionState); 148 149 providerPref.setSummary(null); 150 if (newSelectionState) { 151 mSettingsCloudMediaViewModel.loadAccountNameAsync(); 152 } 153 } 154 } 155 } 156 157 @NonNull buildProviderOptionPreference(@onNull CloudMediaProviderOption provider)158 private Preference buildProviderOptionPreference(@NonNull CloudMediaProviderOption provider) { 159 final SelectorWithWidgetPreference pref = 160 new SelectorWithWidgetPreference(getPrefContext()); 161 162 // Preferences are stored in SharedPreferences by default. This feature is disabled 163 // as Cloud picker preferences are stored in SharedPreferences separately. 164 pref.setPersistent(false); 165 pref.setTitle(provider.getLabel()); 166 pref.setIcon(provider.getIcon()); 167 pref.setKey(provider.getKey()); 168 pref.setOnClickListener(this); 169 return pref; 170 } 171 172 @NonNull buildTitlePreference()173 private Preference buildTitlePreference() { 174 final Preference titlePref = new Preference(getPrefContext()); 175 titlePref.setTitle(R.string.picker_settings_selection_message); 176 titlePref.setSelectable(false); 177 titlePref.setPersistent(false); 178 titlePref.setLayoutResource(R.layout.pref_settings_cloud_select_title); 179 return titlePref; 180 } 181 getPrefContext()182 private Context getPrefContext() { 183 return getPreferenceManager().getContext(); 184 } 185 resetPreferenceScreen(PreferenceScreen screen)186 private void resetPreferenceScreen(PreferenceScreen screen) { 187 screen.removeAll(); 188 } 189 190 @NonNull getUserId()191 private UserId getUserId() { 192 if (getArguments() != null && getArguments().containsKey(EXTRA_TAB_USER_ID)) { 193 final int currentUID = getArguments().getInt(EXTRA_TAB_USER_ID); 194 final UserHandle userHandle = UserHandle.of(currentUID); 195 return UserId.of(userHandle); 196 } else { 197 throw new IllegalArgumentException( 198 "User Id for a SettingsCloudMediaSelectFragment is not set."); 199 } 200 } 201 202 @NonNull createViewModel()203 private SettingsCloudMediaViewModel createViewModel() { 204 final ViewModelProvider viewModelProvider = 205 new ViewModelProvider( 206 requireActivity(), 207 new SettingsViewModelFactory(mContext, mUserId) 208 ); 209 final String viewModelKey = Integer.toString(mUserId.getIdentifier()); 210 211 return viewModelProvider.get(viewModelKey, SettingsCloudMediaViewModel.class); 212 } 213 214 private static class SettingsViewModelFactory implements ViewModelProvider.Factory { 215 @NonNull 216 private final Context mContext; 217 @NonNull 218 private final UserId mUserId; 219 SettingsViewModelFactory(@onNull Context context, @NonNull UserId userId)220 SettingsViewModelFactory(@NonNull Context context, @NonNull UserId userId) { 221 mContext = requireNonNull(context); 222 mUserId = requireNonNull(userId); 223 } 224 225 @NonNull 226 @Override create(@onNull Class<T> modelClass)227 public <T extends ViewModel> T create(@NonNull Class<T> modelClass) { 228 return (T) new SettingsCloudMediaViewModel(mContext, mUserId); 229 } 230 } 231 }