BrowserHelper.java

 1package com.cheogram.android;
 2
 3import android.content.Intent;
 4import android.Manifest;
 5import android.annotation.SuppressLint;
 6import android.content.Context;
 7import android.content.pm.PackageManager;
 8import android.content.pm.ResolveInfo;
 9import android.database.Cursor;
10import android.net.Uri;
11import android.os.Build;
12import android.content.ActivityNotFoundException;
13import java.util.HashSet;
14import java.util.List;
15import java.util.Set;
16import androidx.browser.customtabs.CustomTabsIntent;
17
18import eu.siacs.conversations.R;
19import eu.siacs.conversations.persistance.FileBackend;
20import eu.siacs.conversations.ui.XmppActivity;
21
22public class BrowserHelper {
23	static boolean launchNativeApi30(Context context, Uri uri) {
24		Intent nativeAppIntent = new Intent(Intent.ACTION_VIEW, uri)
25				.addCategory(Intent.CATEGORY_BROWSABLE)
26				.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK |
27						Intent.FLAG_ACTIVITY_REQUIRE_NON_BROWSER);
28		try {
29			context.startActivity(nativeAppIntent);
30			return true;
31		} catch (ActivityNotFoundException ex) {
32			return false;
33		}
34	}
35
36	private static Set<String> extractPackageNames(List<ResolveInfo> infos) {
37		Set<String> names = new HashSet<>();
38		for (ResolveInfo resolveInfo : infos) {
39			String packageName = resolveInfo.activityInfo.packageName;
40			names.add(packageName);
41		}
42		return names;
43	}
44
45	private static boolean launchNativeBeforeApi30(Context context, Uri uri) {
46		PackageManager pm = context.getPackageManager();
47
48		// Get all Apps that resolve a generic url
49		Intent browserActivityIntent = new Intent()
50				.setAction(Intent.ACTION_VIEW)
51				.addCategory(Intent.CATEGORY_BROWSABLE)
52				.setData(Uri.fromParts("http", "", null));
53		Set<String> genericResolvedList = extractPackageNames(
54				pm.queryIntentActivities(browserActivityIntent, 0));
55
56		// Get all apps that resolve the specific Url
57		Intent specializedActivityIntent = new Intent(Intent.ACTION_VIEW, uri)
58				.addCategory(Intent.CATEGORY_BROWSABLE);
59		Set<String> resolvedSpecializedList = extractPackageNames(
60				pm.queryIntentActivities(specializedActivityIntent, 0));
61
62		// Keep only the Urls that resolve the specific, but not the generic
63		// urls.
64		resolvedSpecializedList.removeAll(genericResolvedList);
65
66		// If the list is empty, no native app handlers were found.
67		if (resolvedSpecializedList.isEmpty()) {
68			return false;
69		}
70
71		// We found native handlers. Launch the Intent.
72		specializedActivityIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
73		context.startActivity(specializedActivityIntent);
74		return true;
75	}
76
77	public static void launchUri(Context context, Uri uri) {
78		boolean launched = Build.VERSION.SDK_INT >= 30 ?
79				launchNativeApi30(context, uri) :
80				launchNativeBeforeApi30(context, uri);
81
82		if (!launched) {
83			var builder = new CustomTabsIntent.Builder()
84				.setShowTitle(true)
85				.setShareState(CustomTabsIntent.SHARE_STATE_ON)
86				.setBackgroundInteractionEnabled(true)
87				.setStartAnimations(context, R.anim.slide_in_right, R.anim.slide_out_left)
88				.setExitAnimations(context, android.R.anim.slide_in_left, android.R.anim.slide_out_right)
89				.setCloseButtonIcon(FileBackend.drawDrawable(context.getDrawable(R.drawable.ic_arrow_back_24dp)))
90				.setCloseButtonPosition(CustomTabsIntent.CLOSE_BUTTON_POSITION_START);
91			if (context instanceof XmppActivity) {
92				builder = builder.setColorScheme(((XmppActivity) context).isDark() ? CustomTabsIntent.COLOR_SCHEME_DARK : CustomTabsIntent.COLOR_SCHEME_LIGHT);
93			}
94			builder.build().launchUrl(context, uri);
95		}
96	}
97}