Commit 7e231f0a authored by tedchoc's avatar tedchoc Committed by Commit bot

Add initial support for runtime permissions in android M.

After clicking the "Allow" button in the infobars, Chrome will request
the permission if required from Android.

BUG=496447

Review URL: https://codereview.chromium.org/1164973003

Cr-Commit-Position: refs/heads/master@{#333663}
parent e30560c8
...@@ -4,7 +4,18 @@ ...@@ -4,7 +4,18 @@
package org.chromium.chrome.browser.infobar; package org.chromium.chrome.browser.infobar;
import android.Manifest;
import android.content.Context;
import android.content.pm.PackageManager;
import android.graphics.Bitmap; import android.graphics.Bitmap;
import android.os.Process;
import org.chromium.chrome.browser.ContentSettingsType;
import org.chromium.ui.base.WindowAndroid;
import org.chromium.ui.base.WindowAndroid.PermissionCallback;
import java.util.ArrayList;
import java.util.List;
/** /**
* An infobar that presents the user with several buttons. * An infobar that presents the user with several buttons.
...@@ -24,6 +35,14 @@ public class ConfirmInfoBar extends InfoBar { ...@@ -24,6 +35,14 @@ public class ConfirmInfoBar extends InfoBar {
/** Notified when one of the buttons is clicked. */ /** Notified when one of the buttons is clicked. */
private final InfoBarListeners.Confirm mConfirmListener; private final InfoBarListeners.Confirm mConfirmListener;
private WindowAndroid mWindowAndroid;
/**
* The list of {@link ContentSettingsType}s being requested by this infobar. Can be null or
* empty if none apply.
*/
private int[] mContentSettings;
public ConfirmInfoBar(InfoBarListeners.Confirm confirmListener, int iconDrawableId, public ConfirmInfoBar(InfoBarListeners.Confirm confirmListener, int iconDrawableId,
Bitmap iconBitmap, String message, String linkText, String primaryButtonText, Bitmap iconBitmap, String message, String linkText, String primaryButtonText,
String secondaryButtonText) { String secondaryButtonText) {
...@@ -34,13 +53,106 @@ public class ConfirmInfoBar extends InfoBar { ...@@ -34,13 +53,106 @@ public class ConfirmInfoBar extends InfoBar {
mConfirmListener = confirmListener; mConfirmListener = confirmListener;
} }
/**
* Specifies the {@link ContentSettingsType}s that are controlled by this InfoBar.
*
* @param windowAndroid The WindowAndroid that will be used to check for the necessary
* permissions.
* @param contentSettings The list of {@link ContentSettingsType}s whose access is guarded
* by this InfoBar.
*/
protected void setContentSettings(
WindowAndroid windowAndroid, int[] contentSettings) {
mWindowAndroid = windowAndroid;
mContentSettings = contentSettings;
assert windowAndroid != null
: "A WindowAndroid must be specified to request access to content settings";
}
@Override @Override
public void createContent(InfoBarLayout layout) { public void createContent(InfoBarLayout layout) {
layout.setButtons(mPrimaryButtonText, mSecondaryButtonText, mTertiaryButtonText); layout.setButtons(mPrimaryButtonText, mSecondaryButtonText, mTertiaryButtonText);
} }
private static boolean hasPermission(Context context, String permission) {
return context.checkPermission(permission, Process.myPid(), Process.myUid())
!= PackageManager.PERMISSION_DENIED;
}
private List<String> getPermissionsToRequest() {
Context context = getContext();
List<String> permissionsToRequest = new ArrayList<String>();
for (int i = 0; i < mContentSettings.length; i++) {
switch (mContentSettings[i]) {
case ContentSettingsType.CONTENT_SETTINGS_TYPE_GEOLOCATION:
if (!hasPermission(context, Manifest.permission.ACCESS_FINE_LOCATION)) {
permissionsToRequest.add(Manifest.permission.ACCESS_FINE_LOCATION);
}
break;
case ContentSettingsType.CONTENT_SETTINGS_TYPE_MEDIASTREAM_CAMERA:
if (!hasPermission(context, Manifest.permission.CAMERA)) {
permissionsToRequest.add(Manifest.permission.CAMERA);
}
break;
case ContentSettingsType.CONTENT_SETTINGS_TYPE_MEDIASTREAM_MIC:
if (!hasPermission(context, Manifest.permission.RECORD_AUDIO)) {
permissionsToRequest.add(Manifest.permission.RECORD_AUDIO);
}
break;
default:
// No associated Android permission, so just skip it.
break;
}
}
return permissionsToRequest;
}
@Override @Override
public void onButtonClicked(boolean isPrimaryButton) { public void onButtonClicked(final boolean isPrimaryButton) {
if (mWindowAndroid == null || mContentSettings == null
|| !isPrimaryButton || getContext() == null) {
onButtonClickedInternal(isPrimaryButton);
return;
}
List<String> permissionsToRequest = getPermissionsToRequest();
if (permissionsToRequest.isEmpty()) {
onButtonClickedInternal(isPrimaryButton);
return;
}
PermissionCallback callback = new PermissionCallback() {
@Override
public void onRequestPermissionsResult(
String[] permissions, int[] grantResults) {
boolean grantedAllPermissions = true;
for (int i = 0; i < grantResults.length; i++) {
if (grantResults[i] == PackageManager.PERMISSION_DENIED) {
grantedAllPermissions = false;
break;
}
}
if (!grantedAllPermissions) {
onCloseButtonClicked();
} else {
onButtonClickedInternal(true);
}
}
@Override
public void onRequestPermissionAborted() {
onCloseButtonClicked();
}
};
mWindowAndroid.requestPermissions(
permissionsToRequest.toArray(new String[permissionsToRequest.size()]),
callback);
}
private void onButtonClickedInternal(boolean isPrimaryButton) {
if (mConfirmListener != null) { if (mConfirmListener != null) {
mConfirmListener.onConfirmInfoBarButtonClicked(this, isPrimaryButton); mConfirmListener.onConfirmInfoBarButtonClicked(this, isPrimaryButton);
} }
......
...@@ -8,6 +8,7 @@ import android.graphics.Bitmap; ...@@ -8,6 +8,7 @@ import android.graphics.Bitmap;
import org.chromium.base.CalledByNative; import org.chromium.base.CalledByNative;
import org.chromium.chrome.browser.ResourceId; import org.chromium.chrome.browser.ResourceId;
import org.chromium.ui.base.WindowAndroid;
/** /**
* Provides JNI methods for ConfirmInfoBars * Provides JNI methods for ConfirmInfoBars
...@@ -24,22 +25,28 @@ public class ConfirmInfoBarDelegate { ...@@ -24,22 +25,28 @@ public class ConfirmInfoBarDelegate {
/** /**
* Creates and begins the process for showing a ConfirmInfoBar. * Creates and begins the process for showing a ConfirmInfoBar.
* @param windowAndroid The owning window for the infobar.
* @param enumeratedIconId ID corresponding to the icon that will be shown for the InfoBar. * @param enumeratedIconId ID corresponding to the icon that will be shown for the InfoBar.
* The ID must have been mapped using the ResourceMapper class before * The ID must have been mapped using the ResourceMapper class before
* passing it to this function. * passing it to this function.
* @param iconBitmap Bitmap to use if there is no equivalent Java resource for enumeratedIconId. * @param iconBitmap Bitmap to use if there is no equivalent Java resource for
* enumeratedIconId.
* @param message Message to display to the user indicating what the InfoBar is for. * @param message Message to display to the user indicating what the InfoBar is for.
* @param linkText Link text to display in addition to the message. * @param linkText Link text to display in addition to the message.
* @param buttonOk String to display on the OK button. * @param buttonOk String to display on the OK button.
* @param buttonCancel String to display on the Cancel button. * @param buttonCancel String to display on the Cancel button.
* @param contentSettings The list of ContentSettingTypes being requested by this infobar.
*/ */
@CalledByNative @CalledByNative
InfoBar showConfirmInfoBar(int enumeratedIconId, Bitmap iconBitmap, String message, InfoBar showConfirmInfoBar(WindowAndroid windowAndroid, int enumeratedIconId,
String linkText, String buttonOk, String buttonCancel) { Bitmap iconBitmap, String message, String linkText, String buttonOk,
String buttonCancel, int[] contentSettings) {
int drawableId = ResourceId.mapToDrawableId(enumeratedIconId); int drawableId = ResourceId.mapToDrawableId(enumeratedIconId);
ConfirmInfoBar infoBar = new ConfirmInfoBar( ConfirmInfoBar infoBar = new ConfirmInfoBar(
null, drawableId, iconBitmap, message, linkText, buttonOk, buttonCancel); null, drawableId, iconBitmap, message, linkText, buttonOk, buttonCancel);
infoBar.setContentSettings(windowAndroid, contentSettings);
return infoBar; return infoBar;
} }
} }
...@@ -71,6 +71,7 @@ import org.chromium.content_public.browser.LoadUrlParams; ...@@ -71,6 +71,7 @@ import org.chromium.content_public.browser.LoadUrlParams;
import org.chromium.content_public.browser.readback_types.ReadbackResponse; import org.chromium.content_public.browser.readback_types.ReadbackResponse;
import org.chromium.printing.PrintManagerDelegateImpl; import org.chromium.printing.PrintManagerDelegateImpl;
import org.chromium.printing.PrintingController; import org.chromium.printing.PrintingController;
import org.chromium.ui.base.ActivityWindowAndroid;
import org.chromium.ui.base.DeviceFormFactor; import org.chromium.ui.base.DeviceFormFactor;
import org.chromium.ui.base.PageTransition; import org.chromium.ui.base.PageTransition;
import org.chromium.ui.base.WindowAndroid; import org.chromium.ui.base.WindowAndroid;
...@@ -91,7 +92,7 @@ public abstract class CompositorChromeActivity extends ChromeActivity ...@@ -91,7 +92,7 @@ public abstract class CompositorChromeActivity extends ChromeActivity
private static final String TAG = "CompositorChromeActivity"; private static final String TAG = "CompositorChromeActivity";
private WindowAndroid mWindowAndroid; private ActivityWindowAndroid mWindowAndroid;
private ChromeFullscreenManager mFullscreenManager; private ChromeFullscreenManager mFullscreenManager;
private CompositorViewHolder mCompositorViewHolder; private CompositorViewHolder mCompositorViewHolder;
private ContextualSearchManager mContextualSearchManager; private ContextualSearchManager mContextualSearchManager;
...@@ -331,6 +332,15 @@ public abstract class CompositorChromeActivity extends ChromeActivity ...@@ -331,6 +332,15 @@ public abstract class CompositorChromeActivity extends ChromeActivity
return mWindowAndroid.onActivityResult(requestCode, resultCode, intent); return mWindowAndroid.onActivityResult(requestCode, resultCode, intent);
} }
// @Override[ANDROID-M]
public void onRequestPermissionsResult(int requestCode, String[] permissions,
int[] grantResults) {
if (mWindowAndroid != null) {
mWindowAndroid.onRequestPermissionsResult(requestCode, permissions, grantResults);
}
//super.onRequestPermissionsResult(requestCode, permissions, grantResults);
}
@Override @Override
protected void onSaveInstanceState(Bundle outState) { protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState); super.onSaveInstanceState(outState);
......
...@@ -88,7 +88,7 @@ public class ChromeShellActivity extends AppCompatActivity implements AppMenuPro ...@@ -88,7 +88,7 @@ public class ChromeShellActivity extends AppCompatActivity implements AppMenuPro
} }
}; };
private WindowAndroid mWindow; private ActivityWindowAndroid mWindow;
private TabManager mTabManager; private TabManager mTabManager;
private ChromeShellToolbar mToolbar; private ChromeShellToolbar mToolbar;
private DevToolsServer mDevToolsServer; private DevToolsServer mDevToolsServer;
......
...@@ -34,6 +34,11 @@ void PermissionInfobarDelegate::InfoBarDismissed() { ...@@ -34,6 +34,11 @@ void PermissionInfobarDelegate::InfoBarDismissed() {
SetPermission(false, false); SetPermission(false, false);
} }
PermissionInfobarDelegate*
PermissionInfobarDelegate::AsPermissionInfobarDelegate() {
return this;
}
base::string16 PermissionInfobarDelegate::GetButtonLabel( base::string16 PermissionInfobarDelegate::GetButtonLabel(
InfoBarButton button) const { InfoBarButton button) const {
return l10n_util::GetStringUTF16((button == BUTTON_OK) ? return l10n_util::GetStringUTF16((button == BUTTON_OK) ?
......
...@@ -20,6 +20,9 @@ class PermissionQueueController; ...@@ -20,6 +20,9 @@ class PermissionQueueController;
// provide an icon and a message text to the infobar. // provide an icon and a message text to the infobar.
class PermissionInfobarDelegate : public ConfirmInfoBarDelegate { class PermissionInfobarDelegate : public ConfirmInfoBarDelegate {
public:
ContentSettingsType content_setting() const { return type_; }
protected: protected:
PermissionInfobarDelegate(PermissionQueueController* controller, PermissionInfobarDelegate(PermissionQueueController* controller,
const PermissionRequestID& id, const PermissionRequestID& id,
...@@ -31,6 +34,7 @@ class PermissionInfobarDelegate : public ConfirmInfoBarDelegate { ...@@ -31,6 +34,7 @@ class PermissionInfobarDelegate : public ConfirmInfoBarDelegate {
// ConfirmInfoBarDelegate: // ConfirmInfoBarDelegate:
Type GetInfoBarType() const override; Type GetInfoBarType() const override;
void InfoBarDismissed() override; void InfoBarDismissed() override;
PermissionInfobarDelegate* AsPermissionInfobarDelegate() override;
base::string16 GetButtonLabel(InfoBarButton button) const override; base::string16 GetButtonLabel(InfoBarButton button) const override;
bool Accept() override; bool Accept() override;
bool Cancel() override; bool Cancel() override;
......
...@@ -66,6 +66,14 @@ bool MediaStreamInfoBarDelegate::Create( ...@@ -66,6 +66,14 @@ bool MediaStreamInfoBarDelegate::Create(
return true; return true;
} }
bool MediaStreamInfoBarDelegate::IsRequestingVideoAccess() const {
return controller_->HasVideo();
}
bool MediaStreamInfoBarDelegate::IsRequestingMicrophoneAccess() const {
return controller_->HasAudio();
}
MediaStreamInfoBarDelegate::MediaStreamInfoBarDelegate( MediaStreamInfoBarDelegate::MediaStreamInfoBarDelegate(
scoped_ptr<MediaStreamDevicesController> controller) scoped_ptr<MediaStreamDevicesController> controller)
: ConfirmInfoBarDelegate(), : ConfirmInfoBarDelegate(),
......
...@@ -30,6 +30,9 @@ class MediaStreamInfoBarDelegate : public ConfirmInfoBarDelegate { ...@@ -30,6 +30,9 @@ class MediaStreamInfoBarDelegate : public ConfirmInfoBarDelegate {
const content::MediaStreamRequest& request, const content::MediaStreamRequest& request,
const content::MediaResponseCallback& callback); const content::MediaResponseCallback& callback);
bool IsRequestingVideoAccess() const;
bool IsRequestingMicrophoneAccess() const;
private: private:
friend class WebRtcTestBase; friend class WebRtcTestBase;
......
...@@ -4,13 +4,22 @@ ...@@ -4,13 +4,22 @@
#include "chrome/browser/ui/android/infobars/confirm_infobar.h" #include "chrome/browser/ui/android/infobars/confirm_infobar.h"
#include <vector>
#include "base/android/jni_android.h" #include "base/android/jni_android.h"
#include "base/android/jni_array.h"
#include "base/android/jni_string.h" #include "base/android/jni_string.h"
#include "base/logging.h" #include "base/logging.h"
#include "chrome/browser/android/resource_mapper.h" #include "chrome/browser/android/resource_mapper.h"
#include "chrome/browser/content_settings/permission_infobar_delegate.h"
#include "chrome/browser/infobars/infobar_service.h" #include "chrome/browser/infobars/infobar_service.h"
#include "chrome/browser/media/media_stream_infobar_delegate.h"
#include "components/content_settings/core/common/content_settings_types.h"
#include "components/infobars/core/confirm_infobar_delegate.h" #include "components/infobars/core/confirm_infobar_delegate.h"
#include "content/public/browser/android/content_view_core.h"
#include "content/public/browser/web_contents.h"
#include "jni/ConfirmInfoBarDelegate_jni.h" #include "jni/ConfirmInfoBarDelegate_jni.h"
#include "ui/android/window_android.h"
#include "ui/gfx/android/java_bitmap.h" #include "ui/gfx/android/java_bitmap.h"
#include "ui/gfx/image/image.h" #include "ui/gfx/image/image.h"
...@@ -53,10 +62,37 @@ base::android::ScopedJavaLocalRef<jobject> ConfirmInfoBar::CreateRenderInfoBar( ...@@ -53,10 +62,37 @@ base::android::ScopedJavaLocalRef<jobject> ConfirmInfoBar::CreateRenderInfoBar(
java_bitmap = gfx::ConvertToJavaBitmap(delegate->GetIcon().ToSkBitmap()); java_bitmap = gfx::ConvertToJavaBitmap(delegate->GetIcon().ToSkBitmap());
} }
std::vector<int> content_settings;
if (delegate->AsPermissionInfobarDelegate()) {
content_settings.push_back(
delegate->AsPermissionInfobarDelegate()->content_setting());
} else if (delegate->AsMediaStreamInfoBarDelegate()) {
MediaStreamInfoBarDelegate* media_delegate =
delegate->AsMediaStreamInfoBarDelegate();
if (media_delegate->IsRequestingVideoAccess()) {
content_settings.push_back(
ContentSettingsType::CONTENT_SETTINGS_TYPE_MEDIASTREAM_CAMERA);
} else if (media_delegate->IsRequestingMicrophoneAccess()) {
content_settings.push_back(
ContentSettingsType::CONTENT_SETTINGS_TYPE_MEDIASTREAM_MIC);
}
}
content::WebContents* web_contents =
InfoBarService::WebContentsFromInfoBar(this);
DCHECK(web_contents);
content::ContentViewCore* cvc =
content::ContentViewCore::FromWebContents(web_contents);
DCHECK(cvc);
base::android::ScopedJavaLocalRef<jobject> jwindow_android =
cvc->GetWindowAndroid()->GetJavaObject();
return Java_ConfirmInfoBarDelegate_showConfirmInfoBar( return Java_ConfirmInfoBarDelegate_showConfirmInfoBar(
env, java_confirm_delegate_.obj(), GetEnumeratedIconId(), env, java_confirm_delegate_.obj(),
java_bitmap.obj(), message_text.obj(), link_text.obj(), jwindow_android.obj(), GetEnumeratedIconId(), java_bitmap.obj(),
ok_button_text.obj(), cancel_button_text.obj()); message_text.obj(), link_text.obj(), ok_button_text.obj(),
cancel_button_text.obj(),
base::android::ToJavaIntArray(env, content_settings).obj());
} }
void ConfirmInfoBar::OnLinkClicked(JNIEnv* env, jobject obj) { void ConfirmInfoBar::OnLinkClicked(JNIEnv* env, jobject obj) {
......
...@@ -68,6 +68,10 @@ NativeAppInfoBarDelegate* InfoBarDelegate::AsNativeAppInfoBarDelegate() { ...@@ -68,6 +68,10 @@ NativeAppInfoBarDelegate* InfoBarDelegate::AsNativeAppInfoBarDelegate() {
return nullptr; return nullptr;
} }
PermissionInfobarDelegate* InfoBarDelegate::AsPermissionInfobarDelegate() {
return nullptr;
}
PopupBlockedInfoBarDelegate* InfoBarDelegate::AsPopupBlockedInfoBarDelegate() { PopupBlockedInfoBarDelegate* InfoBarDelegate::AsPopupBlockedInfoBarDelegate() {
return nullptr; return nullptr;
} }
......
...@@ -14,6 +14,7 @@ class ConfirmInfoBarDelegate; ...@@ -14,6 +14,7 @@ class ConfirmInfoBarDelegate;
class InsecureContentInfoBarDelegate; class InsecureContentInfoBarDelegate;
class MediaStreamInfoBarDelegate; class MediaStreamInfoBarDelegate;
class NativeAppInfoBarDelegate; class NativeAppInfoBarDelegate;
class PermissionInfobarDelegate;
class PopupBlockedInfoBarDelegate; class PopupBlockedInfoBarDelegate;
class RegisterProtocolHandlerInfoBarDelegate; class RegisterProtocolHandlerInfoBarDelegate;
class ScreenCaptureInfoBarDelegate; class ScreenCaptureInfoBarDelegate;
...@@ -114,6 +115,7 @@ class InfoBarDelegate { ...@@ -114,6 +115,7 @@ class InfoBarDelegate {
virtual InsecureContentInfoBarDelegate* AsInsecureContentInfoBarDelegate(); virtual InsecureContentInfoBarDelegate* AsInsecureContentInfoBarDelegate();
virtual MediaStreamInfoBarDelegate* AsMediaStreamInfoBarDelegate(); virtual MediaStreamInfoBarDelegate* AsMediaStreamInfoBarDelegate();
virtual NativeAppInfoBarDelegate* AsNativeAppInfoBarDelegate(); virtual NativeAppInfoBarDelegate* AsNativeAppInfoBarDelegate();
virtual PermissionInfobarDelegate* AsPermissionInfobarDelegate();
virtual PopupBlockedInfoBarDelegate* AsPopupBlockedInfoBarDelegate(); virtual PopupBlockedInfoBarDelegate* AsPopupBlockedInfoBarDelegate();
virtual RegisterProtocolHandlerInfoBarDelegate* virtual RegisterProtocolHandlerInfoBarDelegate*
AsRegisterProtocolHandlerInfoBarDelegate(); AsRegisterProtocolHandlerInfoBarDelegate();
......
...@@ -24,7 +24,6 @@ import org.chromium.content.browser.ContentViewCore; ...@@ -24,7 +24,6 @@ import org.chromium.content.browser.ContentViewCore;
import org.chromium.content_shell.Shell; import org.chromium.content_shell.Shell;
import org.chromium.content_shell.ShellManager; import org.chromium.content_shell.ShellManager;
import org.chromium.ui.base.ActivityWindowAndroid; import org.chromium.ui.base.ActivityWindowAndroid;
import org.chromium.ui.base.WindowAndroid;
/** /**
* Test activity used for verifying the different configuration options for the ContentLinker. * Test activity used for verifying the different configuration options for the ContentLinker.
...@@ -43,7 +42,7 @@ public class ChromiumLinkerTestActivity extends Activity { ...@@ -43,7 +42,7 @@ public class ChromiumLinkerTestActivity extends Activity {
private static final String LOW_MEMORY_DEVICE = "--low-memory-device"; private static final String LOW_MEMORY_DEVICE = "--low-memory-device";
private ShellManager mShellManager; private ShellManager mShellManager;
private WindowAndroid mWindowAndroid; private ActivityWindowAndroid mWindowAndroid;
@Override @Override
public void onCreate(final Bundle savedInstanceState) { public void onCreate(final Bundle savedInstanceState) {
...@@ -63,21 +62,21 @@ public class ChromiumLinkerTestActivity extends Activity { ...@@ -63,21 +62,21 @@ public class ChromiumLinkerTestActivity extends Activity {
// reason, so parse the command-line differently here: // reason, so parse the command-line differently here:
boolean hasLowMemoryDeviceSwitch = false; boolean hasLowMemoryDeviceSwitch = false;
String[] cmdline = CommandLine.getJavaSwitchesOrNull(); String[] cmdline = CommandLine.getJavaSwitchesOrNull();
if (cmdline == null) if (cmdline == null) {
Log.i(TAG, "Command line is null"); Log.i(TAG, "Command line is null");
else { } else {
Log.i(TAG, "Command line is:"); Log.i(TAG, "Command line is:");
for (int n = 0; n < cmdline.length; ++n) { for (int n = 0; n < cmdline.length; ++n) {
Log.i(TAG, " '" + cmdline[n] + "'"); Log.i(TAG, " '" + cmdline[n] + "'");
if (cmdline[n].equals(LOW_MEMORY_DEVICE)) if (cmdline[n].equals(LOW_MEMORY_DEVICE)) hasLowMemoryDeviceSwitch = true;
hasLowMemoryDeviceSwitch = true;
} }
} }
// Determine which kind of device to simulate from the command-line. // Determine which kind of device to simulate from the command-line.
int memoryDeviceConfig = Linker.MEMORY_DEVICE_CONFIG_NORMAL; int memoryDeviceConfig = Linker.MEMORY_DEVICE_CONFIG_NORMAL;
if (hasLowMemoryDeviceSwitch) if (hasLowMemoryDeviceSwitch) {
memoryDeviceConfig = Linker.MEMORY_DEVICE_CONFIG_LOW; memoryDeviceConfig = Linker.MEMORY_DEVICE_CONFIG_LOW;
}
Linker.setMemoryDeviceConfig(memoryDeviceConfig); Linker.setMemoryDeviceConfig(memoryDeviceConfig);
// Register the test runner class by name. // Register the test runner class by name.
...@@ -185,12 +184,14 @@ public class ChromiumLinkerTestActivity extends Activity { ...@@ -185,12 +184,14 @@ public class ChromiumLinkerTestActivity extends Activity {
* one is not showing. * one is not showing.
*/ */
public ContentViewCore getActiveContentViewCore() { public ContentViewCore getActiveContentViewCore() {
if (mShellManager == null) if (mShellManager == null) {
return null; return null;
}
Shell shell = mShellManager.getActiveShell(); Shell shell = mShellManager.getActiveShell();
if (shell == null) if (shell == null) {
return null; return null;
}
return shell.getContentViewCore(); return shell.getContentViewCore();
} }
......
...@@ -28,7 +28,6 @@ import org.chromium.content_public.browser.WebContents; ...@@ -28,7 +28,6 @@ import org.chromium.content_public.browser.WebContents;
import org.chromium.content_shell.Shell; import org.chromium.content_shell.Shell;
import org.chromium.content_shell.ShellManager; import org.chromium.content_shell.ShellManager;
import org.chromium.ui.base.ActivityWindowAndroid; import org.chromium.ui.base.ActivityWindowAndroid;
import org.chromium.ui.base.WindowAndroid;
/** /**
* Activity for managing the Content Shell. * Activity for managing the Content Shell.
...@@ -41,7 +40,7 @@ public class ContentShellActivity extends Activity { ...@@ -41,7 +40,7 @@ public class ContentShellActivity extends Activity {
public static final String COMMAND_LINE_ARGS_KEY = "commandLineArgs"; public static final String COMMAND_LINE_ARGS_KEY = "commandLineArgs";
private ShellManager mShellManager; private ShellManager mShellManager;
private WindowAndroid mWindowAndroid; private ActivityWindowAndroid mWindowAndroid;
private Intent mLastSentIntent; private Intent mLastSentIntent;
@Override @Override
......
...@@ -9,6 +9,8 @@ import android.app.PendingIntent; ...@@ -9,6 +9,8 @@ import android.app.PendingIntent;
import android.content.ActivityNotFoundException; import android.content.ActivityNotFoundException;
import android.content.Intent; import android.content.Intent;
import android.content.IntentSender.SendIntentException; import android.content.IntentSender.SendIntentException;
import android.os.Handler;
import android.util.SparseArray;
import android.view.View; import android.view.View;
import org.chromium.base.ActivityState; import org.chromium.base.ActivityState;
...@@ -16,6 +18,8 @@ import org.chromium.base.ApplicationStatus; ...@@ -16,6 +18,8 @@ import org.chromium.base.ApplicationStatus;
import org.chromium.ui.UiUtils; import org.chromium.ui.UiUtils;
import java.lang.ref.WeakReference; import java.lang.ref.WeakReference;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
/** /**
* The class provides the WindowAndroid's implementation which requires * The class provides the WindowAndroid's implementation which requires
...@@ -31,6 +35,12 @@ public class ActivityWindowAndroid ...@@ -31,6 +35,12 @@ public class ActivityWindowAndroid
private static final String TAG = "ActivityWindowAndroid"; private static final String TAG = "ActivityWindowAndroid";
private final WeakReference<Activity> mActivityRef; private final WeakReference<Activity> mActivityRef;
private final Handler mHandler;
private final SparseArray<PermissionCallback> mOutstandingPermissionRequests;
private final Runnable mClearPermissionRequestsTask;
private Method mRequestPermissionsMethod;
private int mNextRequestCode = 0; private int mNextRequestCode = 0;
/** /**
...@@ -51,6 +61,17 @@ public class ActivityWindowAndroid ...@@ -51,6 +61,17 @@ public class ActivityWindowAndroid
public ActivityWindowAndroid(Activity activity, boolean listenToActivityState) { public ActivityWindowAndroid(Activity activity, boolean listenToActivityState) {
super(activity.getApplicationContext()); super(activity.getApplicationContext());
mActivityRef = new WeakReference<Activity>(activity); mActivityRef = new WeakReference<Activity>(activity);
mHandler = new Handler();
mOutstandingPermissionRequests = new SparseArray<PermissionCallback>();
mClearPermissionRequestsTask = new Runnable() {
@Override
public void run() {
for (int i = 0; i < mOutstandingPermissionRequests.size(); i++) {
mOutstandingPermissionRequests.valueAt(i).onRequestPermissionAborted();
}
mOutstandingPermissionRequests.clear();
}
};
if (listenToActivityState) { if (listenToActivityState) {
ApplicationStatus.registerStateListenerForActivity(this, activity); ApplicationStatus.registerStateListenerForActivity(this, activity);
} }
...@@ -113,7 +134,13 @@ public class ActivityWindowAndroid ...@@ -113,7 +134,13 @@ public class ActivityWindowAndroid
activity.finishActivity(requestCode); activity.finishActivity(requestCode);
} }
@Override /**
* Responds to the intent result if the intent was created by the native window.
* @param requestCode Request code of the requested intent.
* @param resultCode Result code of the requested intent.
* @param data The data returned by the intent.
* @return Boolean value of whether the intent was started by the native window.
*/
public boolean onActivityResult(int requestCode, int resultCode, Intent data) { public boolean onActivityResult(int requestCode, int resultCode, Intent data) {
IntentCallback callback = mOutstandingIntents.get(requestCode); IntentCallback callback = mOutstandingIntents.get(requestCode);
mOutstandingIntents.delete(requestCode); mOutstandingIntents.delete(requestCode);
...@@ -132,6 +159,53 @@ public class ActivityWindowAndroid ...@@ -132,6 +159,53 @@ public class ActivityWindowAndroid
return false; return false;
} }
@Override
public void requestPermissions(String[] permissions, PermissionCallback callback) {
mHandler.removeCallbacks(mClearPermissionRequestsTask);
// TODO(tedchoc): Remove the reflection aspect of this once a public M SDK is available.
Activity activity = mActivityRef.get();
if (activity == null) return;
if (mRequestPermissionsMethod == null) {
try {
mRequestPermissionsMethod = Activity.class.getMethod(
"requestPermissions", String[].class, int.class);
} catch (NoSuchMethodException e) {
return;
}
}
int requestCode = generateNextRequestCode();
mOutstandingPermissionRequests.put(requestCode, callback);
try {
mRequestPermissionsMethod.invoke(activity, permissions, requestCode);
} catch (IllegalAccessException e) {
mOutstandingPermissionRequests.delete(requestCode);
} catch (IllegalArgumentException e) {
mOutstandingPermissionRequests.delete(requestCode);
} catch (InvocationTargetException e) {
mOutstandingPermissionRequests.delete(requestCode);
}
}
/**
* Responds to a pending permission result.
* @param requestCode The unique code for the permission request.
* @param permissions The list of permissions in the result.
* @param grantResults Whether the permissions were granted.
* @return Whether the permission request corresponding to a pending permission request.
*/
public boolean onRequestPermissionsResult(int requestCode, String[] permissions,
int[] grantResults) {
PermissionCallback callback = mOutstandingPermissionRequests.get(requestCode);
mOutstandingPermissionRequests.delete(requestCode);
if (callback == null) return false;
callback.onRequestPermissionsResult(permissions, grantResults);
return true;
}
@Override @Override
public WeakReference<Activity> getActivity() { public WeakReference<Activity> getActivity() {
// Return a new WeakReference to prevent clients from releasing our internal WeakReference. // Return a new WeakReference to prevent clients from releasing our internal WeakReference.
...@@ -144,6 +218,11 @@ public class ActivityWindowAndroid ...@@ -144,6 +218,11 @@ public class ActivityWindowAndroid
onActivityPaused(); onActivityPaused();
} else if (newState == ActivityState.RESUMED) { } else if (newState == ActivityState.RESUMED) {
onActivityResumed(); onActivityResumed();
// Work around an issue where we do not always get an onRequestPermissionsResult
// callback if the user hits the back button in the permission dialog instead
// of taking an action.
mHandler.post(mClearPermissionRequestsTask);
} }
} }
......
...@@ -213,6 +213,16 @@ public class WindowAndroid { ...@@ -213,6 +213,16 @@ public class WindowAndroid {
return true; return true;
} }
/**
* Requests the specified permissions are granted for further use.
* @param permissions The list of permissions to request access to.
* @param callback The callback to be notified whether the permissions were granted.
*/
public void requestPermissions(String[] permissions, PermissionCallback callback) {
Log.w(TAG, "Cannot request permissions as the context is not an Activity");
assert false : "Failed to request permissions using a WindowAndroid without an Activity";
}
/** /**
* Displays an error message with a provided error message string. * Displays an error message with a provided error message string.
* @param error The error message string to be displayed. * @param error The error message string to be displayed.
...@@ -305,17 +315,6 @@ public class WindowAndroid { ...@@ -305,17 +315,6 @@ public class WindowAndroid {
nativeOnActivityResumed(mNativeWindowAndroid); nativeOnActivityResumed(mNativeWindowAndroid);
} }
/**
* Responds to the intent result if the intent was created by the native window.
* @param requestCode Request code of the requested intent.
* @param resultCode Result code of the requested intent.
* @param data The data returned by the intent.
* @return Boolean value of whether the intent was started by the native window.
*/
public boolean onActivityResult(int requestCode, int resultCode, Intent data) {
return false;
}
@CalledByNative @CalledByNative
private void requestVSyncUpdate() { private void requestVSyncUpdate() {
mVSyncMonitor.requestUpdate(); mVSyncMonitor.requestUpdate();
...@@ -336,6 +335,23 @@ public class WindowAndroid { ...@@ -336,6 +335,23 @@ public class WindowAndroid {
ContentResolver contentResolver, Intent data); ContentResolver contentResolver, Intent data);
} }
/**
* Callback for permission requests.
*/
public interface PermissionCallback {
/**
* Called upon completing a permission request.
* @param permissions The list of permissions in the result.
* @param grantResults Whether the permissions were granted.
*/
void onRequestPermissionsResult(String[] permissions, int[] grantResults);
/**
* Called when a permission request has been aborted.
*/
void onRequestPermissionAborted();
}
/** /**
* Tests that an activity is available to handle the passed in intent. * Tests that an activity is available to handle the passed in intent.
* @param intent The intent to check. * @param intent The intent to check.
......
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