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 android.photopicker.cts; 18 19 import static android.Manifest.permission.READ_DEVICE_CONFIG; 20 import static android.Manifest.permission.WRITE_ALLOWLISTED_DEVICE_CONFIG; 21 import static android.Manifest.permission.WRITE_DEVICE_CONFIG; 22 import static android.photopicker.cts.PickerProviderMediaGenerator.setCloudProvider; 23 import static android.photopicker.cts.PickerProviderMediaGenerator.syncCloudProvider; 24 import static android.photopicker.cts.util.PhotoPickerUiUtils.findAddButton; 25 import static android.photopicker.cts.util.PhotoPickerUiUtils.findItemList; 26 27 import static com.google.common.truth.Truth.assertThat; 28 import static com.google.common.truth.Truth.assertWithMessage; 29 30 import android.app.UiAutomation; 31 import android.content.ClipData; 32 import android.content.Context; 33 import android.provider.DeviceConfig; 34 import android.provider.MediaStore; 35 import android.util.Pair; 36 37 import androidx.annotation.NonNull; 38 import androidx.annotation.Nullable; 39 import androidx.test.InstrumentationRegistry; 40 import androidx.test.uiautomator.UiDevice; 41 import androidx.test.uiautomator.UiObject; 42 43 import com.android.modules.utils.build.SdkLevel; 44 45 import java.util.ArrayList; 46 import java.util.Collections; 47 import java.util.List; 48 49 public class PhotoPickerCloudUtils { 50 private static final String NAMESPACE_STORAGE_NATIVE_BOOT = "storage_native_boot"; 51 private static final String KEY_ALLOWED_CLOUD_PROVIDERS = "allowed_cloud_providers"; 52 private static final String KEY_CLOUD_MEDIA_FEATURE_ENABLED = "cloud_media_feature_enabled"; 53 extractMediaIds(ClipData clipData, int minCount)54 public static List<String> extractMediaIds(ClipData clipData, int minCount) { 55 final int count = clipData.getItemCount(); 56 assertThat(count).isAtLeast(minCount); 57 58 final List<String> mediaIds = new ArrayList<>(); 59 for (int i = 0; i < count; i++) { 60 mediaIds.add(clipData.getItemAt(i).getUri().getLastPathSegment()); 61 } 62 63 return mediaIds; 64 } 65 selectAndAddPickerMedia(UiDevice uiDevice, int maxCount)66 public static void selectAndAddPickerMedia(UiDevice uiDevice, int maxCount) 67 throws Exception { 68 final List<UiObject> itemList = findItemList(maxCount); 69 for (int i = 0; i < itemList.size(); i++) { 70 final UiObject item = itemList.get(i); 71 item.click(); 72 uiDevice.waitForIdle(); 73 } 74 75 final UiObject addButton = findAddButton(); 76 addButton.click(); 77 uiDevice.waitForIdle(); 78 } 79 fetchPickerMedia(GetResultActivity activity, UiDevice uiDevice, int maxCount)80 public static ClipData fetchPickerMedia(GetResultActivity activity, UiDevice uiDevice, 81 int maxCount) throws Exception { 82 selectAndAddPickerMedia(uiDevice, maxCount); 83 84 return activity.getResult().data.getClipData(); 85 } 86 initCloudProviderWithImage( Context context, PickerProviderMediaGenerator.MediaGenerator mediaGenerator, String authority, Pair<String, String>... mediaPairs)87 public static void initCloudProviderWithImage( 88 Context context, PickerProviderMediaGenerator.MediaGenerator mediaGenerator, 89 String authority, Pair<String, String>... mediaPairs) throws Exception { 90 setCloudProvider(context, authority); 91 assertThat(MediaStore.isCurrentCloudMediaProviderAuthority(context.getContentResolver(), 92 authority)).isTrue(); 93 94 for (Pair<String, String> pair : mediaPairs) { 95 addImage(mediaGenerator, pair.first, pair.second); 96 } 97 98 syncCloudProvider(context); 99 } 100 addImage(PickerProviderMediaGenerator.MediaGenerator generator, String localId, String cloudId)101 public static void addImage(PickerProviderMediaGenerator.MediaGenerator generator, 102 String localId, String cloudId) 103 throws Exception { 104 final long imageSizeBytes = 107684; 105 generator.addMedia(localId, cloudId, /* albumId */ null, "image/jpeg", 106 /* mimeTypeExtension */ 0, imageSizeBytes, /* isFavorite */ false, 107 R.raw.lg_g4_iso_800_jpg); 108 } 109 containsExcept(List<String> mediaIds, String contained, String notContained)110 public static void containsExcept(List<String> mediaIds, String contained, 111 String notContained) { 112 assertThat(mediaIds).contains(contained); 113 assertThat(mediaIds).containsNoneIn(Collections.singletonList(notContained)); 114 } 115 isCloudMediaEnabled()116 public static boolean isCloudMediaEnabled() { 117 return Boolean.parseBoolean(readDeviceConfigProp(KEY_CLOUD_MEDIA_FEATURE_ENABLED)); 118 } 119 120 @Nullable getAllowedProvidersDeviceConfig()121 static String getAllowedProvidersDeviceConfig() { 122 return readDeviceConfigProp(KEY_ALLOWED_CLOUD_PROVIDERS); 123 } 124 enableCloudMediaAndSetAllowedCloudProviders(@onNull String allowedPackagesJoined)125 static void enableCloudMediaAndSetAllowedCloudProviders(@NonNull String allowedPackagesJoined) { 126 writeDeviceConfigProp(KEY_ALLOWED_CLOUD_PROVIDERS, allowedPackagesJoined); 127 assertWithMessage("Failed to update the allowed cloud providers device config") 128 .that(getAllowedProvidersDeviceConfig()) 129 .isEqualTo(allowedPackagesJoined); 130 131 writeDeviceConfigProp(KEY_CLOUD_MEDIA_FEATURE_ENABLED, true); 132 } 133 134 disableCloudMediaAndClearAllowedCloudProviders()135 static void disableCloudMediaAndClearAllowedCloudProviders() { 136 writeDeviceConfigProp(KEY_CLOUD_MEDIA_FEATURE_ENABLED, false); 137 138 deleteDeviceConfigProp(KEY_ALLOWED_CLOUD_PROVIDERS); 139 assertWithMessage("Failed to delete the allowed cloud providers device config") 140 .that(getAllowedProvidersDeviceConfig()) 141 .isNull(); 142 } 143 144 @NonNull getUiAutomation()145 private static UiAutomation getUiAutomation() { 146 return InstrumentationRegistry.getInstrumentation().getUiAutomation(); 147 } 148 149 @Nullable readDeviceConfigProp(@onNull String name)150 private static String readDeviceConfigProp(@NonNull String name) { 151 getUiAutomation().adoptShellPermissionIdentity(READ_DEVICE_CONFIG); 152 try { 153 return DeviceConfig.getProperty(NAMESPACE_STORAGE_NATIVE_BOOT, name); 154 } finally { 155 getUiAutomation().dropShellPermissionIdentity(); 156 } 157 } 158 writeDeviceConfigProp(@onNull String name, boolean value)159 private static void writeDeviceConfigProp(@NonNull String name, boolean value) { 160 writeDeviceConfigProp(name, Boolean.toString(value)); 161 } 162 writeDeviceConfigProp(@onNull String name, @NonNull String value)163 private static void writeDeviceConfigProp(@NonNull String name, @NonNull String value) { 164 if (SdkLevel.isAtLeastU()) { 165 getUiAutomation().adoptShellPermissionIdentity(WRITE_ALLOWLISTED_DEVICE_CONFIG); 166 } else { 167 getUiAutomation().adoptShellPermissionIdentity(WRITE_DEVICE_CONFIG); 168 } 169 try { 170 DeviceConfig.setProperty(NAMESPACE_STORAGE_NATIVE_BOOT, name, value, 171 /* makeDefault*/ false); 172 } finally { 173 getUiAutomation().dropShellPermissionIdentity(); 174 } 175 } 176 deleteDeviceConfigProp(@onNull String name)177 private static void deleteDeviceConfigProp(@NonNull String name) { 178 DeviceConfig.deleteProperty(NAMESPACE_STORAGE_NATIVE_BOOT, name); 179 } 180 } 181