blob: 7d563399ee1c6d44ed726dff3d3ae457954c9948 [file] [log] [blame]
/*
* Copyright (C) 2020 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.systemui.screenshot;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static java.nio.charset.StandardCharsets.US_ASCII;
import android.content.ContentResolver;
import android.content.ContentValues;
import android.graphics.Bitmap;
import android.graphics.Bitmap.CompressFormat;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.os.Build;
import android.os.ParcelFileDescriptor;
import android.provider.MediaStore;
import android.testing.AndroidTestingRunner;
import androidx.exifinterface.media.ExifInterface;
import androidx.test.filters.MediumTest;
import com.android.systemui.SysuiTestCase;
import com.google.common.util.concurrent.ListenableFuture;
import org.junit.Test;
import org.junit.runner.RunWith;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.util.UUID;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executor;
@RunWith(AndroidTestingRunner.class)
@MediumTest // file I/O
public class ImageExporterTest extends SysuiTestCase {
/** Executes directly in the caller's thread */
private static final Executor DIRECT_EXECUTOR = Runnable::run;
private static final byte[] EXIF_FILE_TAG = "Exif\u0000\u0000".getBytes(US_ASCII);
private static final ZonedDateTime CAPTURE_TIME =
ZonedDateTime.of(LocalDateTime.of(2020, 12, 15, 13, 15), ZoneId.of("EST"));
@Test
public void testImageFilename() {
assertEquals("image file name", "Screenshot_20201215-131500.png",
ImageExporter.createFilename(CAPTURE_TIME, CompressFormat.PNG));
}
@Test
public void testUpdateExifAttributes_timeZoneUTC() throws IOException {
ExifInterface exifInterface = new ExifInterface(new ByteArrayInputStream(EXIF_FILE_TAG),
ExifInterface.STREAM_TYPE_EXIF_DATA_ONLY);
ImageExporter.updateExifAttributes(exifInterface,
UUID.fromString("3c11da99-9284-4863-b1d5-6f3684976814"), 100, 100,
ZonedDateTime.of(LocalDateTime.of(2020, 12, 15, 18, 15), ZoneId.of("UTC")));
assertEquals("Exif " + ExifInterface.TAG_IMAGE_UNIQUE_ID,
"3c11da99-9284-4863-b1d5-6f3684976814",
exifInterface.getAttribute(ExifInterface.TAG_IMAGE_UNIQUE_ID));
assertEquals("Exif " + ExifInterface.TAG_OFFSET_TIME_ORIGINAL, "+00:00",
exifInterface.getAttribute(ExifInterface.TAG_OFFSET_TIME_ORIGINAL));
}
@Test
public void testImageExport() throws ExecutionException, InterruptedException, IOException {
ContentResolver contentResolver = mContext.getContentResolver();
ImageExporter exporter = new ImageExporter(contentResolver);
UUID requestId = UUID.fromString("3c11da99-9284-4863-b1d5-6f3684976814");
Bitmap original = createCheckerBitmap(10, 10, 10);
ListenableFuture<ImageExporter.Result> direct =
exporter.export(DIRECT_EXECUTOR, requestId, original, CAPTURE_TIME);
assertTrue("future should be done", direct.isDone());
assertFalse("future should not be canceled", direct.isCancelled());
ImageExporter.Result result = direct.get();
assertEquals("Result should contain the same request id", requestId, result.requestId);
assertEquals("Filename should contain the correct filename",
"Screenshot_20201215-131500.png", result.fileName);
assertNotNull("CompressFormat should be set", result.format);
assertEquals("The default CompressFormat should be PNG", CompressFormat.PNG, result.format);
assertNotNull("Uri should not be null", result.uri);
assertEquals("Timestamp should match input", CAPTURE_TIME.toInstant().toEpochMilli(),
result.timestamp);
Bitmap decoded = null;
try (InputStream in = contentResolver.openInputStream(result.uri)) {
decoded = BitmapFactory.decodeStream(in);
assertNotNull("decoded image should not be null", decoded);
assertTrue("original and decoded image should be identical", original.sameAs(decoded));
try (ParcelFileDescriptor pfd = contentResolver.openFile(result.uri, "r", null)) {
assertNotNull(pfd);
ExifInterface exifInterface = new ExifInterface(pfd.getFileDescriptor());
assertEquals("Exif " + ExifInterface.TAG_IMAGE_UNIQUE_ID,
"3c11da99-9284-4863-b1d5-6f3684976814",
exifInterface.getAttribute(ExifInterface.TAG_IMAGE_UNIQUE_ID));
assertEquals("Exif " + ExifInterface.TAG_SOFTWARE, "Android " + Build.DISPLAY,
exifInterface.getAttribute(ExifInterface.TAG_SOFTWARE));
assertEquals("Exif " + ExifInterface.TAG_IMAGE_WIDTH, 100,
exifInterface.getAttributeInt(ExifInterface.TAG_IMAGE_WIDTH, 0));
assertEquals("Exif " + ExifInterface.TAG_IMAGE_LENGTH, 100,
exifInterface.getAttributeInt(ExifInterface.TAG_IMAGE_LENGTH, 0));
assertEquals("Exif " + ExifInterface.TAG_DATETIME_ORIGINAL, "2020:12:15 13:15:00",
exifInterface.getAttribute(ExifInterface.TAG_DATETIME_ORIGINAL));
assertEquals("Exif " + ExifInterface.TAG_SUBSEC_TIME_ORIGINAL, "000",
exifInterface.getAttribute(ExifInterface.TAG_SUBSEC_TIME_ORIGINAL));
assertEquals("Exif " + ExifInterface.TAG_OFFSET_TIME_ORIGINAL, "-05:00",
exifInterface.getAttribute(ExifInterface.TAG_OFFSET_TIME_ORIGINAL));
}
} finally {
if (decoded != null) {
decoded.recycle();
}
contentResolver.delete(result.uri, null);
}
}
@Test
public void testMediaStoreMetadata() {
String name = ImageExporter.createFilename(CAPTURE_TIME, CompressFormat.PNG);
ContentValues values = ImageExporter.createMetadata(CAPTURE_TIME, CompressFormat.PNG, name);
assertEquals("Pictures/Screenshots",
values.getAsString(MediaStore.MediaColumns.RELATIVE_PATH));
assertEquals("Screenshot_20201215-131500.png",
values.getAsString(MediaStore.MediaColumns.DISPLAY_NAME));
assertEquals("image/png", values.getAsString(MediaStore.MediaColumns.MIME_TYPE));
assertEquals(Long.valueOf(1608056100L),
values.getAsLong(MediaStore.MediaColumns.DATE_ADDED));
assertEquals(Long.valueOf(1608056100L),
values.getAsLong(MediaStore.MediaColumns.DATE_MODIFIED));
assertEquals(Integer.valueOf(1), values.getAsInteger(MediaStore.MediaColumns.IS_PENDING));
assertEquals(Long.valueOf(1608056100L + 86400L), // +1 day
values.getAsLong(MediaStore.MediaColumns.DATE_EXPIRES));
}
@SuppressWarnings("SameParameterValue")
private Bitmap createCheckerBitmap(int tileSize, int w, int h) {
Bitmap bitmap = Bitmap.createBitmap(w * tileSize, h * tileSize, Bitmap.Config.ARGB_8888);
Canvas c = new Canvas(bitmap);
Paint paint = new Paint();
paint.setStyle(Paint.Style.FILL);
for (int i = 0; i < h; i++) {
int top = i * tileSize;
for (int j = 0; j < w; j++) {
int left = j * tileSize;
paint.setColor(paint.getColor() == Color.WHITE ? Color.BLACK : Color.WHITE);
c.drawRect(left, top, left + tileSize, top + tileSize, paint);
}
}
return bitmap;
}
}