Open Soundcloud URL in native Soundcloud app from

2019-06-27 10:29发布

问题:

Scenario

I have a WebView in my Android app which contains a Soundcloud embed (from Embedly). This embed has two buttons: "Play on Soundcloud" and "Listen in browser".

The "Play on Soundcloud" button contains a URL in format intent://tracks:257659076#Intent;scheme=soundcloud;package=com.soundcloud.android;end

Code

My WebView uses a custom WebViewClient (because I need to intercept some URLs for some different stuff).

protected class WebViewClient extends android.webkit.WebViewClient {
    public WebViewClient() { }

    @Override
    public boolean shouldOverrideUrlLoading(WebView view, String url) {
        PackageManager packageManager = context.getPackageManager();

        // Create an Intent from the URL.
        Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));

        // Find out if I have any activities which will handle the URL.
        List<ResolveInfo> resolveInfoList = packageManager.queryIntentActivities(intent, 0);

        // If we have an app installed that can handle the URL, then use it.
        if (resolveInfoList != null && resolveInfoList.size() > 0) {
            Intent viewUrlIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
            context.startActivity(viewUrlIntent);
        }
        else {
            // Do something else.
        }
        return true;
    }
}

Problem

Clicking "Listen in browser" plays the track in the embed itself and works fine. Clicking "Play on Soundcloud" will call into shouldOverrideUrlLoading in the WebViewClient above (as expected). However, my code to find a activity can't find anything that can deal with this Soundcloud URL.

If I don't set my WebViewClient on the WebView (so it just does its own thing), the "Play on Soundcloud" button will work as expected and launch the Soundcloud app.

Temporary (crap) solution

I've managed to make this do what I want it to do by parsing the URL to get the track ID, then building a new URL using a format that Soundcloud definitely accepts (thanks to this SO post). A URL in the format "soundcloud://tracks:[TRACK_ID]" will be accepted by the Soundcloud app.

But WHY?

Either I am doing the whole "find out what activities can handle this URL" thing wrong, or maybe(?!) the default WebViewClient used by the WebView handles this explicitly?! Seems implausible.

回答1:

I'm just extending the Temporary (crap) solution here, so this is far from a perfect answer, but might still help someone who absolutely needs to get this to work, also with private tracks.

The replace method works if the track is public, but with private tracks this does not work, probably because of the missing secret token in the intent URL.

Unfortunately the embed player does not contain all the necessary pieces of the URL I need to generate, except inside the iframe, which I cannot access due to cross-origin policy. So in addition to the iframe code I also need the share link.

What I ended up doing is making sure that the containing HTML page has the share link as a JS variable. I then read that variable using Java and create a new Intent with that URL. This works, because the official app also registers all soundcloud.com URLs.

So for private tracks this goes to the HTML page:

<script>var soundCloudURL = "https://soundcloud.com/my-profile/my-track/my-secret-token";</script>

Then inside your Android app you would have something like this:

@Override
public boolean shouldOverrideUrlLoading (WebView view, String url) {
    if (uri.getScheme().contains("intent")) {
        openSoundCloudPlayer();
        return true;
    }
}

private void openSoundCloudPlayer() {
    appWebView.evaluateJavascript("(function() { return soundCloudUrl })();", new ValueCallback<String>() {
        @Override
        public void onReceiveValue(String soundCloudUrl) {
            // JS null is converted into a string "null", not Java null.
            if (soundCloudUrl != "null") {
                // Take out the quotes from the string
                soundCloudUrl = soundCloudUrl.replace("\"", "");

                Uri newUri = Uri.parse(soundCloudUrl);
                Intent intent = new Intent(Intent.ACTION_VIEW, newUri);
                startActivity(intent);
            }
        }
    });
}