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): resolve issue with activity result API registration for fragments #4402

Merged
merged 18 commits into from
Apr 12, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
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
60 changes: 58 additions & 2 deletions android/capacitor/src/main/java/com/getcapacitor/Bridge.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,13 @@
import android.webkit.ValueCallback;
import android.webkit.WebSettings;
import android.webkit.WebView;
import androidx.activity.result.ActivityResultCallback;
import androidx.activity.result.ActivityResultLauncher;
import androidx.activity.result.contract.ActivityResultContract;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import androidx.fragment.app.Fragment;
import com.getcapacitor.android.R;
import com.getcapacitor.annotation.CapacitorPlugin;
import com.getcapacitor.annotation.Permission;
Expand Down Expand Up @@ -80,6 +85,8 @@ public class Bridge {

// A reference to the main activity for the app
private final AppCompatActivity context;
// A reference to the containing Fragment if used
private final Fragment fragment;
private WebViewLocalServer localServer;
private String localUrl;
private String appUrl;
Expand Down Expand Up @@ -139,9 +146,23 @@ public Bridge(
PluginManager pluginManager,
CordovaPreferences preferences,
CapConfig config
) {
this(context, null, webView, initialPlugins, cordovaInterface, pluginManager, preferences, config);
}

private Bridge(
AppCompatActivity context,
Fragment fragment,
WebView webView,
List<Class<? extends Plugin>> initialPlugins,
MockCordovaInterfaceImpl cordovaInterface,
PluginManager pluginManager,
CordovaPreferences preferences,
CapConfig config
) {
this.app = new App();
this.context = context;
this.fragment = fragment;
this.webView = webView;
this.webViewClient = new BridgeWebViewClient(this);
this.initialPlugins = initialPlugins;
Expand Down Expand Up @@ -330,6 +351,16 @@ public AppCompatActivity getActivity() {
return this.context;
}

/**
* Get the fragment for the app, if applicable. This will likely be null unless Capacitor
* is being used embedded in a Native Android app.
*
* @return The fragment containing the Capacitor WebView.
*/
public Fragment getFragment() {
return this.fragment;
}

/**
* Get the core WebView under Capacitor's control
* @return
Expand Down Expand Up @@ -699,6 +730,25 @@ protected void savePermissionCall(PluginCall call) {
}
}

/**
* Register an Activity Result Launcher to the containing Fragment or Activity.
*
* @param contract A contract specifying that an activity can be called with an input of
* type I and produce an output of type O.
* @param callback The callback run on Activity Result.
* @return A registered Activity Result Launcher.
*/
public <I, O> ActivityResultLauncher<I> registerForActivityResult(
@NonNull final ActivityResultContract<I, O> contract,
@NonNull final ActivityResultCallback<O> callback
) {
if (fragment != null) {
return fragment.registerForActivityResult(contract, callback);
} else {
return context.registerForActivityResult(contract, callback);
}
}

/**
* Build the JSInjector that will be used to inject JS into files served to the app,
* to ensure that Capacitor's JS and the JS for all the plugins is loaded each time.
Expand Down Expand Up @@ -1140,12 +1190,18 @@ static class Builder {
private CapConfig config = null;
private List<Class<? extends Plugin>> plugins = new ArrayList<>();
private AppCompatActivity activity;
private Fragment fragment;
private final List<WebViewListener> webViewListeners = new ArrayList<>();

Builder(AppCompatActivity activity) {
this.activity = activity;
}

Builder(Fragment fragment) {
this.activity = (AppCompatActivity) fragment.getActivity();
this.fragment = fragment;
}

public Builder setInstanceState(Bundle instanceState) {
this.instanceState = instanceState;
return this;
Expand Down Expand Up @@ -1200,14 +1256,14 @@ public Bridge create() {
cordovaInterface.restoreInstanceState(instanceState);
}

WebView webView = activity.findViewById(R.id.webview);
WebView webView = this.fragment != null ? fragment.getView().findViewById(R.id.webview) : activity.findViewById(R.id.webview);
MockCordovaWebViewImpl mockWebView = new MockCordovaWebViewImpl(activity.getApplicationContext());
mockWebView.init(cordovaInterface, pluginEntries, preferences, webView);
PluginManager pluginManager = mockWebView.getPluginManager();
cordovaInterface.onCordovaInit(pluginManager);

// Bridge initialization
Bridge bridge = new Bridge(activity, webView, plugins, cordovaInterface, pluginManager, preferences, config);
Bridge bridge = new Bridge(activity, fragment, webView, plugins, cordovaInterface, pluginManager, preferences, config);
bridge.setCordovaWebView(mockWebView);
bridge.setWebViewListeners(webViewListeners);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import androidx.appcompat.app.AppCompatActivity;
import androidx.fragment.app.Fragment;
import com.getcapacitor.android.R;
import java.util.ArrayList;
Expand Down Expand Up @@ -57,6 +56,10 @@ public void setConfig(CapConfig config) {
this.config = config;
}

public Bridge getBridge() {
return bridge;
}

public void addWebViewListener(WebViewListener webViewListener) {
webViewListeners.add(webViewListener);
}
Expand All @@ -65,7 +68,7 @@ public void addWebViewListener(WebViewListener webViewListener) {
* Load the WebView and create the Bridge
*/
protected void load(Bundle savedInstanceState) {
Logger.debug("Starting BridgeActivity");
Logger.debug("Loading Bridge with BridgeFragment");

Bundle args = getArguments();
String startDir = null;
Expand All @@ -75,7 +78,7 @@ protected void load(Bundle savedInstanceState) {
}

bridge =
new Bridge.Builder((AppCompatActivity) getActivity())
new Bridge.Builder(this)
.setInstanceState(savedInstanceState)
.setPlugins(initialPlugins)
.setConfig(config)
Expand Down Expand Up @@ -116,8 +119,8 @@ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle sa
}

@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
this.load(savedInstanceState);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import android.webkit.WebView;
import android.widget.EditText;
import androidx.activity.result.ActivityResult;
import androidx.activity.result.ActivityResultCallback;
import androidx.activity.result.ActivityResultLauncher;
import androidx.activity.result.contract.ActivityResultContracts;
import androidx.core.content.FileProvider;
Expand Down Expand Up @@ -53,30 +54,23 @@ private interface ActivityResultListener {

public BridgeWebChromeClient(Bridge bridge) {
this.bridge = bridge;
permissionLauncher =
bridge
.getActivity()
.registerForActivityResult(
new ActivityResultContracts.RequestMultiplePermissions(),
(Map<String, Boolean> isGranted) -> {
if (permissionListener != null) {
boolean granted = true;
for (Map.Entry<String, Boolean> permission : isGranted.entrySet()) {
if (!permission.getValue()) granted = false;
}
permissionListener.onPermissionSelect(granted);
}
}
);

ActivityResultCallback<Map<String, Boolean>> permissionCallback = (Map<String, Boolean> isGranted) -> {
if (permissionListener != null) {
boolean granted = true;
for (Map.Entry<String, Boolean> permission : isGranted.entrySet()) {
if (!permission.getValue()) granted = false;
}
permissionListener.onPermissionSelect(granted);
}
};

permissionLauncher = bridge.registerForActivityResult(new ActivityResultContracts.RequestMultiplePermissions(), permissionCallback);
activityLauncher =
bridge
.getActivity()
.registerForActivityResult(
new ActivityResultContracts.StartActivityForResult(),
result -> {
activityListener.onActivityResult(result);
}
);
bridge.registerForActivityResult(
new ActivityResultContracts.StartActivityForResult(),
result -> activityListener.onActivityResult(result)
);
}

/**
Expand Down
26 changes: 10 additions & 16 deletions android/capacitor/src/main/java/com/getcapacitor/Plugin.java
Original file line number Diff line number Diff line change
Expand Up @@ -111,26 +111,20 @@ void initializeActivityLaunchers() {
for (final Method method : pluginClassMethods) {
if (method.isAnnotationPresent(ActivityCallback.class)) {
// register callbacks annotated with ActivityCallback for activity results
activityLaunchers.put(
method.getName(),
bridge
.getActivity()
.registerForActivityResult(
new ActivityResultContracts.StartActivityForResult(),
result -> triggerActivityCallback(method, result)
)
ActivityResultLauncher<Intent> launcher = bridge.registerForActivityResult(
new ActivityResultContracts.StartActivityForResult(),
result -> triggerActivityCallback(method, result)
);

activityLaunchers.put(method.getName(), launcher);
} else if (method.isAnnotationPresent(PermissionCallback.class)) {
// register callbacks annotated with PermissionCallback for permission results
permissionLaunchers.put(
method.getName(),
bridge
.getActivity()
.registerForActivityResult(
new ActivityResultContracts.RequestMultiplePermissions(),
permissions -> triggerPermissionCallback(method, permissions)
)
ActivityResultLauncher<String[]> launcher = bridge.registerForActivityResult(
new ActivityResultContracts.RequestMultiplePermissions(),
permissions -> triggerPermissionCallback(method, permissions)
);

permissionLaunchers.put(method.getName(), launcher);
}
}
}
Expand Down