Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix android.view.WindowManager$BadTokenException crashes #1

Merged
merged 2 commits into from
Sep 26, 2019
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
69 changes: 26 additions & 43 deletions src/android/RemoteInjectionPlugin.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

import android.app.Activity;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.content.res.AssetManager;
import android.util.Base64;

Expand All @@ -17,6 +16,7 @@
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.lang.ref.WeakReference;
import java.net.MalformedURLException;
import java.util.ArrayList;
import java.util.List;
Expand All @@ -31,16 +31,16 @@ public class RemoteInjectionPlugin extends CordovaPlugin {

// List of files to inject before injecting Cordova.
private final ArrayList<String> preInjectionFileNames = new ArrayList<String>();
private int promptInterval; // Delay before prompting user to retry in seconds

private RequestLifecycle lifecycle;

protected void pluginInitialize() {
String pref = webView.getPreferences().getString("CRIInjectFirstFiles", "");
for (String path: pref.split(",")) {
for (String path : pref.split(",")) {
preInjectionFileNames.add(path.trim());
}
promptInterval = webView.getPreferences().getInteger("CRIPageLoadPromptInterval", 10);
// Delay before prompting user to retry in seconds
int promptInterval = webView.getPreferences().getInteger("CRIPageLoadPromptInterval", 10);

final Activity activity = super.cordova.getActivity();
final CordovaWebViewEngine engine = super.webView.getEngine();
Expand Down Expand Up @@ -114,7 +114,7 @@ private boolean isRemote(String url) {

private void injectCordova() {
List<String> jsPaths = new ArrayList<String>();
for (String path: preInjectionFileNames) {
for (String path : preInjectionFileNames) {
jsPaths.add(path);
}

Expand All @@ -134,7 +134,7 @@ private void injectCordova() {
// (https://developer.mozilla.org/en-US/docs/Web/HTTP/data_URIs). The script tag
// is appended to the DOM and executed via a javascript URL (e.g. javascript:doJsStuff()).
StringBuilder jsToInject = new StringBuilder();
for (String path: jsPaths) {
for (String path : jsPaths) {
jsToInject.append(readFile(cordova.getActivity().getResources().getAssets(), path));
}
String jsUrl = "javascript:var script = document.createElement('script');";
Expand Down Expand Up @@ -178,14 +178,14 @@ private String readFile(AssetManager assets, String filePath) {
* Searches the provided path for javascript files recursively.
*
* @param assets
* @param path start path
* @param path start path
* @return found JS files
*/
private List<String> jsPathsToInject(AssetManager assets, String path){
private List<String> jsPathsToInject(AssetManager assets, String path) {
List jsPaths = new ArrayList<String>();

try {
for (String filePath: assets.list(path)) {
for (String filePath : assets.list(path)) {
String fullPath = path + File.separator + filePath;

if (fullPath.endsWith(".js")) {
Expand All @@ -205,13 +205,13 @@ private List<String> jsPathsToInject(AssetManager assets, String path){
}

private static class RequestLifecycle {
private final Activity activity;
private final WeakReference<Activity> activityRef;
private final CordovaWebViewEngine engine;
private UserPromptTask task;
private final int promptInterval;

RequestLifecycle(Activity activity, CordovaWebViewEngine engine, int promptInterval) {
this.activity = activity;
this.activityRef = new WeakReference<>(activity);
this.engine = engine;
this.promptInterval = promptInterval;
}
Expand Down Expand Up @@ -240,8 +240,8 @@ private synchronized void startTask(final String url) {
task.cancel();
}

if (promptInterval > 0 ) {
task = new UserPromptTask(this, activity, engine, url);
if (promptInterval > 0 && activityRef.get() != null && !activityRef.get().isFinishing()) {
task = new UserPromptTask(this, activityRef.get(), engine, url);
new Timer().schedule(task, promptInterval * 1000);
}
}
Expand All @@ -252,15 +252,15 @@ private synchronized void startTask(final String url) {
*/
static class UserPromptTask extends TimerTask {
private final RequestLifecycle lifecycle;
private final Activity activity;
private final WeakReference<Activity> activityRef;
private final CordovaWebViewEngine engine;
final String url;

AlertDialog alertDialog;

UserPromptTask(RequestLifecycle lifecycle, Activity activity, CordovaWebViewEngine engine, String url) {
this.lifecycle = lifecycle;
this.activity = activity;
this.activityRef = new WeakReference<>(activity);
this.engine = engine;
this.url = url;
}
Expand All @@ -282,35 +282,18 @@ private void cleanup() {

@Override
public void run() {
if (lifecycle.isLoading()) {
if (lifecycle.isLoading() && activityRef.get() != null && !activityRef.get().isFinishing()) {
// Prompts the user giving them the choice to wait on the current request or retry.
lifecycle.activity.runOnUiThread(new Runnable() {
@Override
public void run() {
AlertDialog.Builder builder = new AlertDialog.Builder(activity);
builder.setMessage("The server is taking longer than expected to respond.")
.setOnDismissListener(new DialogInterface.OnDismissListener() {
@Override
public void onDismiss(DialogInterface dialog) {
UserPromptTask.this.cleanup();
}
})
.setPositiveButton("Retry", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int id) {
// Obviously only works for GETs but good enough.
engine.loadUrl(engine.getUrl(), false);
}
})
.setNegativeButton("Wait", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int id) {
lifecycle.startTask(url);
}
});
AlertDialog dialog = UserPromptTask.this.alertDialog = builder.create();
dialog.show();
}
activityRef.get().runOnUiThread(() -> {
AlertDialog.Builder builder = new AlertDialog.Builder(activityRef.get());
builder.setMessage("The server is taking longer than expected to respond.")
.setPositiveButton("Retry", (dialog, id) -> {
// Obviously only works for GETs but good enough.
engine.loadUrl(engine.getUrl(), false);
})
.setNegativeButton("Wait", (dialog, id) -> lifecycle.startTask(url));
alertDialog = builder.create();
alertDialog.show();
});
} else {
lifecycle.stopTask();
Expand Down