Bug 1588150 - Add Install AddOn to GVE. r=snorp

This patch adds a new menu item to GVE that allows users to install an
arbitrary extension given it's URL (usually from a.m.o, but we don't really
verify that).

Differential Revision: https://phabricator.services.mozilla.com/D59973

--HG--
extra : moz-landing-system : lando
This commit is contained in:
Agi Sferro 2020-01-15 22:03:13 +00:00
parent acf609f3d9
commit f3a473d590
4 changed files with 120 additions and 14 deletions

View File

@ -29,6 +29,7 @@ import org.mozilla.geckoview.WebResponse;
import android.Manifest;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.DownloadManager;
import android.app.Notification;
import android.app.NotificationChannel;
@ -46,12 +47,14 @@ import android.os.Bundle;
import android.os.Environment;
import android.os.SystemClock;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.app.ActivityCompat;
import android.support.v4.app.NotificationCompat;
import android.support.v4.app.NotificationManagerCompat;
import android.support.v4.content.ContextCompat;
import android.support.v7.app.ActionBar;
import android.support.v7.app.AppCompatActivity;
import android.text.InputType;
import android.util.Log;
import android.util.LruCache;
import android.view.LayoutInflater;
@ -61,6 +64,7 @@ import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
import android.widget.EditText;
import android.widget.ProgressBar;
import android.widget.RelativeLayout;
@ -88,7 +92,9 @@ interface BrowserActionDelegate {
}
}
class WebExtensionManager implements WebExtension.ActionDelegate, TabSessionManager.TabObserver {
class WebExtensionManager implements WebExtension.ActionDelegate,
WebExtensionController.PromptDelegate,
TabSessionManager.TabObserver {
public WebExtension extension;
private LruCache<WebExtension.Icon, Bitmap> mBitmapCache = new LruCache<>(5);
@ -97,6 +103,12 @@ class WebExtensionManager implements WebExtension.ActionDelegate, TabSessionMana
private WeakReference<BrowserActionDelegate> mActionDelegate;
@Nullable
@Override
public GeckoResult<AllowOrDeny> onInstallPrompt(final @NonNull WebExtension extension) {
return GeckoResult.fromValue(AllowOrDeny.ALLOW);
}
// We only support either one browserAction or one pageAction
private void onAction(final WebExtension extension, final GeckoSession session,
final WebExtension.Action action) {
@ -181,7 +193,7 @@ class WebExtensionManager implements WebExtension.ActionDelegate, TabSessionMana
return;
}
if (resolved.enabled == null || !resolved.enabled) {
if (resolved == null || resolved.enabled == null || !resolved.enabled) {
actionDelegate.onActionButton(null);
return;
}
@ -208,7 +220,10 @@ class WebExtensionManager implements WebExtension.ActionDelegate, TabSessionMana
}
public void onClicked(TabSession session) {
actionFor(session).click();
WebExtension.Action action = actionFor(session);
if (action != null) {
action.click();
}
}
public void setActionDelegate(BrowserActionDelegate delegate) {
@ -229,12 +244,37 @@ class WebExtensionManager implements WebExtension.ActionDelegate, TabSessionMana
}
}
public WebExtensionManager(GeckoRuntime runtime) {
public GeckoResult<Void> unregisterExtension(TabSessionManager tabManager) {
if (extension == null) {
return GeckoResult.fromValue(null);
}
tabManager.unregisterWebExtension();
return mRuntime.getWebExtensionController().uninstall(extension).accept((unused) -> {
extension = null;
mDefaultAction = null;
updateAction(null);
});
}
public void registerExtension(WebExtension extension,
TabSessionManager tabManager) {
extension.setActionDelegate(this);
tabManager.setWebExtensionActionDelegate(extension, this);
this.extension = extension;
}
public WebExtensionManager(GeckoRuntime runtime,
TabSessionManager tabManager) {
runtime.getWebExtensionController()
.list().accept(extensions -> {
for (final WebExtension extension : extensions) {
registerExtension(extension, tabManager);
}
});
mRuntime = runtime;
// TODO: allow users to install an extension from file
// extension = new WebExtension("resource://android/assets/chill-out/");
// extension.setActionDelegate(this);
// mRuntime.registerWebExtension(extension);
}
}
@ -365,12 +405,14 @@ public class GeckoViewActivity
@Override
public GeckoResult<AllowOrDeny> onCloseTab(WebExtension source, GeckoSession session) {
TabSession tabSession = mTabSessionManager.getSession(session);
closeTab(tabSession);
if (tabSession != null) {
closeTab(tabSession);
}
return GeckoResult.fromValue(AllowOrDeny.ALLOW);
}
});
sExtensionManager = new WebExtensionManager(sGeckoRuntime);
sExtensionManager = new WebExtensionManager(sGeckoRuntime, mTabSessionManager);
mTabSessionManager.setTabObserver(sExtensionManager);
// `getSystemService` call requires API level 23
@ -483,8 +525,19 @@ public class GeckoViewActivity
ViewGroup.LayoutParams params = mPopupView.getLayoutParams();
boolean shouldShow = force || params.width == 0;
setPopupVisibility(shouldShow);
if (shouldShow) {
return shouldShow ? mPopupSession : null;
}
private void setPopupVisibility(boolean visible) {
if (mPopupView == null) {
return;
}
ViewGroup.LayoutParams params = mPopupView.getLayoutParams();
if (visible) {
params.height = 1100;
params.width = 1200;
} else {
@ -493,7 +546,6 @@ public class GeckoViewActivity
}
mPopupView.setLayoutParams(params);
return shouldShow ? mPopupSession : null;
}
private void openPopupSession() {
@ -589,7 +641,7 @@ public class GeckoViewActivity
}
private void recreateSession(TabSession session) {
if(session != null) {
if (session != null) {
mTabSessionManager.closeSession(session);
}
@ -701,6 +753,9 @@ public class GeckoViewActivity
mUsePrivateBrowsing = !mUsePrivateBrowsing;
recreateSession();
break;
case R.id.install_addon:
installAddon();
break;
case R.id.action_new_tab:
createNewTab();
break;
@ -718,6 +773,37 @@ public class GeckoViewActivity
return true;
}
private void installAddon() {
final AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle(R.string.install_addon);
final EditText input = new EditText(this);
input.setInputType(InputType.TYPE_CLASS_TEXT);
input.setHint(R.string.install_addon_hint);
builder.setView(input);
builder.setPositiveButton(R.string.install, (dialog, which) -> {
final String uri = input.getText().toString();
// We only suopport one extension at a time, so remove the currently installed
// extension if there is one
setPopupVisibility(false);
mPopupView = null;
mPopupSession = null;
sExtensionManager.unregisterExtension(mTabSessionManager).then(unused -> {
final WebExtensionController controller = sGeckoRuntime.getWebExtensionController();
controller.setPromptDelegate(sExtensionManager);
return controller.install(uri);
}).accept(extension ->
sExtensionManager.registerExtension(extension, mTabSessionManager));
});
builder.setNegativeButton(R.string.cancel, (dialog, which) -> {
// Nothing to do
});
builder.show();
}
private void createNewTab() {
TabSession newSession = createSession();
newSession.open(sGeckoRuntime);
@ -726,7 +812,7 @@ public class GeckoViewActivity
}
private void closeTab(TabSession session) {
if(mTabSessionManager.sessionCount() > 1) {
if (mTabSessionManager.sessionCount() > 1) {
mTabSessionManager.closeSession(session);
TabSession tabSession = mTabSessionManager.getCurrentSession();
setGeckoViewSession(tabSession);

View File

@ -4,6 +4,7 @@ import android.support.annotation.Nullable;
import org.mozilla.geckoview.GeckoSession;
import org.mozilla.geckoview.GeckoSessionSettings;
import org.mozilla.geckoview.WebExtension;
import java.util.ArrayList;
@ -19,6 +20,19 @@ public class TabSessionManager {
public TabSessionManager() {
}
public void unregisterWebExtension() {
for (final TabSession session : mTabSessions) {
session.action = null;
}
}
public void setWebExtensionActionDelegate(WebExtension extension,
WebExtension.ActionDelegate delegate) {
for (final TabSession session : mTabSessions) {
session.setWebExtensionActionDelegate(extension, delegate);
}
}
public void setTabObserver(TabObserver observer) {
mTabObserver = observer;
}

View File

@ -10,6 +10,7 @@
<item android:title="@string/private_browsing" android:checkable="true" android:id="@+id/action_pb"/>
<item android:title="@string/remote_debugging" android:checkable="true" android:id="@+id/action_remote_debugging"/>
<item android:title="@string/new_tab" android:id="@+id/action_new_tab"/>
<item android:title="@string/install_addon" android:id="@+id/install_addon"/>
<item android:title="@string/close_tab" android:id="@+id/action_close_tab"/>
<item android:title="@string/forward" android:id="@+id/action_forward"/>
<item android:title="@string/reload" android:id="@+id/action_reload"/>

View File

@ -36,9 +36,14 @@
<string name="device_sharing_camera">Camera is on</string>
<string name="device_sharing_camera_and_mic">Camera and microphone are on</string>
<string name="new_tab">New tab</string>
<string name="install_addon">Install Add-on...</string>
<string name="install_addon_hint">Add-on URL: https://addons.mozilla.org/...</string>
<string name="close_tab">Close tab</string>
<string name="desktop_mode">Desktop site</string>
<string name="slow_script">A script on this page is causing your web browser to run slowly</string>
<string name="wait">Wait</string>
<string name="stop">Stop</string>
<string name="install">Install</string>
<string name="cancel">Cancel</string>
<string name="addon_uri">WebExtension xpi URL</string>
</resources>