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}