Commit e5f0b1f7 authored by Gayane Petrosyan's avatar Gayane Petrosyan Committed by Commit Bot

[QRCode android] Add qrcode to completed downloads.

Add downloaded file to completed dowwnloads for pre-Q versions and
add to MediaStore.Downloads for Q+ versions.

Bug: 993920
Change-Id: I9c4f88c218276ca59a2e1ebf14603af2e080eca2
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2106672
Commit-Queue: Gayane Petrosyan <gayane@chromium.org>
Reviewed-by: default avatarTanya Gupta <tgupta@chromium.org>
Reviewed-by: default avatarMin Qin <qinmin@chromium.org>
Reviewed-by: default avatarKristi Park <kristipark@chromium.org>
Cr-Commit-Position: refs/heads/master@{#753435}
parent 31988f32
...@@ -4,16 +4,21 @@ ...@@ -4,16 +4,21 @@
package org.chromium.chrome.browser.share; package org.chromium.chrome.browser.share;
import android.annotation.TargetApi;
import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.Context; import android.content.Context;
import android.graphics.Bitmap; import android.graphics.Bitmap;
import android.net.Uri; import android.net.Uri;
import android.os.Environment; import android.os.Environment;
import android.provider.MediaStore;
import android.text.TextUtils; import android.text.TextUtils;
import androidx.annotation.VisibleForTesting; import androidx.annotation.VisibleForTesting;
import org.chromium.base.ApplicationState; import org.chromium.base.ApplicationState;
import org.chromium.base.ApplicationStatus; import org.chromium.base.ApplicationStatus;
import org.chromium.base.BuildInfo;
import org.chromium.base.Callback; import org.chromium.base.Callback;
import org.chromium.base.ContentUriUtils; import org.chromium.base.ContentUriUtils;
import org.chromium.base.ContextUtils; import org.chromium.base.ContextUtils;
...@@ -21,14 +26,18 @@ import org.chromium.base.FileUtils; ...@@ -21,14 +26,18 @@ import org.chromium.base.FileUtils;
import org.chromium.base.Log; import org.chromium.base.Log;
import org.chromium.base.StreamUtil; import org.chromium.base.StreamUtil;
import org.chromium.base.task.AsyncTask; import org.chromium.base.task.AsyncTask;
import org.chromium.chrome.browser.download.DownloadManagerBridge;
import org.chromium.content_public.browser.RenderWidgetHostView; import org.chromium.content_public.browser.RenderWidgetHostView;
import org.chromium.content_public.browser.WebContents; import org.chromium.content_public.browser.WebContents;
import org.chromium.ui.UiUtils; import org.chromium.ui.UiUtils;
import org.chromium.ui.base.Clipboard; import org.chromium.ui.base.Clipboard;
import java.io.File; import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream; import java.io.FileOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Locale; import java.util.Locale;
/** /**
...@@ -46,6 +55,7 @@ public class ShareImageFileUtils { ...@@ -46,6 +55,7 @@ public class ShareImageFileUtils {
private static final String SHARE_IMAGES_DIRECTORY_NAME = "screenshot"; private static final String SHARE_IMAGES_DIRECTORY_NAME = "screenshot";
private static final String JPEG_EXTENSION = ".jpg"; private static final String JPEG_EXTENSION = ".jpg";
private static final String FILE_NUMBER_FORMAT = " (%d)"; private static final String FILE_NUMBER_FORMAT = " (%d)";
private static final String MIME_TYPE = "image/JPEG";
/** /**
* Check if the file related to |fileUri| is in the |folder|. * Check if the file related to |fileUri| is in the |folder|.
...@@ -122,25 +132,20 @@ public class ShareImageFileUtils { ...@@ -122,25 +132,20 @@ public class ShareImageFileUtils {
} }
OnImageSaveListener listener = new OnImageSaveListener() { OnImageSaveListener listener = new OnImageSaveListener() {
@Override @Override
public void onImageSaved(File imageFile) { public void onImageSaved(Uri uri, String displayName) {
new AsyncTask<Uri>() { callback.onResult(uri);
@Override
protected Uri doInBackground() {
return ContentUriUtils.getContentUriFromFile(imageFile);
}
@Override
protected void onPostExecute(Uri uri) {
callback.onResult(uri);
}
}.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
} }
@Override @Override
public void onImageSaveError() {} public void onImageSaveError() {}
}; };
String fileName = String.valueOf(System.currentTimeMillis()); String fileName = String.valueOf(System.currentTimeMillis());
saveImage(fileName, "", listener, (fos) -> { writeImageData(fos, jpegImageData); }, true); // Path is passed as a function because in some cases getting the path should be run on a
// background thread.
saveImage(fileName,
()
-> { return ""; },
listener, (fos) -> { writeImageData(fos, jpegImageData); }, true);
} }
/** /**
...@@ -153,15 +158,21 @@ public class ShareImageFileUtils { ...@@ -153,15 +158,21 @@ public class ShareImageFileUtils {
*/ */
public static void saveBitmapToExternalStorage( public static void saveBitmapToExternalStorage(
final Context context, String fileName, Bitmap bitmap, OnImageSaveListener listener) { final Context context, String fileName, Bitmap bitmap, OnImageSaveListener listener) {
String filePath = context.getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS).getPath(); // Passing the path as a function so that it can be called on a background thread in
saveImage(fileName, filePath, listener, (fos) -> { writeBitmap(fos, bitmap); }, false); // |saveImage|.
saveImage(fileName,
()
-> {
return context.getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS).getPath();
},
listener, (fos) -> { writeBitmap(fos, bitmap); }, false);
} }
/** /**
* Interface for notifying bitmap download result. * Interface for notifying image download result.
*/ */
public interface OnImageSaveListener { public interface OnImageSaveListener {
void onImageSaved(File imageFile); void onImageSaved(Uri uri, String displayName);
void onImageSaveError(); void onImageSaveError();
} }
...@@ -172,24 +183,31 @@ public class ShareImageFileUtils { ...@@ -172,24 +183,31 @@ public class ShareImageFileUtils {
void write(FileOutputStream fos) throws IOException; void write(FileOutputStream fos) throws IOException;
} }
/**
* Interface for providing file path. This is used for passing a function for getting the path
* to other function to be called while on a background thread. Should be used on a background
* thread.
*/
private interface FilePathProvider { String getPath(); }
/** /**
* Saves image to the given file. * Saves image to the given file.
* *
* @param fileName The File instance of a destination file. * @param fileName The File instance of a destination file.
* @param filePath The File instance of a destination file. * @param filePathProvider The FilePathProvider for obtaining destination file path.
* @param listener The OnImageSaveListener to notify the download results. * @param listener The OnImageSaveListener to notify the download results.
* @param writer The FileOutputStreamWriter that writes to given stream. * @param writer The FileOutputStreamWriter that writes to given stream.
* @param isTemporary Indicates whether image should be save to a temporary file. * @param isTemporary Indicates whether image should be save to a temporary file.
*/ */
private static void saveImage(String fileName, String filePath, OnImageSaveListener listener, private static void saveImage(String fileName, FilePathProvider filePathProvider,
FileOutputStreamWriter writer, boolean isTemporary) { OnImageSaveListener listener, FileOutputStreamWriter writer, boolean isTemporary) {
new AsyncTask<File>() { new AsyncTask<Uri>() {
@Override @Override
protected File doInBackground() { protected Uri doInBackground() {
FileOutputStream fOut = null; FileOutputStream fOut = null;
File destFile = null; File destFile = null;
try { try {
destFile = createFile(fileName, filePath, isTemporary); destFile = createFile(fileName, filePathProvider.getPath(), isTemporary);
if (destFile != null && destFile.exists()) { if (destFile != null && destFile.exists()) {
fOut = new FileOutputStream(destFile); fOut = new FileOutputStream(destFile);
writer.write(fOut); writer.write(fOut);
...@@ -203,7 +221,15 @@ public class ShareImageFileUtils { ...@@ -203,7 +221,15 @@ public class ShareImageFileUtils {
StreamUtil.closeQuietly(fOut); StreamUtil.closeQuietly(fOut);
} }
return destFile; Uri uri = FileUtils.getUriForFile(destFile);
if (!isTemporary) {
if (BuildInfo.isAtLeastQ()) {
uri = addToMediaStore(destFile);
} else {
addCompletedDownload(destFile);
}
}
return uri;
} }
@Override @Override
...@@ -212,8 +238,8 @@ public class ShareImageFileUtils { ...@@ -212,8 +238,8 @@ public class ShareImageFileUtils {
} }
@Override @Override
protected void onPostExecute(File imageFile) { protected void onPostExecute(Uri uri) {
if (imageFile == null) { if (uri == null) {
listener.onImageSaveError(); listener.onImageSaveError();
return; return;
} }
...@@ -223,7 +249,7 @@ public class ShareImageFileUtils { ...@@ -223,7 +249,7 @@ public class ShareImageFileUtils {
return; return;
} }
listener.onImageSaved(imageFile); listener.onImageSaved(uri, fileName);
} }
}.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
} }
...@@ -302,6 +328,55 @@ public class ShareImageFileUtils { ...@@ -302,6 +328,55 @@ public class ShareImageFileUtils {
fos.write(data); fos.write(data);
} }
/**
* This is a pass through to the {@link AndroidDownloadManager} function of the same name.
* @param file The File corresponding to the download.
* @return the download ID of this item as assigned by the download manager.
*/
public static long addCompletedDownload(File file) {
String title = file.getName();
String path = file.getPath();
long length = file.length();
return DownloadManagerBridge.addCompletedDownload(
title, title, MIME_TYPE, path, length, null, null, title);
}
@TargetApi(29)
public static Uri addToMediaStore(File file) {
assert BuildInfo.isAtLeastQ();
final ContentValues contentValues = new ContentValues();
contentValues.put(MediaStore.MediaColumns.DISPLAY_NAME, file.getName());
contentValues.put(MediaStore.MediaColumns.MIME_TYPE, MIME_TYPE);
contentValues.put(MediaStore.MediaColumns.RELATIVE_PATH, Environment.DIRECTORY_DOWNLOADS);
ContentResolver database = ContextUtils.getApplicationContext().getContentResolver();
Uri insertUri = database.insert(MediaStore.Downloads.EXTERNAL_CONTENT_URI, contentValues);
InputStream input = null;
OutputStream output = null;
try {
input = new FileInputStream(file);
if (insertUri != null) {
output = database.openOutputStream(insertUri);
}
if (output != null) {
byte[] buffer = new byte[4096];
int byteCount = 0;
while ((byteCount = input.read(buffer)) != -1) {
output.write(buffer, 0, byteCount);
}
}
file.delete();
} catch (IOException e) {
} finally {
StreamUtil.closeQuietly(input);
StreamUtil.closeQuietly(output);
}
return insertUri;
}
/** /**
* Captures a screenshot for the provided web contents, persists it and notifies the file * Captures a screenshot for the provided web contents, persists it and notifies the file
* provider that the file is ready to be accessed by the client. * provider that the file is ready to be accessed by the client.
......
...@@ -6,6 +6,9 @@ package org.chromium.chrome.browser.share.qrcode.share_tab; ...@@ -6,6 +6,9 @@ package org.chromium.chrome.browser.share.qrcode.share_tab;
import android.content.Context; import android.content.Context;
import android.graphics.Bitmap; import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.net.Uri;
import android.view.View; import android.view.View;
import org.chromium.base.metrics.RecordUserAction; import org.chromium.base.metrics.RecordUserAction;
...@@ -13,8 +16,6 @@ import org.chromium.chrome.R; ...@@ -13,8 +16,6 @@ import org.chromium.chrome.R;
import org.chromium.chrome.browser.share.ShareImageFileUtils; import org.chromium.chrome.browser.share.ShareImageFileUtils;
import org.chromium.ui.modelutil.PropertyModel; import org.chromium.ui.modelutil.PropertyModel;
import java.io.File;
/** /**
* QrCodeShareMediator is in charge of calculating and setting values for QrCodeShareViewProperties. * QrCodeShareMediator is in charge of calculating and setting values for QrCodeShareViewProperties.
*/ */
...@@ -35,6 +36,7 @@ class QrCodeShareMediator implements ShareImageFileUtils.OnImageSaveListener { ...@@ -35,6 +36,7 @@ class QrCodeShareMediator implements ShareImageFileUtils.OnImageSaveListener {
// TODO(gayane): Request generated QR code bitmap with a callback that sets QRCODE_BITMAP // TODO(gayane): Request generated QR code bitmap with a callback that sets QRCODE_BITMAP
// property. // property.
mPropertyModel.set(QrCodeShareViewProperties.QRCODE_BITMAP, getTestBitmap());
} }
/** Triggers download for the generated QR code bitmap if available. */ /** Triggers download for the generated QR code bitmap if available. */
...@@ -60,7 +62,7 @@ class QrCodeShareMediator implements ShareImageFileUtils.OnImageSaveListener { ...@@ -60,7 +62,7 @@ class QrCodeShareMediator implements ShareImageFileUtils.OnImageSaveListener {
// ShareImageFileUtils.OnImageSaveListener implementation. // ShareImageFileUtils.OnImageSaveListener implementation.
@Override @Override
public void onImageSaved(File imageFile) { public void onImageSaved(Uri uri, String displayName) {
// TODO(gayane): Maybe need to show confirmation message. // TODO(gayane): Maybe need to show confirmation message.
mPropertyModel.set(QrCodeShareViewProperties.DOWNLOAD_SUCCESSFUL, true); mPropertyModel.set(QrCodeShareViewProperties.DOWNLOAD_SUCCESSFUL, true);
RecordUserAction.record("SharingQRCode.DownloadQRCode.Succeeded"); RecordUserAction.record("SharingQRCode.DownloadQRCode.Succeeded");
...@@ -72,4 +74,15 @@ class QrCodeShareMediator implements ShareImageFileUtils.OnImageSaveListener { ...@@ -72,4 +74,15 @@ class QrCodeShareMediator implements ShareImageFileUtils.OnImageSaveListener {
mPropertyModel.set(QrCodeShareViewProperties.DOWNLOAD_SUCCESSFUL, false); mPropertyModel.set(QrCodeShareViewProperties.DOWNLOAD_SUCCESSFUL, false);
RecordUserAction.record("SharingQRCode.DownloadQRCode.Failed"); RecordUserAction.record("SharingQRCode.DownloadQRCode.Failed");
} }
private Bitmap getTestBitmap() {
int size = 500;
Bitmap bitmap = Bitmap.createBitmap(size, size, Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap);
Paint paint = new Paint();
paint.setColor(android.graphics.Color.GREEN);
canvas.drawRect(0F, 0F, (float) size, (float) size, paint);
return bitmap;
}
} }
\ No newline at end of file
...@@ -4,13 +4,19 @@ ...@@ -4,13 +4,19 @@
package org.chromium.chrome.browser.share; package org.chromium.chrome.browser.share;
import android.annotation.TargetApi;
import android.app.DownloadManager;
import android.content.ContentResolver;
import android.content.ContentUris;
import android.content.Context; import android.content.Context;
import android.database.Cursor;
import android.graphics.Bitmap; import android.graphics.Bitmap;
import android.graphics.Canvas; import android.graphics.Canvas;
import android.graphics.Paint; import android.graphics.Paint;
import android.net.Uri; import android.net.Uri;
import android.os.Build; import android.os.Build;
import android.os.Environment; import android.os.Environment;
import android.provider.MediaStore;
import android.support.test.filters.SmallTest; import android.support.test.filters.SmallTest;
import androidx.core.content.FileProvider; import androidx.core.content.FileProvider;
...@@ -22,6 +28,7 @@ import org.junit.Rule; ...@@ -22,6 +28,7 @@ import org.junit.Rule;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import org.chromium.base.BuildInfo;
import org.chromium.base.Callback; import org.chromium.base.Callback;
import org.chromium.base.ContentUriUtils; import org.chromium.base.ContentUriUtils;
import org.chromium.base.ContextUtils; import org.chromium.base.ContextUtils;
...@@ -50,8 +57,10 @@ public class ShareImageFileUtilsTest { ...@@ -50,8 +57,10 @@ public class ShareImageFileUtilsTest {
public ChromeActivityTestRule<ChromeActivity> mActivityTestRule = public ChromeActivityTestRule<ChromeActivity> mActivityTestRule =
new ChromeActivityTestRule<>(ChromeActivity.class); new ChromeActivityTestRule<>(ChromeActivity.class);
private static final long WAIT_TIMEOUT_SECONDS = 5L; private static final long WAIT_TIMEOUT_SECONDS = 30L;
private static final byte[] TEST_IMAGE_DATA = new byte[] {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; private static final byte[] TEST_IMAGE_DATA = new byte[] {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
private static final String TEST_IMAGE_FILE_NAME = "chrome-test-bitmap";
private static final String TEST_IMAGE_FILE_EXTENSION = ".jpg";
private class FileProviderHelper implements ContentUriUtils.FileProviderUtil { private class FileProviderHelper implements ContentUriUtils.FileProviderUtil {
private static final String API_AUTHORITY_SUFFIX = ".FileProvider"; private static final String API_AUTHORITY_SUFFIX = ".FileProvider";
...@@ -89,13 +98,13 @@ public class ShareImageFileUtilsTest { ...@@ -89,13 +98,13 @@ public class ShareImageFileUtilsTest {
public void setUp() { public void setUp() {
mActivityTestRule.startMainActivityFromLauncher(); mActivityTestRule.startMainActivityFromLauncher();
ContentUriUtils.setFileProviderUtil(new FileProviderHelper()); ContentUriUtils.setFileProviderUtil(new FileProviderHelper());
clearExternalStorageDir();
} }
@After @After
public void tearDown() throws TimeoutException { public void tearDown() throws TimeoutException {
Clipboard.getInstance().setText(""); Clipboard.getInstance().setText("");
clearSharedImages(); clearSharedImages();
deleteAllTestImages();
} }
private int fileCount(File file) { private int fileCount(File file) {
...@@ -138,20 +147,47 @@ public class ShareImageFileUtilsTest { ...@@ -138,20 +147,47 @@ public class ShareImageFileUtilsTest {
// ShareImageFileUtils::clearSharedImages uses AsyncTask.SERIAL_EXECUTOR to schedule a // ShareImageFileUtils::clearSharedImages uses AsyncTask.SERIAL_EXECUTOR to schedule a
// clearing the shared folder job, so schedule a new job and wait for the new job finished // clearing the shared folder job, so schedule a new job and wait for the new job finished
// to make sure ShareImageFileUtils::clearSharedImages's clearing folder job finished. // to make sure ShareImageFileUtils::clearSharedImages's clearing folder job finished.
waitForAsync();
}
private void waitForAsync() throws TimeoutException {
AsyncTaskRunnableHelper runnableHelper = new AsyncTaskRunnableHelper(); AsyncTaskRunnableHelper runnableHelper = new AsyncTaskRunnableHelper();
AsyncTask.SERIAL_EXECUTOR.execute(runnableHelper); AsyncTask.SERIAL_EXECUTOR.execute(runnableHelper);
runnableHelper.waitForCallback(0, 1, WAIT_TIMEOUT_SECONDS, TimeUnit.SECONDS); runnableHelper.waitForCallback(0, 1, WAIT_TIMEOUT_SECONDS, TimeUnit.SECONDS);
AsyncTask.THREAD_POOL_EXECUTOR.execute(runnableHelper);
runnableHelper.waitForCallback(0, 1, WAIT_TIMEOUT_SECONDS, TimeUnit.SECONDS);
} }
public void clearExternalStorageDir() { private void deleteAllTestImages() throws TimeoutException {
AsyncTask.SERIAL_EXECUTOR.execute(() -> { AsyncTask.SERIAL_EXECUTOR.execute(() -> {
File externalStorageDir = mActivityTestRule.getActivity().getExternalFilesDir( if (BuildInfo.isAtLeastQ()) {
Environment.DIRECTORY_DOWNLOADS); deleteMediaStoreFiles();
String[] children = externalStorageDir.list();
for (int i = 0; i < children.length; i++) {
new File(externalStorageDir, children[i]).delete();
} }
deleteExternalStorageFiles();
}); });
waitForAsync();
}
@TargetApi(29)
private void deleteMediaStoreFiles() {
ContentResolver contentResolver = ContextUtils.getApplicationContext().getContentResolver();
Cursor cursor =
contentResolver.query(MediaStore.Downloads.EXTERNAL_CONTENT_URI, null, null, null);
while (cursor.moveToNext()) {
long id = cursor.getLong(cursor.getColumnIndexOrThrow(MediaStore.Downloads._ID));
Uri uri = ContentUris.withAppendedId(MediaStore.Downloads.EXTERNAL_CONTENT_URI, id);
contentResolver.delete(uri, null, null);
}
}
public void deleteExternalStorageFiles() {
File externalStorageDir = ContextUtils.getApplicationContext().getExternalFilesDir(
Environment.DIRECTORY_DOWNLOADS);
String[] children = externalStorageDir.list();
for (int i = 0; i < children.length; i++) {
new File(externalStorageDir, children[i]).delete();
}
} }
private int fileCountInShareDirectory() throws IOException { private int fileCountInShareDirectory() throws IOException {
...@@ -202,17 +238,25 @@ public class ShareImageFileUtilsTest { ...@@ -202,17 +238,25 @@ public class ShareImageFileUtilsTest {
@Test @Test
@SmallTest @SmallTest
@DisableIf.Build(sdk_is_less_than = Build.VERSION_CODES.LOLLIPOP, message = "crbug.com/1056059") @DisableIf.Build(sdk_is_less_than = Build.VERSION_CODES.LOLLIPOP, message = "crbug.com/1056059")
public void testSaveBitmap() throws IOException { public void testSaveBitmap() throws IOException, TimeoutException {
String fileName = "chrome-test-bitmap"; String fileName = TEST_IMAGE_FILE_NAME + "_save_bitmap";
ShareImageFileUtils.OnImageSaveListener listener = ShareImageFileUtils.OnImageSaveListener listener =
new ShareImageFileUtils.OnImageSaveListener() { new ShareImageFileUtils.OnImageSaveListener() {
@Override @Override
public void onImageSaved(File imageFile) { public void onImageSaved(Uri uri, String displayName) {
Assert.assertNotNull(uri);
Assert.assertEquals(fileName, displayName);
AsyncTask.SERIAL_EXECUTOR.execute(() -> { AsyncTask.SERIAL_EXECUTOR.execute(() -> {
Assert.assertTrue(imageFile.exists()); File file = new File(uri.getPath());
Assert.assertTrue(imageFile.isFile()); Assert.assertTrue(file.exists());
Assert.assertTrue(imageFile.getPath().contains(fileName)); Assert.assertTrue(file.isFile());
}); });
// Wait for the above checks to complete.
try {
waitForAsync();
} catch (TimeoutException ex) {
}
} }
@Override @Override
...@@ -222,23 +266,88 @@ public class ShareImageFileUtilsTest { ...@@ -222,23 +266,88 @@ public class ShareImageFileUtilsTest {
}; };
ShareImageFileUtils.saveBitmapToExternalStorage( ShareImageFileUtils.saveBitmapToExternalStorage(
mActivityTestRule.getActivity(), fileName, getTestBitmap(), listener); mActivityTestRule.getActivity(), fileName, getTestBitmap(), listener);
waitForAsync();
} }
@Test @Test
@SmallTest @SmallTest
public void testGetNextAvailableFile() throws IOException { @DisableIf.Build(sdk_is_less_than = 29)
String filename = "chrome-test-bitmap"; public void testSaveBitmapAndMediaStore() throws IOException, TimeoutException {
String extension = ".jpg"; String fileName = TEST_IMAGE_FILE_NAME + "_mediastore";
ShareImageFileUtils.OnImageSaveListener listener =
new ShareImageFileUtils.OnImageSaveListener() {
@Override
public void onImageSaved(Uri uri, String displayName) {
Assert.assertNotNull(uri);
Assert.assertEquals(fileName, displayName);
AsyncTask.SERIAL_EXECUTOR.execute(() -> {
Cursor cursor =
mActivityTestRule.getActivity().getContentResolver().query(
uri, null, null, null, null);
Assert.assertNotNull(cursor);
Assert.assertTrue(cursor.moveToFirst());
Assert.assertEquals(fileName + TEST_IMAGE_FILE_EXTENSION,
cursor.getString(cursor.getColumnIndex(
MediaStore.MediaColumns.DISPLAY_NAME)));
});
// Wait for the above checks to complete.
try {
waitForAsync();
} catch (TimeoutException ex) {
}
}
@Override
public void onImageSaveError() {
Assert.fail();
}
};
ShareImageFileUtils.saveBitmapToExternalStorage(
mActivityTestRule.getActivity(), fileName, getTestBitmap(), listener);
waitForAsync();
}
@Test
@SmallTest
public void testGetNextAvailableFile() throws IOException {
String fileName = TEST_IMAGE_FILE_NAME + "_next_availble";
File externalStorageDir = mActivityTestRule.getActivity().getExternalFilesDir( File externalStorageDir = mActivityTestRule.getActivity().getExternalFilesDir(
Environment.DIRECTORY_DOWNLOADS); Environment.DIRECTORY_DOWNLOADS);
File imageFile = ShareImageFileUtils.getNextAvailableFile( File imageFile = ShareImageFileUtils.getNextAvailableFile(
externalStorageDir.getPath(), filename, extension); externalStorageDir.getPath(), fileName, TEST_IMAGE_FILE_EXTENSION);
Assert.assertTrue(imageFile.exists()); Assert.assertTrue(imageFile.exists());
File imageFile2 = ShareImageFileUtils.getNextAvailableFile( File imageFile2 = ShareImageFileUtils.getNextAvailableFile(
externalStorageDir.getPath(), filename, extension); externalStorageDir.getPath(), fileName, TEST_IMAGE_FILE_EXTENSION);
Assert.assertTrue(imageFile2.exists()); Assert.assertTrue(imageFile2.exists());
Assert.assertNotEquals(imageFile.getPath(), imageFile2.getPath()); Assert.assertNotEquals(imageFile.getPath(), imageFile2.getPath());
} }
@Test
@SmallTest
@DisableIf.Build(sdk_is_greater_than = 28)
public void testAddCompletedDownload() throws IOException {
String filename =
TEST_IMAGE_FILE_NAME + "_add_completed_download" + TEST_IMAGE_FILE_EXTENSION;
File externalStorageDir = mActivityTestRule.getActivity().getExternalFilesDir(
Environment.DIRECTORY_DOWNLOADS);
File qrcodeFile = new File(externalStorageDir, filename);
Assert.assertTrue(qrcodeFile.createNewFile());
long downloadId = ShareImageFileUtils.addCompletedDownload(qrcodeFile);
Assert.assertNotEquals(0L, downloadId);
DownloadManager downloadManager =
(DownloadManager) mActivityTestRule.getActivity().getSystemService(
Context.DOWNLOAD_SERVICE);
DownloadManager.Query query = new DownloadManager.Query().setFilterById(downloadId);
Cursor c = downloadManager.query(query);
Assert.assertNotNull(c);
Assert.assertTrue(c.moveToFirst());
Assert.assertEquals(
filename, c.getString(c.getColumnIndexOrThrow(DownloadManager.COLUMN_TITLE)));
c.close();
}
} }
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment