Commit 06121b69 authored by Gayane Petrosyan's avatar Gayane Petrosyan Committed by Commit Bot

[QRCode Android] QR code download notifications

screenshots:
https://screenshot.googleplex.com/sTQU0sY0yEL.png
https://screenshot.googleplex.com/e30urO57vCZ.png

Bug: 993920
Change-Id: I9210c56ceb0ce7f59913bcbfa448bc5fb0c036ee
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2125448Reviewed-by: default avatarTheresa  <twellington@chromium.org>
Reviewed-by: default avatarPeter Beverloo <peter@chromium.org>
Reviewed-by: default avatarTanya Gupta <tgupta@chromium.org>
Commit-Queue: Gayane Petrosyan <gayane@chromium.org>
Cr-Commit-Position: refs/heads/master@{#754896}
parent 7dbda08d
......@@ -1219,6 +1219,10 @@ android:value="true" />
<receiver android:name="org.chromium.chrome.browser.send_tab_to_self.NotificationManager$TimeoutReceiver"
android:exported="false"/>
<!-- Save image notification receiver -->
<receiver android:name="org.chromium.chrome.browser.share.SaveImageNotificationManager$TapReceiver"
android:exported="false"/>
<receiver android:name="org.chromium.chrome.browser.sharing.click_to_call.ClickToCallMessageHandler$TapReceiver"
android:exported="false"/>
<receiver android:name="org.chromium.chrome.browser.sharing.shared_clipboard.SharedClipboardMessageHandler$TapReceiver"
......
......@@ -1398,6 +1398,9 @@
<receiver
android:exported="false"
android:name="org.chromium.chrome.browser.send_tab_to_self.NotificationManager$TimeoutReceiver"/>
<receiver
android:exported="false"
android:name="org.chromium.chrome.browser.share.SaveImageNotificationManager$TapReceiver"/>
<receiver
android:exported="false"
android:name="org.chromium.chrome.browser.sharing.click_to_call.ClickToCallMessageHandler$TapReceiver"/>
......
......@@ -139,6 +139,7 @@ public class NotificationConstants {
public static final String GROUP_SEND_TAB_TO_SELF = "SendTabToSelf";
public static final String GROUP_CLICK_TO_CALL = "ClickToCall";
public static final String GROUP_SHARED_CLIPBOARD = "SharedClipboard";
public static final String GROUP_SHARE_SAVE_IMAGE = "ShareSaveImage";
// Web notification group names are set dynamically as this prefix + notification origin.
// For example, 'Web:chromium.org' for a notification from chromium.org.
......
......@@ -52,7 +52,8 @@ public class NotificationUmaTracker {
SystemNotificationType.SEND_TAB_TO_SELF, SystemNotificationType.UPDATES,
SystemNotificationType.CLICK_TO_CALL, SystemNotificationType.SHARED_CLIPBOARD,
SystemNotificationType.PERMISSION_REQUESTS,
SystemNotificationType.PERMISSION_REQUESTS_HIGH, SystemNotificationType.ANNOUNCEMENT})
SystemNotificationType.PERMISSION_REQUESTS_HIGH, SystemNotificationType.ANNOUNCEMENT,
SystemNotificationType.SHARE_SAVE_IMAGE})
@Retention(RetentionPolicy.SOURCE)
public @interface SystemNotificationType {
int UNKNOWN = -1;
......@@ -78,8 +79,9 @@ public class NotificationUmaTracker {
int PERMISSION_REQUESTS = 19;
int PERMISSION_REQUESTS_HIGH = 20;
int ANNOUNCEMENT = 21;
int SHARE_SAVE_IMAGE = 22;
int NUM_ENTRIES = 22;
int NUM_ENTRIES = 23;
}
/*
......
......@@ -10,5 +10,16 @@ include_rules = [
"+chrome/android/java/src/org/chromium/chrome/browser/screenshot/EditorScreenshotTask.java",
"+chrome/android/java/src/org/chromium/chrome/browser/tab/Tab.java",
"+content/public/android/java/src/org/chromium/content_public/browser/RenderWidgetHostView.java",
"+content/public/android/java/src/org/chromium/content_public/browser/WebContents.java"
"+content/public/android/java/src/org/chromium/content_public/browser/WebContents.java",
"+chrome/android/java/src/org/chromium/chrome/browser/media/MediaViewerUtils.java",
"+chrome/android/java/src/org/chromium/chrome/browser/notifications/ChromeNotificationBuilder.java",
"+chrome/android/java/src/org/chromium/chrome/browser/notifications/NotificationBuilderFactory.java",
"+chrome/android/java/src/org/chromium/chrome/browser/notifications/NotificationConstants.java",
"+chrome/android/java/src/org/chromium/chrome/browser/notifications/NotificationIntentInterceptor.java",
"+chrome/android/java/src/org/chromium/chrome/browser/notifications/NotificationManagerProxy.java",
"+chrome/android/java/src/org/chromium/chrome/browser/notifications/NotificationManagerProxyImpl.java",
"+chrome/android/java/src/org/chromium/chrome/browser/notifications/NotificationMetadata.java",
"+chrome/android/java/src/org/chromium/chrome/browser/notifications/NotificationUmaTracker.java",
"+chrome/android/java/src/org/chromium/chrome/browser/notifications/PendingIntentProvider.java",
"+chrome/android/java/src/org/chromium/chrome/browser/notifications/channels/ChannelDefinitions.java"
]
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package org.chromium.chrome.browser.share;
import android.app.Notification;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import androidx.core.app.NotificationCompat;
import org.chromium.base.IntentUtils;
import org.chromium.chrome.R;
import org.chromium.chrome.browser.IntentHandler;
import org.chromium.chrome.browser.media.MediaViewerUtils;
import org.chromium.chrome.browser.notifications.ChromeNotificationBuilder;
import org.chromium.chrome.browser.notifications.NotificationBuilderFactory;
import org.chromium.chrome.browser.notifications.NotificationConstants;
import org.chromium.chrome.browser.notifications.NotificationIntentInterceptor;
import org.chromium.chrome.browser.notifications.NotificationManagerProxy;
import org.chromium.chrome.browser.notifications.NotificationManagerProxyImpl;
import org.chromium.chrome.browser.notifications.NotificationMetadata;
import org.chromium.chrome.browser.notifications.NotificationUmaTracker;
import org.chromium.chrome.browser.notifications.PendingIntentProvider;
import org.chromium.chrome.browser.notifications.channels.ChannelDefinitions;
/**
* Manages save image related notifications for Android.
*/
public class SaveImageNotificationManager {
private static final String TAG = "share";
private static final String MIME_TYPE = "image/JPEG";
private static final String EXTRA_INTENT_TYPE =
"org.chromium.chrome.browser.share.SaveImageNotificationManager.EXTRA_INTENT_TYPE";
private static final String EXTRA_ID =
"org.chromium.chrome.browser.share.SaveImageNotificationManager.EXTRA_ID";
private static int sNextId;
/**
* Handles the tapping of a notification by starting an intent to view downloaded content.
*/
public static final class TapReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
int intentType = IntentUtils.safeGetIntExtra(
intent, EXTRA_INTENT_TYPE, NotificationIntentInterceptor.IntentType.UNKNOWN);
int id = IntentUtils.safeGetIntExtra(
intent, EXTRA_ID, NotificationIntentInterceptor.IntentType.UNKNOWN);
switch (intentType) {
case NotificationIntentInterceptor.IntentType.UNKNOWN:
break;
case NotificationIntentInterceptor.IntentType.CONTENT_INTENT:
Uri uri = intent.getData();
Intent viewIntent = MediaViewerUtils.getMediaViewerIntent(
uri, uri, Intent.normalizeMimeType(MIME_TYPE), true);
IntentHandler.startActivityForTrustedIntent(viewIntent);
close(context, id);
break;
case NotificationIntentInterceptor.IntentType.DELETE_INTENT:
close(context, id);
}
}
}
/**
* Displays a notification for successful download.
*
* @param context The Context to use for accessing notification manager.
* @param uri The Uri of the notification to show.
* @param notificationTitle The title of the notification.
*/
public static void showSuccessNotification(Context context, Uri uri, String notificationTitle) {
String notificationText =
context.getResources().getString(R.string.download_notification_completed);
int iconId = R.drawable.offline_pin;
showNotification(context, uri, notificationTitle, notificationText, iconId,
NotificationIntentInterceptor.IntentType.CONTENT_INTENT);
}
/**
* Displays a notification for failed download.
*
* @param context The Context to use for accessing notification manager.
* @param uri The Uri of the notification to hide.
* @param notificationTitle The title of the notification.
*/
public static void showFailureNotification(Context context, Uri uri, String notificationTitle) {
String notificationText =
context.getResources().getString(R.string.download_notification_failed);
int iconId = android.R.drawable.stat_sys_download_done;
showNotification(context, uri, notificationTitle, notificationText, iconId,
NotificationIntentInterceptor.IntentType.DELETE_INTENT);
}
/**
* Displays a notification.
*
* @param context The Context to use for accessing notification manager.
* @param uri The Uri of the notification to hide.
* @param title The title of the notification.
* @param text The text of the notification.
* @param iconId The resource id for icon of the notification.
* @param intentType The type of the notification.
*/
private static void showNotification(
Context context, Uri uri, String title, String text, int iconId, int intentType) {
PendingIntentProvider contentIntent = PendingIntentProvider.getBroadcast(context, sNextId++,
new Intent(context, SaveImageNotificationManager.TapReceiver.class)
.setData(uri)
.putExtra(EXTRA_INTENT_TYPE, intentType)
.putExtra(EXTRA_ID, sNextId),
PendingIntent.FLAG_UPDATE_CURRENT);
NotificationManagerProxy manager = new NotificationManagerProxyImpl(context);
// Build the notification.
ChromeNotificationBuilder builder =
NotificationBuilderFactory
.createChromeNotificationBuilder(true, ChannelDefinitions.ChannelId.SHARING,
null,
new NotificationMetadata(
NotificationUmaTracker.SystemNotificationType
.SHARE_SAVE_IMAGE,
NotificationConstants.GROUP_SHARE_SAVE_IMAGE, sNextId))
.setContentIntent(contentIntent)
.setContentTitle(title)
.setContentText(text)
.setGroup(NotificationConstants.GROUP_SHARE_SAVE_IMAGE)
.setPriorityBeforeO(NotificationCompat.PRIORITY_HIGH)
.setVibrate(new long[0])
.setSmallIcon(iconId)
.setDefaults(Notification.DEFAULT_ALL);
manager.notify(builder.buildChromeNotification());
}
/**
* Closes a notification.
*
* @param context The Context to use for accessing notification manager.
* @param guid The GUID of the notification to hide.
*/
private static void close(Context context, int id) {
new NotificationManagerProxyImpl(context).cancel(
NotificationConstants.GROUP_SHARE_SAVE_IMAGE, id);
}
}
\ No newline at end of file
......@@ -5,6 +5,7 @@
package org.chromium.chrome.browser.share;
import android.annotation.TargetApi;
import android.app.DownloadManager;
import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.Context;
......@@ -136,7 +137,7 @@ public class ShareImageFileUtils {
callback.onResult(uri);
}
@Override
public void onImageSaveError() {}
public void onImageSaveError(String displayName) {}
};
String fileName = String.valueOf(System.currentTimeMillis());
......@@ -173,7 +174,7 @@ public class ShareImageFileUtils {
*/
public interface OnImageSaveListener {
void onImageSaved(Uri uri, String displayName);
void onImageSaveError();
void onImageSaveError(String displayName);
}
/**
......@@ -221,26 +222,32 @@ public class ShareImageFileUtils {
StreamUtil.closeQuietly(fOut);
}
Uri uri = FileUtils.getUriForFile(destFile);
Uri uri = null;
if (!isTemporary) {
if (BuildInfo.isAtLeastQ()) {
uri = addToMediaStore(destFile);
} else {
addCompletedDownload(destFile);
long downloadId = addCompletedDownload(destFile);
DownloadManager manager =
(DownloadManager) ContextUtils.getApplicationContext()
.getSystemService(Context.DOWNLOAD_SERVICE);
return manager.getUriForDownloadedFile(downloadId);
}
} else {
uri = FileUtils.getUriForFile(destFile);
}
return uri;
}
@Override
protected void onCancelled() {
listener.onImageSaveError();
listener.onImageSaveError(fileName);
}
@Override
protected void onPostExecute(Uri uri) {
if (uri == null) {
listener.onImageSaveError();
listener.onImageSaveError(fileName);
return;
}
......
......@@ -6,15 +6,15 @@ package org.chromium.chrome.browser.share.qrcode.share_tab;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.net.Uri;
import android.view.View;
import org.chromium.base.metrics.RecordUserAction;
import org.chromium.chrome.R;
import org.chromium.chrome.browser.share.SaveImageNotificationManager;
import org.chromium.chrome.browser.share.ShareImageFileUtils;
import org.chromium.ui.modelutil.PropertyModel;
import org.chromium.ui.widget.Toast;
/**
* QrCodeShareMediator is in charge of calculating and setting values for QrCodeShareViewProperties.
......@@ -36,7 +36,6 @@ class QrCodeShareMediator implements ShareImageFileUtils.OnImageSaveListener {
// TODO(gayane): Request generated QR code bitmap with a callback that sets QRCODE_BITMAP
// property.
mPropertyModel.set(QrCodeShareViewProperties.QRCODE_BITMAP, getTestBitmap());
}
/** Triggers download for the generated QR code bitmap if available. */
......@@ -63,26 +62,25 @@ class QrCodeShareMediator implements ShareImageFileUtils.OnImageSaveListener {
// ShareImageFileUtils.OnImageSaveListener implementation.
@Override
public void onImageSaved(Uri uri, String displayName) {
// TODO(gayane): Maybe need to show confirmation message.
mPropertyModel.set(QrCodeShareViewProperties.DOWNLOAD_SUCCESSFUL, true);
RecordUserAction.record("SharingQRCode.DownloadQRCode.Succeeded");
// Notify success.
Toast.makeText(mContext,
mContext.getResources().getString(R.string.download_notification_completed),
Toast.LENGTH_LONG)
.show();
SaveImageNotificationManager.showSuccessNotification(mContext, uri, displayName);
}
@Override
public void onImageSaveError() {
// TODO(gayane): Maybe need to show error message.
mPropertyModel.set(QrCodeShareViewProperties.DOWNLOAD_SUCCESSFUL, false);
public void onImageSaveError(String displayName) {
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;
// Notify failure.
Toast.makeText(mContext,
mContext.getResources().getString(R.string.download_notification_failed),
Toast.LENGTH_LONG)
.show();
SaveImageNotificationManager.showFailureNotification(mContext, null, displayName);
}
}
\ No newline at end of file
......@@ -7,7 +7,6 @@ package org.chromium.chrome.browser.share.qrcode.share_tab;
import android.graphics.Bitmap;
import org.chromium.ui.modelutil.PropertyKey;
import org.chromium.ui.modelutil.PropertyModel.WritableBooleanPropertyKey;
import org.chromium.ui.modelutil.PropertyModel.WritableObjectPropertyKey;
class QrCodeShareViewProperties {
......@@ -15,9 +14,5 @@ class QrCodeShareViewProperties {
public static final WritableObjectPropertyKey<Bitmap> QRCODE_BITMAP =
new WritableObjectPropertyKey<>();
/** Indicates whether download was successful. */
public static final WritableBooleanPropertyKey DOWNLOAD_SUCCESSFUL =
new WritableBooleanPropertyKey();
public static final PropertyKey[] ALL_KEYS = {QRCODE_BITMAP, DOWNLOAD_SUCCESSFUL};
public static final PropertyKey[] ALL_KEYS = {QRCODE_BITMAP};
}
......@@ -5,6 +5,7 @@
# TODO(crbug/1022172): This should be a separate build target when circular
# dependencies are removed.
share_java_sources = [
"//chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/SaveImageNotificationManager.java",
"//chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/ShareImageFileUtils.java",
"//chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/qrcode/QrCodeCoordinator.java",
"//chrome/browser/share/android/java/src/org/chromium/chrome/browser/share/qrcode/QrCodeDialog.java",
......
......@@ -260,7 +260,7 @@ public class ShareImageFileUtilsTest {
}
@Override
public void onImageSaveError() {
public void onImageSaveError(String displayName) {
Assert.fail();
}
};
......@@ -299,7 +299,7 @@ public class ShareImageFileUtilsTest {
}
@Override
public void onImageSaveError() {
public void onImageSaveError(String displayName) {
Assert.fail();
}
};
......
......@@ -64411,6 +64411,7 @@ would be helpful to identify which type is being sent.
<int value="19" label="Permission Requests"/>
<int value="20" label="Permission Requests High"/>
<int value="21" label="Announcement"/>
<int value="22" label="Share Save Image"/>
</enum>
<enum name="TabBackgroundLoadStatus">
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