diff options
15 files changed, 318 insertions, 32 deletions
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/infobar/ConfirmInfoBar.java b/chrome/android/java/src/org/chromium/chrome/browser/infobar/ConfirmInfoBar.java index c33b904..ea4bde7 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/infobar/ConfirmInfoBar.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/infobar/ConfirmInfoBar.java @@ -4,7 +4,18 @@ package org.chromium.chrome.browser.infobar; +import android.Manifest; +import android.content.Context; +import android.content.pm.PackageManager; 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. @@ -24,6 +35,14 @@ public class ConfirmInfoBar extends InfoBar { /** Notified when one of the buttons is clicked. */ 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, Bitmap iconBitmap, String message, String linkText, String primaryButtonText, String secondaryButtonText) { @@ -34,13 +53,106 @@ public class ConfirmInfoBar extends InfoBar { 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 public void createContent(InfoBarLayout layout) { 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 - 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) { mConfirmListener.onConfirmInfoBarButtonClicked(this, isPrimaryButton); } diff --git a/chrome/android/java/src/org/chromium/chrome/browser/infobar/ConfirmInfoBarDelegate.java b/chrome/android/java/src/org/chromium/chrome/browser/infobar/ConfirmInfoBarDelegate.java index d5d0ad1..d82b81c 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/infobar/ConfirmInfoBarDelegate.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/infobar/ConfirmInfoBarDelegate.java @@ -8,6 +8,7 @@ import android.graphics.Bitmap; import org.chromium.base.CalledByNative; import org.chromium.chrome.browser.ResourceId; +import org.chromium.ui.base.WindowAndroid; /** * Provides JNI methods for ConfirmInfoBars @@ -24,22 +25,28 @@ public class ConfirmInfoBarDelegate { /** * 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. * The ID must have been mapped using the ResourceMapper class before * 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 linkText Link text to display in addition to the message. * @param buttonOk String to display on the OK button. * @param buttonCancel String to display on the Cancel button. + * @param contentSettings The list of ContentSettingTypes being requested by this infobar. */ @CalledByNative - InfoBar showConfirmInfoBar(int enumeratedIconId, Bitmap iconBitmap, String message, - String linkText, String buttonOk, String buttonCancel) { + InfoBar showConfirmInfoBar(WindowAndroid windowAndroid, int enumeratedIconId, + Bitmap iconBitmap, String message, String linkText, String buttonOk, + String buttonCancel, int[] contentSettings) { int drawableId = ResourceId.mapToDrawableId(enumeratedIconId); ConfirmInfoBar infoBar = new ConfirmInfoBar( null, drawableId, iconBitmap, message, linkText, buttonOk, buttonCancel); + infoBar.setContentSettings(windowAndroid, contentSettings); + return infoBar; } } diff --git a/chrome/android/java_staging/src/org/chromium/chrome/browser/CompositorChromeActivity.java b/chrome/android/java_staging/src/org/chromium/chrome/browser/CompositorChromeActivity.java index 9b9e7c5..fd20655 100644 --- a/chrome/android/java_staging/src/org/chromium/chrome/browser/CompositorChromeActivity.java +++ b/chrome/android/java_staging/src/org/chromium/chrome/browser/CompositorChromeActivity.java @@ -71,6 +71,7 @@ import org.chromium.content_public.browser.LoadUrlParams; import org.chromium.content_public.browser.readback_types.ReadbackResponse; import org.chromium.printing.PrintManagerDelegateImpl; import org.chromium.printing.PrintingController; +import org.chromium.ui.base.ActivityWindowAndroid; import org.chromium.ui.base.DeviceFormFactor; import org.chromium.ui.base.PageTransition; import org.chromium.ui.base.WindowAndroid; @@ -91,7 +92,7 @@ public abstract class CompositorChromeActivity extends ChromeActivity private static final String TAG = "CompositorChromeActivity"; - private WindowAndroid mWindowAndroid; + private ActivityWindowAndroid mWindowAndroid; private ChromeFullscreenManager mFullscreenManager; private CompositorViewHolder mCompositorViewHolder; private ContextualSearchManager mContextualSearchManager; @@ -331,6 +332,15 @@ public abstract class CompositorChromeActivity extends ChromeActivity 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 protected void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); diff --git a/chrome/android/shell/java/src/org/chromium/chrome/shell/ChromeShellActivity.java b/chrome/android/shell/java/src/org/chromium/chrome/shell/ChromeShellActivity.java index cd2a469..28fa149 100644 --- a/chrome/android/shell/java/src/org/chromium/chrome/shell/ChromeShellActivity.java +++ b/chrome/android/shell/java/src/org/chromium/chrome/shell/ChromeShellActivity.java @@ -88,7 +88,7 @@ public class ChromeShellActivity extends AppCompatActivity implements AppMenuPro } }; - private WindowAndroid mWindow; + private ActivityWindowAndroid mWindow; private TabManager mTabManager; private ChromeShellToolbar mToolbar; private DevToolsServer mDevToolsServer; diff --git a/chrome/browser/content_settings/permission_infobar_delegate.cc b/chrome/browser/content_settings/permission_infobar_delegate.cc index 6117827..4ff0975 100644 --- a/chrome/browser/content_settings/permission_infobar_delegate.cc +++ b/chrome/browser/content_settings/permission_infobar_delegate.cc @@ -34,6 +34,11 @@ void PermissionInfobarDelegate::InfoBarDismissed() { SetPermission(false, false); } +PermissionInfobarDelegate* +PermissionInfobarDelegate::AsPermissionInfobarDelegate() { + return this; +} + base::string16 PermissionInfobarDelegate::GetButtonLabel( InfoBarButton button) const { return l10n_util::GetStringUTF16((button == BUTTON_OK) ? diff --git a/chrome/browser/content_settings/permission_infobar_delegate.h b/chrome/browser/content_settings/permission_infobar_delegate.h index 25bb0b4..519fbca 100644 --- a/chrome/browser/content_settings/permission_infobar_delegate.h +++ b/chrome/browser/content_settings/permission_infobar_delegate.h @@ -20,6 +20,9 @@ class PermissionQueueController; // provide an icon and a message text to the infobar. class PermissionInfobarDelegate : public ConfirmInfoBarDelegate { + public: + ContentSettingsType content_setting() const { return type_; } + protected: PermissionInfobarDelegate(PermissionQueueController* controller, const PermissionRequestID& id, @@ -31,6 +34,7 @@ class PermissionInfobarDelegate : public ConfirmInfoBarDelegate { // ConfirmInfoBarDelegate: Type GetInfoBarType() const override; void InfoBarDismissed() override; + PermissionInfobarDelegate* AsPermissionInfobarDelegate() override; base::string16 GetButtonLabel(InfoBarButton button) const override; bool Accept() override; bool Cancel() override; diff --git a/chrome/browser/media/media_stream_infobar_delegate.cc b/chrome/browser/media/media_stream_infobar_delegate.cc index 6cc451b..6c9c1c0 100644 --- a/chrome/browser/media/media_stream_infobar_delegate.cc +++ b/chrome/browser/media/media_stream_infobar_delegate.cc @@ -66,6 +66,14 @@ bool MediaStreamInfoBarDelegate::Create( return true; } +bool MediaStreamInfoBarDelegate::IsRequestingVideoAccess() const { + return controller_->HasVideo(); +} + +bool MediaStreamInfoBarDelegate::IsRequestingMicrophoneAccess() const { + return controller_->HasAudio(); +} + MediaStreamInfoBarDelegate::MediaStreamInfoBarDelegate( scoped_ptr<MediaStreamDevicesController> controller) : ConfirmInfoBarDelegate(), diff --git a/chrome/browser/media/media_stream_infobar_delegate.h b/chrome/browser/media/media_stream_infobar_delegate.h index e055289..4f86c71 100644 --- a/chrome/browser/media/media_stream_infobar_delegate.h +++ b/chrome/browser/media/media_stream_infobar_delegate.h @@ -30,6 +30,9 @@ class MediaStreamInfoBarDelegate : public ConfirmInfoBarDelegate { const content::MediaStreamRequest& request, const content::MediaResponseCallback& callback); + bool IsRequestingVideoAccess() const; + bool IsRequestingMicrophoneAccess() const; + private: friend class WebRtcTestBase; diff --git a/chrome/browser/ui/android/infobars/confirm_infobar.cc b/chrome/browser/ui/android/infobars/confirm_infobar.cc index 9fef5cf..43dcc78 100644 --- a/chrome/browser/ui/android/infobars/confirm_infobar.cc +++ b/chrome/browser/ui/android/infobars/confirm_infobar.cc @@ -4,13 +4,22 @@ #include "chrome/browser/ui/android/infobars/confirm_infobar.h" +#include <vector> + #include "base/android/jni_android.h" +#include "base/android/jni_array.h" #include "base/android/jni_string.h" #include "base/logging.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/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 "content/public/browser/android/content_view_core.h" +#include "content/public/browser/web_contents.h" #include "jni/ConfirmInfoBarDelegate_jni.h" +#include "ui/android/window_android.h" #include "ui/gfx/android/java_bitmap.h" #include "ui/gfx/image/image.h" @@ -53,10 +62,37 @@ base::android::ScopedJavaLocalRef<jobject> ConfirmInfoBar::CreateRenderInfoBar( 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( - env, java_confirm_delegate_.obj(), GetEnumeratedIconId(), - java_bitmap.obj(), message_text.obj(), link_text.obj(), - ok_button_text.obj(), cancel_button_text.obj()); + env, java_confirm_delegate_.obj(), + jwindow_android.obj(), GetEnumeratedIconId(), java_bitmap.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) { diff --git a/components/infobars/core/infobar_delegate.cc b/components/infobars/core/infobar_delegate.cc index 37fbdf7..d9f7655 100644 --- a/components/infobars/core/infobar_delegate.cc +++ b/components/infobars/core/infobar_delegate.cc @@ -68,6 +68,10 @@ NativeAppInfoBarDelegate* InfoBarDelegate::AsNativeAppInfoBarDelegate() { return nullptr; } +PermissionInfobarDelegate* InfoBarDelegate::AsPermissionInfobarDelegate() { + return nullptr; +} + PopupBlockedInfoBarDelegate* InfoBarDelegate::AsPopupBlockedInfoBarDelegate() { return nullptr; } diff --git a/components/infobars/core/infobar_delegate.h b/components/infobars/core/infobar_delegate.h index ec6cd82..4c1e234 100644 --- a/components/infobars/core/infobar_delegate.h +++ b/components/infobars/core/infobar_delegate.h @@ -14,6 +14,7 @@ class ConfirmInfoBarDelegate; class InsecureContentInfoBarDelegate; class MediaStreamInfoBarDelegate; class NativeAppInfoBarDelegate; +class PermissionInfobarDelegate; class PopupBlockedInfoBarDelegate; class RegisterProtocolHandlerInfoBarDelegate; class ScreenCaptureInfoBarDelegate; @@ -114,6 +115,7 @@ class InfoBarDelegate { virtual InsecureContentInfoBarDelegate* AsInsecureContentInfoBarDelegate(); virtual MediaStreamInfoBarDelegate* AsMediaStreamInfoBarDelegate(); virtual NativeAppInfoBarDelegate* AsNativeAppInfoBarDelegate(); + virtual PermissionInfobarDelegate* AsPermissionInfobarDelegate(); virtual PopupBlockedInfoBarDelegate* AsPopupBlockedInfoBarDelegate(); virtual RegisterProtocolHandlerInfoBarDelegate* AsRegisterProtocolHandlerInfoBarDelegate(); diff --git a/content/shell/android/linker_test_apk/src/org/chromium/chromium_linker_test_apk/ChromiumLinkerTestActivity.java b/content/shell/android/linker_test_apk/src/org/chromium/chromium_linker_test_apk/ChromiumLinkerTestActivity.java index f13e348..aa7a08a 100644 --- a/content/shell/android/linker_test_apk/src/org/chromium/chromium_linker_test_apk/ChromiumLinkerTestActivity.java +++ b/content/shell/android/linker_test_apk/src/org/chromium/chromium_linker_test_apk/ChromiumLinkerTestActivity.java @@ -24,7 +24,6 @@ import org.chromium.content.browser.ContentViewCore; import org.chromium.content_shell.Shell; import org.chromium.content_shell.ShellManager; import org.chromium.ui.base.ActivityWindowAndroid; -import org.chromium.ui.base.WindowAndroid; /** * Test activity used for verifying the different configuration options for the ContentLinker. @@ -43,7 +42,7 @@ public class ChromiumLinkerTestActivity extends Activity { private static final String LOW_MEMORY_DEVICE = "--low-memory-device"; private ShellManager mShellManager; - private WindowAndroid mWindowAndroid; + private ActivityWindowAndroid mWindowAndroid; @Override public void onCreate(final Bundle savedInstanceState) { @@ -63,21 +62,21 @@ public class ChromiumLinkerTestActivity extends Activity { // reason, so parse the command-line differently here: boolean hasLowMemoryDeviceSwitch = false; String[] cmdline = CommandLine.getJavaSwitchesOrNull(); - if (cmdline == null) + if (cmdline == null) { Log.i(TAG, "Command line is null"); - else { + } else { Log.i(TAG, "Command line is:"); for (int n = 0; n < cmdline.length; ++n) { Log.i(TAG, " '" + cmdline[n] + "'"); - if (cmdline[n].equals(LOW_MEMORY_DEVICE)) - hasLowMemoryDeviceSwitch = true; + if (cmdline[n].equals(LOW_MEMORY_DEVICE)) hasLowMemoryDeviceSwitch = true; } } // Determine which kind of device to simulate from the command-line. int memoryDeviceConfig = Linker.MEMORY_DEVICE_CONFIG_NORMAL; - if (hasLowMemoryDeviceSwitch) + if (hasLowMemoryDeviceSwitch) { memoryDeviceConfig = Linker.MEMORY_DEVICE_CONFIG_LOW; + } Linker.setMemoryDeviceConfig(memoryDeviceConfig); // Register the test runner class by name. @@ -185,12 +184,14 @@ public class ChromiumLinkerTestActivity extends Activity { * one is not showing. */ public ContentViewCore getActiveContentViewCore() { - if (mShellManager == null) + if (mShellManager == null) { return null; + } Shell shell = mShellManager.getActiveShell(); - if (shell == null) + if (shell == null) { return null; + } return shell.getContentViewCore(); } diff --git a/content/shell/android/shell_apk/src/org/chromium/content_shell_apk/ContentShellActivity.java b/content/shell/android/shell_apk/src/org/chromium/content_shell_apk/ContentShellActivity.java index ea0c4e6..a394a1b 100644 --- a/content/shell/android/shell_apk/src/org/chromium/content_shell_apk/ContentShellActivity.java +++ b/content/shell/android/shell_apk/src/org/chromium/content_shell_apk/ContentShellActivity.java @@ -28,7 +28,6 @@ import org.chromium.content_public.browser.WebContents; import org.chromium.content_shell.Shell; import org.chromium.content_shell.ShellManager; import org.chromium.ui.base.ActivityWindowAndroid; -import org.chromium.ui.base.WindowAndroid; /** * Activity for managing the Content Shell. @@ -41,7 +40,7 @@ public class ContentShellActivity extends Activity { public static final String COMMAND_LINE_ARGS_KEY = "commandLineArgs"; private ShellManager mShellManager; - private WindowAndroid mWindowAndroid; + private ActivityWindowAndroid mWindowAndroid; private Intent mLastSentIntent; @Override diff --git a/ui/android/java/src/org/chromium/ui/base/ActivityWindowAndroid.java b/ui/android/java/src/org/chromium/ui/base/ActivityWindowAndroid.java index c83a0ca..6e6e308 100644 --- a/ui/android/java/src/org/chromium/ui/base/ActivityWindowAndroid.java +++ b/ui/android/java/src/org/chromium/ui/base/ActivityWindowAndroid.java @@ -9,6 +9,8 @@ import android.app.PendingIntent; import android.content.ActivityNotFoundException; import android.content.Intent; import android.content.IntentSender.SendIntentException; +import android.os.Handler; +import android.util.SparseArray; import android.view.View; import org.chromium.base.ActivityState; @@ -16,6 +18,8 @@ import org.chromium.base.ApplicationStatus; import org.chromium.ui.UiUtils; import java.lang.ref.WeakReference; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; /** * The class provides the WindowAndroid's implementation which requires @@ -31,6 +35,12 @@ public class ActivityWindowAndroid private static final String TAG = "ActivityWindowAndroid"; 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; /** @@ -51,6 +61,17 @@ public class ActivityWindowAndroid public ActivityWindowAndroid(Activity activity, boolean listenToActivityState) { super(activity.getApplicationContext()); 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) { ApplicationStatus.registerStateListenerForActivity(this, activity); } @@ -113,7 +134,13 @@ public class ActivityWindowAndroid 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) { IntentCallback callback = mOutstandingIntents.get(requestCode); mOutstandingIntents.delete(requestCode); @@ -133,6 +160,53 @@ public class ActivityWindowAndroid } @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 public WeakReference<Activity> getActivity() { // Return a new WeakReference to prevent clients from releasing our internal WeakReference. return new WeakReference<Activity>(mActivityRef.get()); @@ -144,6 +218,11 @@ public class ActivityWindowAndroid onActivityPaused(); } else if (newState == ActivityState.RESUMED) { 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); } } diff --git a/ui/android/java/src/org/chromium/ui/base/WindowAndroid.java b/ui/android/java/src/org/chromium/ui/base/WindowAndroid.java index 84a3499..d0dde3d 100644 --- a/ui/android/java/src/org/chromium/ui/base/WindowAndroid.java +++ b/ui/android/java/src/org/chromium/ui/base/WindowAndroid.java @@ -214,6 +214,16 @@ public class WindowAndroid { } /** + * 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. * @param error The error message string to be displayed. */ @@ -305,17 +315,6 @@ public class WindowAndroid { 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 private void requestVSyncUpdate() { mVSyncMonitor.requestUpdate(); @@ -337,6 +336,23 @@ public class WindowAndroid { } /** + * 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. * @param intent The intent to check. * @return True if an activity is available to process this intent when started, meaning that |