Custom tabs, thanks qy!

Stephen Paul Weber created

Change summary

build.gradle                                                   |  1 
src/cheogram/java/com/cheogram/android/BrowserHelper.java      | 88 ++++
src/main/java/eu/siacs/conversations/ui/text/FixedURLSpan.java | 16 
3 files changed, 101 insertions(+), 4 deletions(-)

Detailed changes

build.gradle 🔗

@@ -107,6 +107,7 @@ dependencies {
     implementation 'io.github.nishkarsh:android-permissions:2.1.6'
     implementation 'androidx.recyclerview:recyclerview:1.1.0'
     implementation 'androidx.documentfile:documentfile:1.0.1'
+    implementation 'androidx.browser:browser:1.8.0'
     implementation 'com.github.martin-stone:hsv-alpha-color-picker-android:3.1.0'
     implementation 'com.github.ipld:java-cid:v1.3.1'
     //implementation 'com.splitwise:tokenautocomplete:3.0.2'

src/cheogram/java/com/cheogram/android/BrowserHelper.java 🔗

@@ -0,0 +1,88 @@
+package com.cheogram.android;
+
+import android.content.Intent;
+import android.Manifest;
+import android.annotation.SuppressLint;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.Build;
+import android.content.ActivityNotFoundException;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import androidx.browser.customtabs.CustomTabsIntent;
+
+import eu.siacs.conversations.ui.XmppActivity;
+
+public class BrowserHelper {
+	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 Set<String> extractPackageNames(List<ResolveInfo> infos) {
+		Set<String> names = new HashSet<>();
+		for (ResolveInfo resolveInfo : infos) {
+			String packageName = resolveInfo.activityInfo.packageName;
+			names.add(packageName);
+		}
+		return names;
+	}
+
+	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 void launchUri(Context context, Uri uri) {
+		boolean launched = Build.VERSION.SDK_INT >= 30 ?
+				launchNativeApi30(context, uri) :
+				launchNativeBeforeApi30(context, uri);
+
+		if (!launched) {
+			var builder = new CustomTabsIntent.Builder().setShowTitle(true);
+			if (context instanceof XmppActivity) {
+				builder = builder.setColorScheme(((XmppActivity) context).isDark() ? CustomTabsIntent.COLOR_SCHEME_DARK : CustomTabsIntent.COLOR_SCHEME_LIGHT);
+			}
+			builder.build().launchUrl(context, uri);
+		}
+	}
+}

src/main/java/eu/siacs/conversations/ui/text/FixedURLSpan.java 🔗

@@ -42,6 +42,8 @@ import android.view.SoundEffectConstants;
 import android.view.View;
 import android.widget.Toast;
 
+import com.cheogram.android.BrowserHelper;
+
 import java.util.Arrays;
 
 import eu.siacs.conversations.R;
@@ -95,11 +97,17 @@ public class FixedURLSpan extends URLSpan {
 			}
 		}
 
+		if (uri.getScheme().equals("http") || uri.getScheme().equals("https")) {
+			widget.playSoundEffect(SoundEffectConstants.CLICK);
+			BrowserHelper.launchUri(context, uri);
+			return;
+		}
+
 		final Intent intent = new Intent(Intent.ACTION_VIEW, uri);
-      if ("geo".equalsIgnoreCase(uri.getScheme())) {
-          intent.setClass(context, ShowLocationActivity.class);
-      } else {
-          intent.setFlags(Intent.FLAG_ACTIVITY_NEW_DOCUMENT);
+		if ("geo".equalsIgnoreCase(uri.getScheme())) {
+			intent.setClass(context, ShowLocationActivity.class);
+		} else {
+			intent.setFlags(Intent.FLAG_ACTIVITY_NEW_DOCUMENT);
 		}
 		try {
 			context.startActivity(intent);