1 /* 2 * Copyright (C) 2021 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.data.model; 18 19 import static android.provider.CloudMediaProviderContract.MediaColumns; 20 import static android.provider.MediaStore.Files.FileColumns._SPECIAL_FORMAT_ANIMATED_WEBP; 21 import static android.provider.MediaStore.Files.FileColumns._SPECIAL_FORMAT_GIF; 22 import static android.provider.MediaStore.Files.FileColumns._SPECIAL_FORMAT_MOTION_PHOTO; 23 import static android.provider.MediaStore.Files.FileColumns._SPECIAL_FORMAT_NONE; 24 25 import static com.google.common.truth.Truth.assertThat; 26 27 import android.content.Context; 28 import android.database.Cursor; 29 import android.database.MatrixCursor; 30 import android.net.Uri; 31 import android.os.UserHandle; 32 import android.provider.MediaStore; 33 34 import androidx.test.InstrumentationRegistry; 35 import androidx.test.runner.AndroidJUnit4; 36 37 import com.android.providers.media.photopicker.PickerSyncController; 38 39 import org.junit.Test; 40 import org.junit.runner.RunWith; 41 42 import java.time.LocalDate; 43 import java.time.ZoneId; 44 45 @RunWith(AndroidJUnit4.class) 46 public class ItemTest { 47 48 @Test testConstructor()49 public void testConstructor() { 50 final String id = "1"; 51 final long dateTaken = 12345678L; 52 final long generationModified = 1L; 53 final String mimeType = "image/png"; 54 final long duration = 1000; 55 final Cursor cursor = generateCursorForItem(id, mimeType, dateTaken, generationModified, 56 duration, _SPECIAL_FORMAT_NONE); 57 cursor.moveToFirst(); 58 59 final Item item = new Item(cursor, UserId.CURRENT_USER); 60 61 assertThat(item.getId()).isEqualTo(id); 62 assertThat(item.getDateTaken()).isEqualTo(dateTaken); 63 assertThat(item.getGenerationModified()).isEqualTo(generationModified); 64 assertThat(item.getMimeType()).isEqualTo(mimeType); 65 assertThat(item.getDuration()).isEqualTo(duration); 66 assertThat(item.getContentUri()).isEqualTo( 67 Uri.parse("content://com.android.providers.media.photopicker/media/1")); 68 69 assertThat(item.isDate()).isFalse(); 70 assertThat(item.isImage()).isTrue(); 71 assertThat(item.isVideo()).isFalse(); 72 assertThat(item.isGifOrAnimatedWebp()).isFalse(); 73 assertThat(item.isMotionPhoto()).isFalse(); 74 } 75 76 @Test testConstructor_differentUser()77 public void testConstructor_differentUser() { 78 final String id = "1"; 79 final long dateTaken = 12345678L; 80 final long generationModified = 1L; 81 final String mimeType = "image/png"; 82 final long duration = 1000; 83 final Cursor cursor = generateCursorForItem(id, mimeType, dateTaken, generationModified, 84 duration, _SPECIAL_FORMAT_NONE); 85 cursor.moveToFirst(); 86 final UserId userId = UserId.of(UserHandle.of(10)); 87 88 final Item item = new Item(cursor, userId); 89 90 assertThat(item.getId()).isEqualTo(id); 91 assertThat(item.getDateTaken()).isEqualTo(dateTaken); 92 assertThat(item.getGenerationModified()).isEqualTo(generationModified); 93 assertThat(item.getMimeType()).isEqualTo(mimeType); 94 assertThat(item.getDuration()).isEqualTo(duration); 95 assertThat(item.getContentUri()).isEqualTo( 96 Uri.parse("content://10@com.android.providers.media.photopicker/media/1")); 97 98 assertThat(item.isImage()).isTrue(); 99 100 assertThat(item.isDate()).isFalse(); 101 assertThat(item.isVideo()).isFalse(); 102 assertThat(item.isGifOrAnimatedWebp()).isFalse(); 103 assertThat(item.isMotionPhoto()).isFalse(); 104 } 105 106 @Test testIsImage()107 public void testIsImage() { 108 final String id = "1"; 109 final long dateTaken = 12345678L; 110 final long generationModified = 1L; 111 final String mimeType = "image/png"; 112 final long duration = 1000; 113 final Item item = generateItem(id, mimeType, dateTaken, generationModified, duration); 114 115 assertThat(item.isImage()).isTrue(); 116 117 assertThat(item.isDate()).isFalse(); 118 assertThat(item.isVideo()).isFalse(); 119 assertThat(item.isGifOrAnimatedWebp()).isFalse(); 120 assertThat(item.isMotionPhoto()).isFalse(); 121 } 122 123 @Test testIsVideo()124 public void testIsVideo() { 125 final String id = "1"; 126 final long dateTaken = 12345678L; 127 final long generationModified = 1L; 128 final String mimeType = "video/mpeg"; 129 final long duration = 1000; 130 final Item item = generateItem(id, mimeType, dateTaken, generationModified, duration); 131 132 assertThat(item.isVideo()).isTrue(); 133 134 assertThat(item.isDate()).isFalse(); 135 assertThat(item.isImage()).isFalse(); 136 assertThat(item.isGifOrAnimatedWebp()).isFalse(); 137 assertThat(item.isMotionPhoto()).isFalse(); 138 } 139 140 @Test testIsMotionPhoto()141 public void testIsMotionPhoto() { 142 final String id = "1"; 143 final long dateTaken = 12345678L; 144 final long generationModified = 1L; 145 final String mimeType = "image/jpeg"; 146 final long duration = 1000; 147 final Item item = generateSpecialFormatItem(id, mimeType, dateTaken, generationModified, 148 duration, _SPECIAL_FORMAT_MOTION_PHOTO); 149 150 assertThat(item.isMotionPhoto()).isTrue(); 151 assertThat(item.isImage()).isTrue(); 152 153 assertThat(item.isGifOrAnimatedWebp()).isFalse(); 154 assertThat(item.isDate()).isFalse(); 155 assertThat(item.isVideo()).isFalse(); 156 } 157 158 @Test testIsGifOrAnimatedWebp()159 public void testIsGifOrAnimatedWebp() { 160 final String id = "1"; 161 final long dateTaken = 12345678L; 162 final long generationModified = 1L; 163 final String mimeType = "image/jpeg"; 164 final long duration = 1000; 165 final Item gifItem = generateSpecialFormatItem(id, mimeType, dateTaken, generationModified, 166 duration, _SPECIAL_FORMAT_GIF); 167 168 assertThat(gifItem.isGifOrAnimatedWebp()).isTrue(); 169 assertThat(gifItem.isGif()).isTrue(); 170 assertThat(gifItem.isImage()).isTrue(); 171 172 assertThat(gifItem.isAnimatedWebp()).isFalse(); 173 assertThat(gifItem.isDate()).isFalse(); 174 assertThat(gifItem.isVideo()).isFalse(); 175 176 final Item animatedWebpItem = generateSpecialFormatItem(id, mimeType, dateTaken, 177 generationModified, duration, _SPECIAL_FORMAT_ANIMATED_WEBP); 178 179 assertThat(animatedWebpItem.isGifOrAnimatedWebp()).isTrue(); 180 assertThat(animatedWebpItem.isAnimatedWebp()).isTrue(); 181 assertThat(animatedWebpItem.isImage()).isTrue(); 182 183 assertThat(animatedWebpItem.isGif()).isFalse(); 184 assertThat(animatedWebpItem.isDate()).isFalse(); 185 assertThat(animatedWebpItem.isVideo()).isFalse(); 186 } 187 188 @Test testIsGifDoesNotUseMimeType()189 public void testIsGifDoesNotUseMimeType() { 190 final String id = "1"; 191 final long dateTaken = 12345678L; 192 final long generationModified = 1L; 193 final String mimeType = "image/gif"; 194 final long duration = 1000; 195 final Item item = generateSpecialFormatItem(id, mimeType, dateTaken, generationModified, 196 duration, _SPECIAL_FORMAT_NONE); 197 198 assertThat(item.isImage()).isTrue(); 199 200 assertThat(item.isGifOrAnimatedWebp()).isFalse(); 201 assertThat(item.isDate()).isFalse(); 202 assertThat(item.isVideo()).isFalse(); 203 assertThat(item.isMotionPhoto()).isFalse(); 204 } 205 206 @Test testCreateDateItem()207 public void testCreateDateItem() { 208 final long dateTaken = 12345678L; 209 210 final Item item = Item.createDateItem(dateTaken); 211 212 assertThat(item.getDateTaken()).isEqualTo(dateTaken); 213 assertThat(item.isDate()).isTrue(); 214 } 215 216 @Test testCompareTo_differentDateTaken()217 public void testCompareTo_differentDateTaken() { 218 final String id1 = "1"; 219 final long dateTaken1 = 1000000L; 220 final long generationModified1 = 1L; 221 final Item item1 = generateJpegItem(id1, dateTaken1, generationModified1); 222 223 final String id2 = "2"; 224 final long dateTaken2 = 20000000L; 225 final long generationModified2 = 2L; 226 final Item item2 = generateJpegItem(id2, dateTaken2, generationModified2); 227 228 assertThat(item1.compareTo(item2)).isEqualTo(-1); 229 assertThat(item2.compareTo(item1)).isEqualTo(1); 230 } 231 232 @Test testCompareTo_sameDateTaken()233 public void testCompareTo_sameDateTaken() { 234 final long dateTaken = 12345678L; 235 final long generationModified = 1L; 236 237 final String id1 = "1"; 238 final Item item1 = generateJpegItem(id1, dateTaken, generationModified); 239 240 final String id2 = "2"; 241 final Item item2 = generateJpegItem(id2, dateTaken, generationModified); 242 243 assertThat(item1.compareTo(item2)).isEqualTo(-1); 244 assertThat(item2.compareTo(item1)).isEqualTo(1); 245 246 // Compare the same object 247 assertThat(item2.compareTo(item2)).isEqualTo(0); 248 249 // Compare two items with same dateTaken and same id. This will never happen in real world 250 // use-case because ids are always unique. 251 final Item item2SameValues = generateJpegItem(id2, dateTaken, generationModified); 252 assertThat(item2SameValues.compareTo(item2)).isEqualTo(0); 253 } 254 255 @Test testGetContentDescription()256 public void testGetContentDescription() { 257 final String id = "1"; 258 final long dateTaken = LocalDate.of(2020 /* year */, 7 /* month */, 7 /* dayOfMonth */) 259 .atStartOfDay(ZoneId.systemDefault()).toInstant().toEpochMilli(); 260 final long generationModified = 1L; 261 final long duration = 1000; 262 final Context context = InstrumentationRegistry.getTargetContext(); 263 264 Item item = generateItem(id, "image/jpeg", dateTaken, generationModified, duration); 265 assertThat(item.getContentDescription(context)) 266 .isEqualTo("Photo taken on Jul 7, 2020, 12:00:00 AM"); 267 268 item = generateItem(id, "video/mp4", dateTaken, generationModified, duration); 269 assertThat(item.getContentDescription(context)).isEqualTo( 270 "Video taken on Jul 7, 2020, 12:00:00 AM with duration " + item.getDurationText()); 271 272 item = generateSpecialFormatItem(id, "image/gif", dateTaken, generationModified, duration, 273 _SPECIAL_FORMAT_GIF); 274 assertThat(item.getContentDescription(context)) 275 .isEqualTo("GIF taken on Jul 7, 2020, 12:00:00 AM"); 276 277 item = generateSpecialFormatItem(id, "image/webp", dateTaken, generationModified, duration, 278 _SPECIAL_FORMAT_ANIMATED_WEBP); 279 assertThat(item.getContentDescription(context)) 280 .isEqualTo("GIF taken on Jul 7, 2020, 12:00:00 AM"); 281 282 item = generateSpecialFormatItem(id, "image/jpeg", dateTaken, generationModified, duration, 283 _SPECIAL_FORMAT_MOTION_PHOTO); 284 assertThat(item.getContentDescription(context)) 285 .isEqualTo("Motion Photo taken on Jul 7, 2020, 12:00:00 AM"); 286 } 287 288 @Test testGetDurationText()289 public void testGetDurationText() { 290 final String id = "1"; 291 final long dateTaken = LocalDate.of(2020 /* year */, 7 /* month */, 7 /* dayOfMonth */) 292 .atStartOfDay(ZoneId.systemDefault()).toInstant().toEpochMilli(); 293 final long generationModified = 1L; 294 295 // no duration 296 Item item = generateItem(id, "video", dateTaken, generationModified, -1); 297 assertThat(item.getDurationText()).isEqualTo(""); 298 299 // duration = 1000 ms 300 item = generateItem(id, "video", dateTaken, generationModified, 1000); 301 assertThat(item.getDurationText()).isEqualTo("00:01"); 302 303 // duration = 10000 ms 304 item = generateItem(id, "video", dateTaken, generationModified, 10000); 305 assertThat(item.getDurationText()).isEqualTo("00:10"); 306 307 // duration = 60000 ms 308 item = generateItem(id, "video", dateTaken, generationModified, 60000); 309 assertThat(item.getDurationText()).isEqualTo("01:00"); 310 311 // duration = 600000 ms 312 item = generateItem(id, "video", dateTaken, generationModified, 600000); 313 assertThat(item.getDurationText()).isEqualTo("10:00"); 314 } 315 generateCursorForItem(String id, String mimeType, long dateTaken, long generationModified, long duration, int specialFormat)316 private static Cursor generateCursorForItem(String id, String mimeType, long dateTaken, 317 long generationModified, long duration, int specialFormat) { 318 final MatrixCursor cursor = new MatrixCursor(MediaColumns.ALL_PROJECTION); 319 cursor.addRow(new Object[] { 320 id, 321 dateTaken, 322 generationModified, 323 mimeType, 324 specialFormat, 325 "1", // size_bytes 326 null, // media_store_uri 327 duration, 328 "0", // is_favorite 329 "/storage/emulated/0/foo", // data 330 PickerSyncController.LOCAL_PICKER_PROVIDER_AUTHORITY}); 331 return cursor; 332 } 333 generateJpegItem(String id, long dateTaken, long generationModified)334 private static Item generateJpegItem(String id, long dateTaken, long generationModified) { 335 final String mimeType = "image/jpeg"; 336 final long duration = 1000; 337 return generateItem(id, mimeType, dateTaken, generationModified, duration); 338 } 339 340 /** 341 * Generate the {@link Item} 342 * @param id the id 343 * @param mimeType the mime type 344 * @param dateTaken the time of date taken 345 * @param generationModified the generation number associated with the media 346 * @param duration the duration 347 * @return the Item 348 */ generateItem(String id, String mimeType, long dateTaken, long generationModified, long duration)349 public static Item generateItem(String id, String mimeType, long dateTaken, 350 long generationModified, long duration) { 351 return new Item(id, mimeType, dateTaken, generationModified, duration, 352 MediaStore.Files.getContentUri(MediaStore.VOLUME_EXTERNAL, Long.parseLong(id)), 353 _SPECIAL_FORMAT_NONE); 354 } 355 356 /** 357 * Generate the {@link Item} 358 * @param id the id 359 * @param mimeType the mime type 360 * @param dateTaken the time of date taken 361 * @param generationModified the generation number associated with the media 362 * @param duration the duration 363 * @param specialFormat the special format. See 364 * {@link MediaStore.Files.FileColumns#_SPECIAL_FORMAT} 365 * @return the Item 366 */ generateSpecialFormatItem(String id, String mimeType, long dateTaken, long generationModified, long duration, int specialFormat)367 public static Item generateSpecialFormatItem(String id, String mimeType, long dateTaken, 368 long generationModified, long duration, int specialFormat) { 369 return new Item(id, mimeType, dateTaken, generationModified, duration, 370 MediaStore.Files.getContentUri(MediaStore.VOLUME_EXTERNAL, Long.parseLong(id)), 371 specialFormat); 372 } 373 } 374