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