AyogoHealth / cordova-plugin-oauth

Cordova plugin for performing OAuth login flows.

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Support for androidx

B33rman opened this issue · comments

Hi there,
Thanks for your work.
I am trying to use the plugin but I am getting following error:

OAuthPlugin.java:26: error: cannot find symbol
import android.support.customtabs.CustomTabsIntent;

Am using volt build (=phonegap build) so I am not compiling anything in my project.

I need to have AndroidXEnabled, is there a chance to upload a new version of the plugin supporting it?

import androidx.browser.customtabs.CustomTabsIntent;

thanks,
kind regards

I used the modified class below. Works for me:

package com.ayogo.cordova.oauth;

import android.app.Activity;
import android.content.ActivityNotFoundException;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.net.Uri;
//import android.support.customtabs.CustomTabsIntent;
import android.os.Build;
import android.text.TextUtils;

import androidx.browser.customtabs.CustomTabsIntent;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import org.apache.cordova.CallbackContext;
import org.apache.cordova.CordovaInterface;
import org.apache.cordova.CordovaPlugin;
import org.apache.cordova.CordovaWebView;
import org.apache.cordova.LOG;
import org.apache.cordova.PluginResult;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

public class OAuthPlugin extends CordovaPlugin {
private final String TAG = "OAuthPlugin";

// Taken from Google's CustomTabsHelper
// https://github.com/GoogleChrome/custom-tabs-client/blob/da65829d7d4b80c00809c6c4aa7f61f88fc7ca26/shared/src/main/java/org/chromium/customtabsclient/shared/CustomTabsHelper.java
static final String STABLE_PACKAGE = "com.android.chrome";
static final String BETA_PACKAGE = "com.chrome.beta";
static final String DEV_PACKAGE = "com.chrome.dev";
static final String LOCAL_PACKAGE = "com.google.android.apps.chrome";
private static final String EXTRA_CUSTOM_TABS_KEEP_ALIVE = "android.support.customtabs.extra.KEEP_ALIVE";
private static final String ACTION_CUSTOM_TABS_CONNECTION = "android.support.customtabs.action.CustomTabsService";


/**
 * The name of the package to use for the custom tab service.
 */
private String tabProvider = null;


/**
 * Executes the request.
 *
 * This method is called from the WebView thread. To do a non-trivial amount of work, use:
 *     cordova.getThreadPool().execute(runnable);
 *
 * To run on the UI thread, use:
 *     cordova.getActivity().runOnUiThread(runnable);
 *
 * @param action          The action to execute.
 * @param rawArgs         The exec() arguments in JSON form.
 * @param callbackContext The callback context used when calling back into JavaScript.
 * @return                Whether the action was valid.
 */
@Override
public boolean execute(String action, JSONArray args, CallbackContext callbackContext) {
    if ("startOAuth".equals(action)) {
        try {
            String authEndpoint = args.getString(0);

            this.startOAuth(authEndpoint);

            callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.OK));
            return true;
        } catch (JSONException e) {
            callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.ERROR));
            return false;
        }
    }

    callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.INVALID_ACTION));
    return false;
}



/**
 * Called when the activity receives a new intent.
 *
 * We use this method to intercept the OAuth callback URI and dispatch a
 * message to the WebView.
 */
@Override
public void onNewIntent(Intent intent) {
    if (intent == null || !intent.getAction().equals(Intent.ACTION_VIEW)) {
        return;
    }

    final Uri uri = intent.getData();

    if (uri.getHost().equals("oauth_callback")) {
        LOG.i(TAG, "OAuth called back with parameters.");

        try {
            JSONObject jsobj = new JSONObject();

            for (String queryKey : uri.getQueryParameterNames()) {
                jsobj.put(queryKey, uri.getQueryParameter(queryKey));
            }

            final String msg = jsobj.toString();
            this.webView.getEngine().evaluateJavascript("window.dispatchEvent(new MessageEvent('message', { data: 'oauth::" + msg + "' }));", null);
        } catch (JSONException e) {
            LOG.e(TAG, "JSON Serialization failed");
            e.printStackTrace();
        }
    }
}


/**
 * Launches the custom tab with the OAuth endpoint URL.
 *
 * @param url The URL of the OAuth endpoint.
 */
private void startOAuth(String url) {
    /*
	String customTabsBrowser = findCustomTabProvider();

    CustomTabsIntent.Builder builder = new CustomTabsIntent.Builder();
    CustomTabsIntent customTabsIntent = builder.build();

    String packageName = this.findCustomTabProvider();
    if (packageName != null) {
       customTabsIntent.intent.setPackage(packageName);
    }

    customTabsIntent.launchUrl(this.cordova.getActivity(), Uri.parse(url));
	*/

	launchUri(this.cordova.getActivity(), Uri.parse(url));
}


static void launchUri(Context context, Uri uri) {
	boolean launched = Build.VERSION.SDK_INT >= 30 ?
			launchNativeApi30(context, uri) :
			launchNativeBeforeApi30(context, uri);

	if (!launched) {
		new CustomTabsIntent.Builder()
				.build()
				.launchUrl(context, uri);
	}
}


static boolean launchNativeApi30(Context context, Uri uri) {
	Intent nativeAppIntent = new Intent(Intent.ACTION_VIEW, uri)
			.addCategory(Intent.CATEGORY_BROWSABLE)
			.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK |
					Intent.FLAG_ACTIVITY_REQUIRE_NON_BROWSER);
	try {
		context.startActivity(nativeAppIntent);
		return true;
	} catch (ActivityNotFoundException ex) {
		return false;
	}
}


private static boolean launchNativeBeforeApi30(Context context, Uri uri) {
	PackageManager pm = context.getPackageManager();

	// Get all Apps that resolve a generic url
	Intent browserActivityIntent = new Intent()
			.setAction(Intent.ACTION_VIEW)
			.addCategory(Intent.CATEGORY_BROWSABLE)
			.setData(Uri.fromParts("http", "", null));
	Set<String> genericResolvedList = extractPackageNames(
			pm.queryIntentActivities(browserActivityIntent, 0));

	// Get all apps that resolve the specific Url
	Intent specializedActivityIntent = new Intent(Intent.ACTION_VIEW, uri)
			.addCategory(Intent.CATEGORY_BROWSABLE);
	Set<String> resolvedSpecializedList = extractPackageNames(
			pm.queryIntentActivities(specializedActivityIntent, 0));

	// Keep only the Urls that resolve the specific, but not the generic
	// urls.
	resolvedSpecializedList.removeAll(genericResolvedList);

	// If the list is empty, no native app handlers were found.
	if (resolvedSpecializedList.isEmpty()) {
		return false;
	}

	// We found native handlers. Launch the Intent.
	specializedActivityIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
	context.startActivity(specializedActivityIntent);
	return true;
}

public static Set extractPackageNames(List resolveInfos) {
Set packageNameSet = new HashSet<>();
for (ResolveInfo ri: resolveInfos) {
packageNameSet.add(ri.activityInfo.packageName);
}
return packageNameSet;
}

/**
 * Goes through all apps that handle VIEW intents and have a warmup service.
 *
 * Picks the one chosen by the user if there is one, otherwise makes a best
 * effort to return a valid package name.
 *
 * This is <strong>not</strong> threadsafe.
 *
 * @return The package name recommended to use for connecting to custom
 * tabs related components.
 */
private String findCustomTabProvider() {
    if (this.tabProvider != null) {
        return this.tabProvider;
    }

    PackageManager pm = this.cordova.getActivity().getPackageManager();

    // Get default VIEW intent handler.
    Intent activityIntent = new Intent(Intent.ACTION_VIEW, Uri.parse("http://www.example.com"));
    ResolveInfo defaultViewHandlerInfo = pm.resolveActivity(activityIntent, 0);
    String defaultViewHandlerPackageName = null;

    if (defaultViewHandlerInfo != null) {
        defaultViewHandlerPackageName = defaultViewHandlerInfo.activityInfo.packageName;
    }


    // Get all apps that can handle VIEW intents.
    List<ResolveInfo> resolvedActivityList = pm.queryIntentActivities(activityIntent, PackageManager.MATCH_ALL);
    List<String> packagesSupportingCustomTabs = new ArrayList<>();

    for (ResolveInfo info : resolvedActivityList) {
        Intent serviceIntent = new Intent();
        serviceIntent.setAction(ACTION_CUSTOM_TABS_CONNECTION);
        serviceIntent.setPackage(info.activityInfo.packageName);

        if (pm.resolveService(serviceIntent, 0) != null) {
            packagesSupportingCustomTabs.add(info.activityInfo.packageName);
        }
    }

    // Now packagesSupportingCustomTabs contains all apps that can handle both VIEW intents
    // and service calls.
    if (packagesSupportingCustomTabs.isEmpty()) {
        this.tabProvider = null;
    } else if (packagesSupportingCustomTabs.size() == 1) {
        this.tabProvider = packagesSupportingCustomTabs.get(0);
    } else if (!TextUtils.isEmpty(defaultViewHandlerPackageName) && !this.hasSpecializedHandlerIntents(activityIntent) && packagesSupportingCustomTabs.contains(defaultViewHandlerPackageName)) {
        this.tabProvider = defaultViewHandlerPackageName;
    } else if (packagesSupportingCustomTabs.contains(STABLE_PACKAGE)) {
        this.tabProvider = STABLE_PACKAGE;
    } else if (packagesSupportingCustomTabs.contains(BETA_PACKAGE)) {
        this.tabProvider = BETA_PACKAGE;
    } else if (packagesSupportingCustomTabs.contains(DEV_PACKAGE)) {
        this.tabProvider = DEV_PACKAGE;
    } else if (packagesSupportingCustomTabs.contains(LOCAL_PACKAGE)) {
        this.tabProvider = LOCAL_PACKAGE;
    }

    return this.tabProvider;
}


/**
 * Used to check whether there is a specialized handler for a given intent.
 *
 * @param intent The intent to check with.
 * @return Whether there is a specialized handler for the given intent.
 */
private boolean hasSpecializedHandlerIntents(Intent intent) {
    try {
        PackageManager pm = this.cordova.getActivity().getPackageManager();
        List<ResolveInfo> handlers = pm.queryIntentActivities(intent, PackageManager.GET_RESOLVED_FILTER);

        if (handlers == null || handlers.size() == 0) {
            return false;
        }

        for (ResolveInfo resolveInfo : handlers) {
            IntentFilter filter = resolveInfo.filter;

            if (filter == null || filter.countDataAuthorities() == 0 || filter.countDataPaths() == 0 || resolveInfo.activityInfo == null) {
                continue;
            }

            return true;
        }
    } catch (RuntimeException e) {
        LOG.e(TAG, "Runtime exception while getting specialized handlers");
    }

    return false;
}

}

@B33rman @alexesto do you guys have a fork of this with the problem solved?
I see that the androidx adapter is not fixing this... :-/

AndroidX support is covered in #25

I've published (with the next tag for now) version 4.0.0 to npm with AndroidX support.

If I don't hear of any issues in the next week, I'll mark 4.0.0 as latest on npm :)