diff --git a/app/src/common/shared/org/mozilla/vrbrowser/PermissionDelegate.java b/app/src/common/shared/org/mozilla/vrbrowser/PermissionDelegate.java index 9e48d0ba97..a8a79a9907 100644 --- a/app/src/common/shared/org/mozilla/vrbrowser/PermissionDelegate.java +++ b/app/src/common/shared/org/mozilla/vrbrowser/PermissionDelegate.java @@ -1,8 +1,10 @@ package org.mozilla.vrbrowser; +import android.Manifest; import android.app.Activity; import android.content.Context; import android.content.pm.PackageManager; +import android.support.annotation.NonNull; import android.util.Log; import org.mozilla.geckoview.GeckoSession; @@ -54,7 +56,7 @@ public void onRequestPermissionsResult(int requestCode, String[] permissions, in } } - private void handleContentPermission(final String aUri, final PermissionWidget.PermissionType aType, final Callback aCallback) { + public void handlePermission(final String aUri, final PermissionWidget.PermissionType aType, final Callback aCallback) { if (mPermissionWidget == null) { mPermissionWidget = new PermissionWidget(mContext); mPermissionWidget.getPlacement().parentHandle = mParentWidgetHandle; @@ -122,7 +124,7 @@ public void onContentPermissionRequest(GeckoSession aSession, String aUri, int a return; } - handleContentPermission(aUri, type, callback); + handlePermission(aUri, type, callback); } @Override @@ -155,6 +157,51 @@ public void reject() { } }; - handleContentPermission(aUri, type, callback); + handlePermission(aUri, type, callback); + } + + public boolean isPermissionGranted(@NonNull String permission) { + return mContext.checkSelfPermission(permission) == PackageManager.PERMISSION_GRANTED; + } + + // Handle app permissions that Gecko doesn't handle itself yet + public void onAppPermissionRequest(final GeckoSession aSession, String aUri, final String permission, final Callback callback) { + Log.d(LOGTAG, "onAppPermissionRequest: " + aUri); + + // If the permission is already granted we just grant + if (mContext.checkSelfPermission(permission) != PackageManager.PERMISSION_GRANTED) { + + // Check if we support a rationale for that permission + PermissionWidget.PermissionType type = null; + if (permission.equals(Manifest.permission.READ_EXTERNAL_STORAGE)) { + type = PermissionWidget.PermissionType.ReadExternalStorage; + } + + if (type != null) { + // Show rationale + handlePermission(mContext.getString(R.string.app_name), type, new Callback() { + @Override + public void grant() { + onAndroidPermissionsRequest(aSession, new String[]{permission}, callback); + } + + @Override + public void reject() { + if (callback != null) { + callback.reject(); + } + } + }); + + } else { + // Let Android handle the permission request + onAndroidPermissionsRequest(aSession, new String[]{permission}, callback); + } + + } else { + if (callback != null) { + callback.grant(); + } + } } } diff --git a/app/src/common/shared/org/mozilla/vrbrowser/SessionStore.java b/app/src/common/shared/org/mozilla/vrbrowser/SessionStore.java index 4d23650723..8a96d11ded 100644 --- a/app/src/common/shared/org/mozilla/vrbrowser/SessionStore.java +++ b/app/src/common/shared/org/mozilla/vrbrowser/SessionStore.java @@ -14,16 +14,30 @@ import android.view.inputmethod.CursorAnchorInfo; import android.view.inputmethod.ExtractedText; import android.view.inputmethod.ExtractedTextRequest; + import org.mozilla.gecko.GeckoAppShell; import org.mozilla.gecko.GeckoProfile; -import org.mozilla.geckoview.*; +import org.mozilla.geckoview.GeckoResult; +import org.mozilla.geckoview.GeckoRuntime; +import org.mozilla.geckoview.GeckoRuntimeSettings; +import org.mozilla.geckoview.GeckoSession; +import org.mozilla.geckoview.GeckoSessionSettings; import org.mozilla.vrbrowser.telemetry.TelemetryWrapper; +import org.mozilla.vrbrowser.utils.ValueHolder; import java.io.File; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; -import java.util.*; +import java.util.ArrayDeque; +import java.util.ArrayList; +import java.util.Deque; +import java.util.HashMap; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; public class SessionStore implements GeckoSession.NavigationDelegate, GeckoSession.ProgressDelegate, GeckoSession.ContentDelegate, GeckoSession.TextInputDelegate, GeckoSession.TrackingProtectionDelegate, @@ -787,14 +801,31 @@ public GeckoResult onLoadRequest(@NonNull GeckoSession aSession, @NonNull String aUri, @TargetWindow int target, @LoadRequestFlags int flags) { - GeckoResult result = new GeckoResult<>(); + final GeckoResult result = new GeckoResult<>(); if (aUri.equalsIgnoreCase(PRIVATE_BROWSING_URI)) { switchPrivateMode(); result.complete(true); } else { - result.complete(false); + final ValueHolder count = new ValueHolder(new Integer(0)); + final ValueHolder listenersResult = new ValueHolder(new Boolean(false)); + for (GeckoSession.NavigationDelegate listener: mNavigationListeners) { + GeckoResult listenerResult = listener.onLoadRequest(aSession, aUri, target, flags); + listenerResult.then(new GeckoResult.OnValueListener() { + @Nullable + @Override + public GeckoResult onValue(@Nullable Boolean value) { + listenersResult.setValue(listenersResult.getValue().booleanValue() | value); + if (count.getValue() == mNavigationListeners.size() - 1) { + result.complete(listenersResult.getValue()); + } + count.setValue(count.getValue().intValue() + 1); + + return null; + } + }); + } } return result; diff --git a/app/src/common/shared/org/mozilla/vrbrowser/VRBrowserActivity.java b/app/src/common/shared/org/mozilla/vrbrowser/VRBrowserActivity.java index a1f71e8c71..c818257061 100644 --- a/app/src/common/shared/org/mozilla/vrbrowser/VRBrowserActivity.java +++ b/app/src/common/shared/org/mozilla/vrbrowser/VRBrowserActivity.java @@ -17,21 +17,30 @@ import android.os.Handler; import android.os.Looper; import android.support.annotation.Keep; +import android.support.annotation.NonNull; import android.util.Log; import android.view.KeyEvent; -import android.view.MotionEvent; import android.view.View; import android.view.ViewTreeObserver; import android.widget.FrameLayout; + import org.mozilla.gecko.GeckoVRManager; import org.mozilla.gecko.util.ThreadUtils; import org.mozilla.geckoview.CrashReporter; import org.mozilla.geckoview.GeckoRuntime; +import org.mozilla.geckoview.GeckoSession; import org.mozilla.vrbrowser.audio.AudioEngine; import org.mozilla.vrbrowser.audio.VRAudioTheme; import org.mozilla.vrbrowser.search.SearchEngine; import org.mozilla.vrbrowser.telemetry.TelemetryWrapper; -import org.mozilla.vrbrowser.ui.*; +import org.mozilla.vrbrowser.ui.BrowserWidget; +import org.mozilla.vrbrowser.ui.CrashDialogWidget; +import org.mozilla.vrbrowser.ui.KeyboardWidget; +import org.mozilla.vrbrowser.ui.NavigationBarWidget; +import org.mozilla.vrbrowser.ui.OffscreenDisplay; +import org.mozilla.vrbrowser.ui.RootWidget; +import org.mozilla.vrbrowser.ui.TopBarWidget; +import org.mozilla.vrbrowser.ui.TrayWidget; import java.io.IOException; import java.net.URISyntaxException; @@ -851,6 +860,20 @@ public void run() { }); } + @Override + public boolean isPermissionGranted(@NonNull String permission) { + return mPermissionDelegate.isPermissionGranted(permission); + } + + @Override + public void requestPermission(@NonNull String uri, @NonNull String permission, GeckoSession.PermissionDelegate.Callback aCallback) { + mPermissionDelegate.onAppPermissionRequest( + SessionStore.get().getCurrentSession(), + uri, + permission, + aCallback); + } + @Override public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) { super.onRequestPermissionsResult(requestCode, permissions, grantResults); diff --git a/app/src/common/shared/org/mozilla/vrbrowser/WidgetManagerDelegate.java b/app/src/common/shared/org/mozilla/vrbrowser/WidgetManagerDelegate.java index 1cac1d169e..f8eec930a5 100644 --- a/app/src/common/shared/org/mozilla/vrbrowser/WidgetManagerDelegate.java +++ b/app/src/common/shared/org/mozilla/vrbrowser/WidgetManagerDelegate.java @@ -3,6 +3,8 @@ import android.support.annotation.NonNull; import android.view.View; +import org.mozilla.geckoview.GeckoSession; + public interface WidgetManagerDelegate { interface UpdateListener { void onWidgetUpdate(Widget aWidget); @@ -30,8 +32,10 @@ interface FocusChangeListener { void keyboardDismissed(); void updateEnvironment(); void updatePointerColor(); - void addPermissionListener(@NonNull PermissionListener aListener); - void removePermissionListener(@NonNull PermissionListener aListener); void addFocusChangeListener(@NonNull FocusChangeListener aListener); void removeFocusChangeListener(@NonNull FocusChangeListener aListener); + void addPermissionListener(PermissionListener aListener); + void removePermissionListener(PermissionListener aListener); + boolean isPermissionGranted(@NonNull String permission); + void requestPermission(@NonNull String uri, @NonNull String permission, GeckoSession.PermissionDelegate.Callback aCallback); } diff --git a/app/src/common/shared/org/mozilla/vrbrowser/ui/NavigationBarWidget.java b/app/src/common/shared/org/mozilla/vrbrowser/ui/NavigationBarWidget.java index 7fe501013e..ec97e1d524 100644 --- a/app/src/common/shared/org/mozilla/vrbrowser/ui/NavigationBarWidget.java +++ b/app/src/common/shared/org/mozilla/vrbrowser/ui/NavigationBarWidget.java @@ -6,6 +6,7 @@ package org.mozilla.vrbrowser.ui; import android.content.Context; +import android.net.Uri; import android.support.annotation.NonNull; import android.util.AttributeSet; import android.util.Log; @@ -404,12 +405,45 @@ public void onCanGoForward(GeckoSession aSession, boolean canGoForward) { } @Override - public GeckoResult onLoadRequest(GeckoSession aSession, String aUri, int target, int flags) { + public GeckoResult onLoadRequest(GeckoSession aSession, final String aUri, int target, int flags) { if (mURLBar != null) { Log.d(LOGTAG, "Got onLoadUri"); mURLBar.setURL(aUri); } - return null; + + final GeckoResult result = new GeckoResult<>(); + if (aUri != null) { + Uri uri = Uri.parse(aUri); + if (uri.getScheme().equals("file")) { + if (!mWidgetManager.isPermissionGranted(android.Manifest.permission.READ_EXTERNAL_STORAGE)) { + mWidgetManager.requestPermission( + aUri, + android.Manifest.permission.READ_EXTERNAL_STORAGE, + new GeckoSession.PermissionDelegate.Callback() { + @Override + public void grant() { + result.complete(false); + } + + @Override + public void reject() { + result.complete(false); + } + }); + + } else { + result.complete(false); + } + + } else { + result.complete(false); + } + + } else { + result.complete(false); + } + + return result; } // Progress Listener diff --git a/app/src/common/shared/org/mozilla/vrbrowser/ui/PermissionWidget.java b/app/src/common/shared/org/mozilla/vrbrowser/ui/PermissionWidget.java index 70c3d6348a..a04a93ec5e 100644 --- a/app/src/common/shared/org/mozilla/vrbrowser/ui/PermissionWidget.java +++ b/app/src/common/shared/org/mozilla/vrbrowser/ui/PermissionWidget.java @@ -43,7 +43,8 @@ public enum PermissionType { Microphone, CameraAndMicrophone, Location, - Notification + Notification, + ReadExternalStorage } public PermissionWidget(Context aContext) { @@ -132,6 +133,10 @@ public void showPrompt(String aUri, PermissionType aType, GeckoSession.Permissio messageId = R.string.permission_notification; iconId = R.drawable.ic_icon_dialog_notification; break; + case ReadExternalStorage: + messageId = R.string.permission_read_external_storage; + iconId = R.drawable.ic_icon_dialog_notification; + break; default: Log.e(LOGTAG, "Unimplemented permission type: " + aType); aCallback.reject(); diff --git a/app/src/common/shared/org/mozilla/vrbrowser/utils/ValueHolder.java b/app/src/common/shared/org/mozilla/vrbrowser/utils/ValueHolder.java new file mode 100644 index 0000000000..2c12f7af2a --- /dev/null +++ b/app/src/common/shared/org/mozilla/vrbrowser/utils/ValueHolder.java @@ -0,0 +1,19 @@ +package org.mozilla.vrbrowser.utils; + +public class ValueHolder { + + private T value; + + public ValueHolder(T aValue) { + value = aValue; + } + + public T getValue() { + return value; + } + + public void setValue(Object newValue) { + value = (T) newValue; + } + +} diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 8fe28a21a5..234cbbbc59 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -6,11 +6,11 @@ + + - - + android:protectionLevel="signature"/> Will you allow %1$s to access your location? Will you allow %1$s to send notifications? + + Will you allow %1$s to read your external storage? Speak now version %1$s Crash Reporting