summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorShubhi Saxena <shubhisaxena@google.com>2023-02-27 13:56:35 +0000
committerAndroid (Google) Code Review <android-gerrit@google.com>2023-02-27 13:56:35 +0000
commit7dd2ef7631703b1064ee30beecd7338ea0a38ce9 (patch)
treecb856ff7b383db7304dd2b5e287b1ec5bbb393aa
parentba096ab4695b23c485d161d4003c47b6a27b7683 (diff)
parent535ba803eaa9946019ada37c8a40f3f057b953e1 (diff)
downloadMediaProvider-7dd2ef7631703b1064ee30beecd7338ea0a38ce9.tar.gz
Merge "Add unit tests for Picker Settings" into tm-mainline-prod
-rw-r--r--src/com/android/providers/media/photopicker/ui/settings/SettingsCloudMediaViewModel.java103
-rw-r--r--src/com/android/providers/media/photopicker/ui/settings/SettingsProfileSelectFragment.java4
-rw-r--r--src/com/android/providers/media/photopicker/util/CloudProviderUtils.java35
-rw-r--r--tests/src/com/android/providers/media/photopicker/ui/settings/SettingsCloudMediaViewModelTest.java213
4 files changed, 295 insertions, 60 deletions
diff --git a/src/com/android/providers/media/photopicker/ui/settings/SettingsCloudMediaViewModel.java b/src/com/android/providers/media/photopicker/ui/settings/SettingsCloudMediaViewModel.java
index b3e6ec994..a60a32823 100644
--- a/src/com/android/providers/media/photopicker/ui/settings/SettingsCloudMediaViewModel.java
+++ b/src/com/android/providers/media/photopicker/ui/settings/SettingsCloudMediaViewModel.java
@@ -17,10 +17,10 @@
package com.android.providers.media.photopicker.ui.settings;
import static android.provider.MediaStore.AUTHORITY;
-import static android.provider.MediaStore.EXTRA_CLOUD_PROVIDER;
-import static android.provider.MediaStore.GET_CLOUD_PROVIDER_CALL;
-import static android.provider.MediaStore.GET_CLOUD_PROVIDER_RESULT;
-import static android.provider.MediaStore.SET_CLOUD_PROVIDER_CALL;
+
+import static com.android.providers.media.photopicker.util.CloudProviderUtils.fetchProviderAuthority;
+import static com.android.providers.media.photopicker.util.CloudProviderUtils.getAllAvailableCloudProviders;
+import static com.android.providers.media.photopicker.util.CloudProviderUtils.persistSelectedProvider;
import static java.util.Objects.requireNonNull;
@@ -28,20 +28,18 @@ import android.content.ContentProviderClient;
import android.content.Context;
import android.content.pm.PackageManager;
import android.graphics.drawable.Drawable;
-import android.os.Bundle;
import android.os.UserHandle;
import android.util.Log;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
-import androidx.appcompat.content.res.AppCompatResources;
+import androidx.annotation.VisibleForTesting;
import androidx.lifecycle.ViewModel;
import com.android.providers.media.ConfigStore;
import com.android.providers.media.R;
import com.android.providers.media.photopicker.data.CloudProviderInfo;
import com.android.providers.media.photopicker.data.model.UserId;
-import com.android.providers.media.photopicker.util.CloudProviderUtils;
import java.util.ArrayList;
import java.util.List;
@@ -62,7 +60,9 @@ public class SettingsCloudMediaViewModel extends ViewModel {
@Nullable
private String mSelectedProviderAuthority;
- public SettingsCloudMediaViewModel(@NonNull Context context, @NonNull UserId userId) {
+ public SettingsCloudMediaViewModel(
+ @NonNull Context context,
+ @NonNull UserId userId) {
super();
mContext = requireNonNull(context);
@@ -100,10 +100,21 @@ public class SettingsCloudMediaViewModel extends ViewModel {
*/
public boolean updateSelectedProvider(@NonNull String newPreferenceKey) {
final String newCloudProvider = getProviderAuthority(newPreferenceKey);
- final boolean success = persistSelectedProvider(newCloudProvider);
- if (success) {
- mSelectedProviderAuthority = newCloudProvider;
- return true;
+ try (ContentProviderClient client = getContentProviderClient()) {
+ if (client == null) {
+ // This could happen when work profile is turned off after opening the Settings
+ // page. The work tab would still be visible but the MP process for work profile
+ // will not be running.
+ return false;
+ }
+ final boolean success =
+ persistSelectedProvider(client, newCloudProvider);
+ if (success) {
+ mSelectedProviderAuthority = newCloudProvider;
+ return true;
+ }
+ } catch (Exception e) {
+ Log.e(TAG, "Could not persist selected cloud provider", e);
}
return false;
}
@@ -128,14 +139,27 @@ public class SettingsCloudMediaViewModel extends ViewModel {
}
private void refreshSelectedProvider() {
- mSelectedProviderAuthority = fetchCurrentProviderAuthority();
+ try (ContentProviderClient client = getContentProviderClient()) {
+ if (client == null) {
+ // TODO(b/266927613): Handle the edge case where work profile is turned off
+ // while user is on the settings page but work tab's data is not fetched yet.
+ throw new IllegalArgumentException("Could not get selected cloud provider"
+ + " because Media Provider client is null.");
+ }
+ mSelectedProviderAuthority =
+ fetchProviderAuthority(client, /* default */ NONE_PREF_KEY);
+ } catch (Exception e) {
+ // Since displaying the current cloud provider is the core function of the Settings
+ // page, if we're not able to fetch this info, there is no point in displaying this
+ // activity.
+ throw new IllegalArgumentException("Could not get selected cloud provider", e);
+ }
}
@NonNull
private List<CloudMediaProviderOption> fetchProviderOptions(@NonNull ConfigStore configStore) {
// Get info of available cloud providers.
- List<CloudProviderInfo> cloudProviders =
- CloudProviderUtils.getAllAvailableCloudProviders(
+ List<CloudProviderInfo> cloudProviders = getAllAvailableCloudProviders(
mContext, configStore, UserHandle.of(mUserId.getIdentifier()));
return getProviderOptionsFromCloudProviderInfos(cloudProviders);
@@ -157,55 +181,18 @@ public class SettingsCloudMediaViewModel extends ViewModel {
@NonNull
private CloudMediaProviderOption getNoneProviderOption() {
- final Drawable nonePrefIcon = AppCompatResources
- .getDrawable(this.mContext, R.drawable.ic_cloud_picker_off);
- final String nonePrefLabel = this.mContext.getString(R.string.picker_settings_no_provider);
+ final Drawable nonePrefIcon = mContext.getDrawable(R.drawable.ic_cloud_picker_off);
+ final String nonePrefLabel = mContext.getString(R.string.picker_settings_no_provider);
return new CloudMediaProviderOption(NONE_PREF_KEY, nonePrefLabel, nonePrefIcon);
}
@Nullable
- private String fetchCurrentProviderAuthority() {
- try (ContentProviderClient client = getContentProviderClient()) {
- if (client == null) {
- // TODO(b/266927613): Handle the edge case where work profile is turned off while
- // user is on the settings page but work tab's data is not fetched yet.
- throw new IllegalArgumentException("Could not get selected cloud provider because "
- + "Media Provider client is null.");
- }
- final Bundle result = client.call(GET_CLOUD_PROVIDER_CALL,
- /* arg */ null, /* extras */ null);
- return result.getString(GET_CLOUD_PROVIDER_RESULT, NONE_PREF_KEY);
- } catch (Exception e) {
- // Since displaying the current cloud provider is the core function of the Settings
- // page, if we're not able to fetch this info, there is no point in displaying this
- // activity.
- throw new IllegalArgumentException("Could not get selected cloud provider", e);
- }
- }
-
- private boolean persistSelectedProvider(@Nullable String newCloudProvider) {
- try (ContentProviderClient client = getContentProviderClient()) {
- if (client == null) {
- // This could happen when work profile is turned off after opening the Settings
- // page. The work tab would still be visible but the MP process for work profile
- // will not be running.
- return false;
- }
- final Bundle input = new Bundle();
- input.putString(EXTRA_CLOUD_PROVIDER, newCloudProvider);
- client.call(SET_CLOUD_PROVIDER_CALL, /* arg */ null, /* extras */ input);
- return true;
- } catch (Exception e) {
- Log.e(TAG, "Could not persist selected cloud provider", e);
- return false;
- }
- }
-
- @Nullable
- private ContentProviderClient getContentProviderClient()
+ @VisibleForTesting
+ public ContentProviderClient getContentProviderClient()
throws PackageManager.NameNotFoundException {
- return mUserId.getContentResolver(mContext)
+ return mUserId
+ .getContentResolver(mContext)
.acquireUnstableContentProviderClient(AUTHORITY);
}
}
diff --git a/src/com/android/providers/media/photopicker/ui/settings/SettingsProfileSelectFragment.java b/src/com/android/providers/media/photopicker/ui/settings/SettingsProfileSelectFragment.java
index 9b10a1ed6..d49068437 100644
--- a/src/com/android/providers/media/photopicker/ui/settings/SettingsProfileSelectFragment.java
+++ b/src/com/android/providers/media/photopicker/ui/settings/SettingsProfileSelectFragment.java
@@ -81,8 +81,7 @@ public class SettingsProfileSelectFragment extends ProfileSelectFragment {
final int previouslySelectedTab = mSettingsViewModel.getSelectedTab();
if (previouslySelectedTab != SettingsViewModel.TAB_NOT_SET) {
// Selected tab state has previously been set in onPause() and we should restore it.
- final TabLayout.Tab tab = mTabLayout.getTabAt(previouslySelectedTab);
- tab.select();
+ mTabLayout.getTabAt(previouslySelectedTab).select();
}
}
@@ -121,6 +120,7 @@ public class SettingsProfileSelectFragment extends ProfileSelectFragment {
return fragment;
}
+ @UserIdInt
private int getTabUserId(int tabPosition) {
final UserIdManager userIdManager = mSettingsViewModel.getUserIdManager();
diff --git a/src/com/android/providers/media/photopicker/util/CloudProviderUtils.java b/src/com/android/providers/media/photopicker/util/CloudProviderUtils.java
index 4f5493e37..04c32165d 100644
--- a/src/com/android/providers/media/photopicker/util/CloudProviderUtils.java
+++ b/src/com/android/providers/media/photopicker/util/CloudProviderUtils.java
@@ -17,18 +17,26 @@
package com.android.providers.media.photopicker.util;
import static android.provider.CloudMediaProviderContract.MANAGE_CLOUD_MEDIA_PROVIDERS_PERMISSION;
+import static android.provider.MediaStore.EXTRA_CLOUD_PROVIDER;
+import static android.provider.MediaStore.GET_CLOUD_PROVIDER_CALL;
+import static android.provider.MediaStore.GET_CLOUD_PROVIDER_RESULT;
+import static android.provider.MediaStore.SET_CLOUD_PROVIDER_CALL;
+import android.content.ContentProviderClient;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ProviderInfo;
import android.content.pm.ResolveInfo;
+import android.os.Bundle;
import android.os.Process;
+import android.os.RemoteException;
import android.os.UserHandle;
import android.provider.CloudMediaProvider;
import android.provider.CloudMediaProviderContract;
import android.util.Log;
import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
import com.android.providers.media.ConfigStore;
import com.android.providers.media.photopicker.data.CloudProviderInfo;
@@ -140,4 +148,31 @@ public class CloudProviderUtils {
return context.getPackageManager()
.queryIntentContentProvidersAsUser(intent, 0, userHandle);
}
+
+ /**
+ * Request content provider to change cloud provider.
+ */
+ public static boolean persistSelectedProvider(
+ @NonNull ContentProviderClient client,
+ @Nullable String newCloudProvider) throws RemoteException {
+ final Bundle input = new Bundle();
+ input.putString(EXTRA_CLOUD_PROVIDER, newCloudProvider);
+ client.call(SET_CLOUD_PROVIDER_CALL, /* arg */ null, /* extras */ input);
+ return true;
+ }
+
+ /**
+ * Fetch selected cloud provider from content provider.
+ * @param defaultAuthority is the default returned in case query result is null.
+ * @return fetched cloud provider authority if it is non-null.
+ * Otherwise return defaultAuthority.
+ */
+ @Nullable
+ public static String fetchProviderAuthority(
+ @NonNull ContentProviderClient client,
+ @NonNull String defaultAuthority) throws RemoteException {
+ final Bundle result = client.call(GET_CLOUD_PROVIDER_CALL, /* arg */ null,
+ /* extras */ null);
+ return result.getString(GET_CLOUD_PROVIDER_RESULT, defaultAuthority);
+ }
}
diff --git a/tests/src/com/android/providers/media/photopicker/ui/settings/SettingsCloudMediaViewModelTest.java b/tests/src/com/android/providers/media/photopicker/ui/settings/SettingsCloudMediaViewModelTest.java
new file mode 100644
index 000000000..4cdb5d2f0
--- /dev/null
+++ b/tests/src/com/android/providers/media/photopicker/ui/settings/SettingsCloudMediaViewModelTest.java
@@ -0,0 +1,213 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.providers.media.photopicker.ui.settings;
+
+import static android.provider.CloudMediaProviderContract.MANAGE_CLOUD_MEDIA_PROVIDERS_PERMISSION;
+import static android.provider.MediaStore.GET_CLOUD_PROVIDER_CALL;
+import static android.provider.MediaStore.GET_CLOUD_PROVIDER_RESULT;
+import static android.provider.MediaStore.SET_CLOUD_PROVIDER_CALL;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import android.content.ContentProviderClient;
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.ProviderInfo;
+import android.content.pm.ResolveInfo;
+import android.content.res.Resources;
+import android.os.Bundle;
+import android.os.RemoteException;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.providers.media.ConfigStore;
+import com.android.providers.media.photopicker.data.model.UserId;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@RunWith(AndroidJUnit4.class)
+public class SettingsCloudMediaViewModelTest {
+ private static final List<String> sProviderAuthorities =
+ List.of("cloud_provider_1", "cloud_provider_2");
+ private static final List<ResolveInfo> sAvailableProviders = getAvailableProviders();
+
+ @Mock
+ private ConfigStore mConfigStore;
+ @Mock
+ private Context mContext;
+ @Mock
+ private Resources mResources;
+ @Mock
+ private PackageManager mPackageManager;
+ @Mock
+ private ContentProviderClient mContentProviderClient;
+ @NonNull
+ private SettingsCloudMediaViewModel mCloudMediaViewModel;
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+
+ mCloudMediaViewModel =
+ Mockito.spy(new SettingsCloudMediaViewModel(mContext, UserId.CURRENT_USER));
+
+ doReturn(mPackageManager).when(mContext).getPackageManager();
+ doReturn(mResources).when(mContext).getResources();
+ doReturn(mContentProviderClient).when(mCloudMediaViewModel).getContentProviderClient();
+ doAnswer(i -> {
+ ApplicationInfo applicationInfo = new ApplicationInfo();
+ applicationInfo.packageName = (String) i.getArgument(0);
+ applicationInfo.uid = 0;
+ return applicationInfo;
+ }).when(mPackageManager).getApplicationInfo(any(), anyInt());
+ }
+
+ @Test
+ public void testLoadDataWithMultipleProviders() throws RemoteException {
+ final String expectedCloudProvider = sProviderAuthorities.get(0);
+ setUpCurrentCloudProvider(expectedCloudProvider);
+ setUpAvailableCloudProviders(sAvailableProviders);
+
+ mCloudMediaViewModel.loadData(mConfigStore);
+
+ // Verify cloud provider options
+ final List<CloudMediaProviderOption> providerOptions =
+ mCloudMediaViewModel.getProviderOptions();
+ assertThat(providerOptions.size()).isEqualTo(sProviderAuthorities.size() + 1);
+ for (int i = 0; i < sProviderAuthorities.size(); i++) {
+ assertThat(providerOptions.get(i).getKey()).isEqualTo(sProviderAuthorities.get(i));
+ }
+ assertThat(providerOptions.get(providerOptions.size() - 1).getKey())
+ .isEqualTo(SettingsCloudMediaViewModel.NONE_PREF_KEY);
+
+ // Verify selected cloud provider
+ final String resultCloudProvider =
+ mCloudMediaViewModel.getSelectedProviderAuthority();
+ assertThat(resultCloudProvider).isEqualTo(expectedCloudProvider);
+ }
+
+ @Test
+ public void testLoadDataWithNoProvider() throws RemoteException {
+ final String expectedCloudProvider = SettingsCloudMediaViewModel.NONE_PREF_KEY;
+ setUpCurrentCloudProvider(expectedCloudProvider);
+ setUpAvailableCloudProviders(new ArrayList<>());
+ mCloudMediaViewModel.loadData(mConfigStore);
+
+ // Verify cloud provider options
+ final List<CloudMediaProviderOption> providerOptions =
+ mCloudMediaViewModel.getProviderOptions();
+ assertThat(providerOptions.size()).isEqualTo(1);
+ assertThat(providerOptions.get(0).getKey())
+ .isEqualTo(SettingsCloudMediaViewModel.NONE_PREF_KEY);
+
+ // Verify selected cloud provider
+ final String resultCloudProvider =
+ mCloudMediaViewModel.getSelectedProviderAuthority();
+ assertThat(resultCloudProvider).isEqualTo(expectedCloudProvider);
+ }
+
+ @Test
+ public void testUpdateProvider() throws RemoteException {
+ final String expectedCloudProvider = sProviderAuthorities.get(0);
+ setUpCurrentCloudProvider(expectedCloudProvider);
+ setUpAvailableCloudProviders(sAvailableProviders);
+
+ mCloudMediaViewModel.loadData(mConfigStore);
+
+ // Verify selected cloud provider
+ final String resultCloudProvider =
+ mCloudMediaViewModel.getSelectedProviderAuthority();
+ assertThat(resultCloudProvider).isEqualTo(expectedCloudProvider);
+
+ // Update cloud provider
+ final String newCloudProvider = sProviderAuthorities.get(1);
+ final boolean success = mCloudMediaViewModel.updateSelectedProvider(newCloudProvider);
+
+ // Verify selected cloud provider
+ assertThat(success).isTrue();
+ final String resultNewCloudProvider =
+ mCloudMediaViewModel.getSelectedProviderAuthority();
+ assertThat(resultNewCloudProvider).isEqualTo(newCloudProvider);
+ verify(mContentProviderClient, times(1))
+ .call(eq(SET_CLOUD_PROVIDER_CALL), any(), any());
+ }
+
+ private void setUpAvailableCloudProviders(@NonNull List<ResolveInfo> availableProviders) {
+ doReturn(availableProviders).when(mPackageManager)
+ .queryIntentContentProvidersAsUser(any(), eq(0), any());
+ }
+
+ private void setUpCurrentCloudProvider(@Nullable String providerAuthority)
+ throws RemoteException {
+ final Bundle result = new Bundle();
+ result.putString(GET_CLOUD_PROVIDER_RESULT, providerAuthority);
+ doReturn(result).when(mContentProviderClient)
+ .call(eq(GET_CLOUD_PROVIDER_CALL), any(), any());
+ }
+
+ @NonNull
+ private static List<ResolveInfo> getAvailableProviders() {
+ final List<ResolveInfo> availableProviders = new ArrayList<>();
+ for (String authority : sProviderAuthorities) {
+ availableProviders.add(createResolveInfo(authority));
+ }
+ return availableProviders;
+ }
+
+ @NonNull
+ private static ResolveInfo createResolveInfo(@NonNull String authority) {
+ final ResolveInfo resolveInfo = new ResolveInfo();
+ resolveInfo.providerInfo = createProviderInfo(authority);
+ return resolveInfo;
+ }
+
+ @NonNull
+ private static ProviderInfo createProviderInfo(@NonNull String authority) {
+ final ProviderInfo providerInfo = new ProviderInfo();
+ providerInfo.authority = authority;
+ providerInfo.readPermission = MANAGE_CLOUD_MEDIA_PROVIDERS_PERMISSION;
+ providerInfo.applicationInfo = createApplicationInfo(authority);
+ return providerInfo;
+ }
+
+ @NonNull
+ private static ApplicationInfo createApplicationInfo(@NonNull String authority) {
+ final ApplicationInfo applicationInfo = new ApplicationInfo();
+ applicationInfo.packageName = authority;
+ applicationInfo.uid = 0;
+ return applicationInfo;
+ }
+}