Commit 2149fc5b authored by Tanya Gupta's avatar Tanya Gupta Committed by Commit Bot

[QRCode] Created a open settings sheet which is displayed when

the user denies permission for Chrome to prompt for camera permission.

Screenshot:
https://screenshot.googleplex.com/himQOS9PQrY

Bug: 1046574
Change-Id: I0df3cf870f0f08e5f0bc08e0dd4f8d963f4797ae
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2037860
Commit-Queue: Tanya Gupta <tgupta@chromium.org>
Reviewed-by: default avatarGayane Petrosyan <gayane@chromium.org>
Cr-Commit-Position: refs/heads/master@{#741571}
parent 2b5a142e
<?xml version="1.0" encoding="utf-8"?>
<!-- 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. -->
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/permission_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<org.chromium.ui.widget.ChromeImageView
android:id="@+id/camera_icon"
android:layout_marginTop="200dp"
android:layout_gravity="center_horizontal"
android:layout_width="125dp"
android:layout_height="125dp"
android:scaleType="center"
android:importantForAccessibility="no"
android:src="@drawable/camera" />
<TextView
android:id="@+id/qrcode_permission_image"
android:layout_width="250dp"
android:layout_height="wrap_content"
android:layout_marginTop="80dp"
android:drawablePadding="24dp"
android:gravity="center_horizontal"
android:layout_gravity="center_horizontal"
android:text="@string/qr_code_open_settings_description"/>
<org.chromium.ui.widget.ButtonCompat
android:id="@+id/open_settings_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:layout_marginTop="30dp"
android:text="@string/qr_code_open_settings_label"
style="@style/TextButton"/>
</LinearLayout>
\ No newline at end of file
......@@ -24,7 +24,11 @@ import java.util.ArrayList;
public class QrCodeDialog extends DialogFragment {
private ArrayList<QrCodeDialogTab> mTabs;
/** The QrCodeDialog constructor. */
/**
* The QrCodeDialog constructor.
* TODO(tgupta): This causes an NPE when the user moves away from Chrome
* and comes back. Fix this issue.
*/
public QrCodeDialog() {}
/**
......
......@@ -60,9 +60,7 @@ public class QrCodeScanMediator implements Camera.PreviewCallback {
mPropertyModel = propertyModel;
mPermissionDelegate = new ActivityAndroidPermissionDelegate(
new WeakReference<Activity>((Activity) mContext));
mPropertyModel.set(QrCodeScanViewProperties.HAS_CAMERA_PERMISSION, hasCameraPermission());
mPropertyModel.set(
QrCodeScanViewProperties.CAN_PROMPT_FOR_PERMISSION, canPromptForPermission());
updatePermissionSettings();
mDetector = new BarcodeDetector.Builder(context).build();
mNavigationObserver = observer;
mTabCreator = tabCreator;
......@@ -75,17 +73,31 @@ public class QrCodeScanMediator implements Camera.PreviewCallback {
== PackageManager.PERMISSION_GRANTED;
}
/** Returns whether the user has granted camera permissions. */
/** Returns whether the user can be prompted for camera permissions. */
private Boolean canPromptForPermission() {
return mPermissionDelegate.canRequestPermission(permission.CAMERA);
}
/** Updates the permission settings with the latest values. */
private void updatePermissionSettings() {
mPropertyModel.set(
QrCodeScanViewProperties.CAN_PROMPT_FOR_PERMISSION, canPromptForPermission());
mPropertyModel.set(QrCodeScanViewProperties.HAS_CAMERA_PERMISSION, hasCameraPermission());
}
/**
* Sets whether QrCode UI is on foreground.
*
* @param isOnForeground Indicates whether this component UI is current on foreground.
*/
public void setIsOnForeground(boolean isOnForeground) {
// If the app is in the foreground, the permissions need to be checked again to ensure
// the user is seeing the right thing.
if (isOnForeground) {
updatePermissionSettings();
}
// This is intentionally done last so that the view is updated according to the latest
// permissions.
mPropertyModel.set(QrCodeScanViewProperties.IS_ON_FOREGROUND, isOnForeground);
}
......@@ -104,11 +116,14 @@ public class QrCodeScanMediator implements Camera.PreviewCallback {
if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
mPropertyModel.set(QrCodeScanViewProperties.HAS_CAMERA_PERMISSION, true);
} else {
mPropertyModel.set(QrCodeScanViewProperties.HAS_CAMERA_PERMISSION, false);
// The order in which these fields are important because it causes updates to
// the view. CanPromptForPermission must be updated first so that it doesn't
// cause the view to be updated twice creating a flicker effect.
if (!mPermissionDelegate.canRequestPermission(permission.CAMERA)) {
mPropertyModel.set(
QrCodeScanViewProperties.CAN_PROMPT_FOR_PERMISSION, false);
}
mPropertyModel.set(QrCodeScanViewProperties.HAS_CAMERA_PERMISSION, false);
}
}
};
......
......@@ -4,10 +4,14 @@
package org.chromium.chrome.browser.share.qrcode.scan_tab;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.hardware.Camera;
import android.hardware.Camera.ErrorCallback;
import android.hardware.Camera.PreviewCallback;
import android.net.Uri;
import android.provider.Settings;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
......@@ -22,6 +26,7 @@ import org.chromium.ui.widget.ButtonCompat;
*/
class QrCodeScanView {
public interface PermissionPrompter { void promptForCameraPermission(); }
public interface PermissionPromptAllowedChecker { Boolean canPromptForPermission(); }
private final Context mContext;
private final FrameLayout mView;
......@@ -33,6 +38,7 @@ class QrCodeScanView {
private CameraPreview mCameraPreview;
private View mPermissionsView;
private View mCameraErrorView;
private View mOpenSettingsView;
/**
* The QrCodeScanView constructor.
......@@ -45,16 +51,13 @@ class QrCodeScanView {
mContext = context;
mCameraPreviewCallback = cameraCallback;
mView = new FrameLayout(context);
mOpenSettingsView = createOpenSettingsView(context);
mView.setLayoutParams(
new FrameLayout.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
mPermissionsView = createPermissionView(context, permissionPrompter);
mCameraErrorView = createCameraErrorView(context);
}
public View getView() {
return mView;
}
private View createPermissionView(Context context, PermissionPrompter permissionPrompter) {
View permissionView = (View) LayoutInflater.from(context).inflate(
org.chromium.chrome.browser.share.qrcode.R.layout.qrcode_permission_layout, null,
......@@ -106,6 +109,31 @@ class QrCodeScanView {
}
};
public View getView() {
return mView;
}
/**
* Creates a view that opens the settings page for the app and allows the user to
* to update permissions including give the app camera permission.
*/
private View createOpenSettingsView(Context context) {
View openSettingsView = (View) LayoutInflater.from(context).inflate(
org.chromium.chrome.browser.share.qrcode.R.layout.qrcode_open_settings_layout, null,
false);
ButtonCompat cameraPermissionPrompt = openSettingsView.findViewById(
org.chromium.chrome.browser.share.qrcode.R.id.open_settings_button);
cameraPermissionPrompt.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
Intent openSettingsIntent = getAppInfoIntent(context.getPackageName());
((Activity) context).startActivity(openSettingsIntent);
}
});
return openSettingsView;
}
/**
* Sets camera if possible.
*
......@@ -119,13 +147,30 @@ class QrCodeScanView {
return;
}
mHasCameraPermission = hasCameraPermission;
updateView();
}
/**
* Update the view based on the latest environment:
* - app is in the foreground
* - user has given camera permission
* - user can be prompted for camera permission
*/
private void updateView() {
// The scan tab is not in the foreground so don't do any rendering.
if (!mIsOnForeground) {
return;
}
// Check that the camera permission has changed and that it is now set to true.
if (hasCameraPermission) {
if (mHasCameraPermission && mCameraPreview == null) {
setCameraPreview();
} else {
// TODO(tgupta): Check that the user can be prompted. If not, show the error
// screen instead.
} else if (mHasCameraPermission && mCameraPreview != null) {
updateCameraPreviewState();
} else if (mCanPromptForPermission) {
displayPermissionDialog();
} else {
displayOpenSettingsDialog();
}
}
......@@ -133,23 +178,26 @@ class QrCodeScanView {
* Checks whether Chrome can prompt the user for Camera permission. Updates the view accordingly
* to let the user know if the permission has been permanently denied.
*
* @param canPromptForPermission
* @param canPromptForPermission Indicates whether the user can be prompted for camera
* permission
*/
public void canPromptForPermissionChanged(Boolean canPromptForPermission) {
if (mCanPromptForPermission != canPromptForPermission && !canPromptForPermission) {
// User chose the "don't ask again option, display the appropriate message
// TODO (tgupta): Update this message
}
mCanPromptForPermission = canPromptForPermission;
updateView();
}
/**
* Applies changes necessary to camera preview.
*
* @param isOnForeground Indicates whether this component UI is current on foreground.
* @param isOnForeground Indicates whether this component UI is currently on foreground.
*/
public void onForegroundChanged(Boolean isOnForeground) {
mIsOnForeground = isOnForeground;
updateCameraPreviewState();
if (!mIsOnForeground && mCameraPreview != null) {
mCameraPreview.stopCamera();
} else {
updateView();
}
}
/** Creates and sets the camera preview. */
......@@ -176,7 +224,7 @@ class QrCodeScanView {
return;
}
if (mIsOnForeground) {
if (mIsOnForeground && mHasCameraPermission) {
mCameraPreview.startCamera();
} else {
mCameraPreview.stopCamera();
......@@ -201,4 +249,20 @@ class QrCodeScanView {
mView.removeAllViews();
mView.addView(mCameraErrorView);
}
/**
* Displays the open settings dialog.
*/
private void displayOpenSettingsDialog() {
mView.removeAllViews();
mView.addView(mOpenSettingsView);
}
/**
* Returns an Intent to show the App Info page for the current app.
*/
private Intent getAppInfoIntent(String packageName) {
Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
intent.setData(new Uri.Builder().scheme("package").opaquePart(packageName).build());
return intent;
}
}
......@@ -3998,6 +3998,10 @@ The site does NOT gain access to the camera. The camera images are only visible
Please enable your camera to scan a QR code
</message>
<message name="IDS_QR_CODE_OPEN_SETTINGS_DESCRIPTION" desc="Text on QR code sharing tab indicating that user needs to open settings to give camera permission.">
To scan a QR code, change your settings so that Chrome can use your camera
</message>
<message name="IDS_QR_CODE_PERMISSION_CONTINUE_LABEL" desc="Text on button on QR code sharing tab triggering camera permission dialog.">
Continue
</message>
......@@ -4018,6 +4022,10 @@ The site does NOT gain access to the camera. The camera images are only visible
Can't open your camera. Something went wrong.
</message>
<message name="IDS_QR_CODE_OPEN_SETTINGS_LABEL" desc="Text on button on QR code sharing tab triggering Android settings.">
Open Settings
</message>
<!-- Chime DFM module strings -->
<message name="IDS_CHIME_MODULE_TITLE" desc="Text shown when the chime module is referenced in install start, success, failure UI (e.g. in IDS_MODULE_INSTALL_START_TEXT, which will expand to 'Installing Google Notifications Platform for Chrome…').">
Google Notifications Platform
......
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