This repository has no description
0

Configure Feed

Select the types of activity you want to include in your feed.

1package us.lassul.assist; 2 3import android.app.Activity; 4import android.content.ComponentName; 5import android.content.Context; 6import android.content.Intent; 7import android.content.pm.PackageManager; 8import android.content.pm.ResolveInfo; 9import android.net.Uri; 10import android.os.Bundle; 11import android.util.Log; 12import androidx.browser.customtabs.CustomTabsCallback; 13import androidx.browser.customtabs.CustomTabsClient; 14import androidx.browser.customtabs.CustomTabsIntent; 15import androidx.browser.customtabs.CustomTabsService; 16import androidx.browser.customtabs.CustomTabsServiceConnection; 17import androidx.browser.customtabs.CustomTabsSession; 18import androidx.browser.trusted.TrustedWebActivityIntentBuilder; 19 20/** 21 * The voice page, opened as a Trusted Web Activity, else as a Custom Tab, and its postMessage 22 * channel: the page reports "started" and "ended" for each call, the app sends "hangup". One per 23 * process, used on the main thread only (androidx delivers the callbacks there). 24 */ 25final class Link { 26 static final String TAG = "Assist"; 27 // The app speaks as this origin; assetlinks.json grants it (use_as_origin). 28 private static final Uri ORIGIN = Uri.parse("https://assist.lassul.us"); 29 private static final Uri URL = Uri.parse("https://assist.lassul.us/?listen"); 30 31 private static Context app; 32 private static CustomTabsServiceConnection connection; 33 private static CustomTabsSession session; 34 private static boolean twa; 35 // Opened before the browser's service connected: launched once it is. 36 private static Activity pending; 37 38 private static final CustomTabsCallback CALLBACK = new CustomTabsCallback() { 39 @Override 40 public void onRelationshipValidationResult( 41 int relation, Uri origin, boolean result, Bundle extras) { 42 Log.i(TAG, "use_as_origin " + origin + ": " + result); 43 } 44 45 // Every page load needs its own channel; the page takes the port from the first message. 46 @Override 47 public void onNavigationEvent(int event, Bundle extras) { 48 if (event == NAVIGATION_FINISHED && session != null) { 49 boolean requested = session.requestPostMessageChannel(ORIGIN, ORIGIN, new Bundle()); 50 Log.i(TAG, "postMessage channel requested: " + requested); 51 } 52 } 53 54 @Override 55 public void onMessageChannelReady(Bundle extras) { 56 Log.i(TAG, "postMessage channel ready: " + session.postMessage("hello", null)); 57 } 58 59 @Override 60 public void onPostMessage(String message, Bundle extras) { 61 Log.i(TAG, "page: " + message); 62 if ("started".equals(message)) { 63 CallService.pageStarted(); 64 } else if ("ended".equals(message)) { 65 CallService.pageEnded(); 66 } 67 } 68 }; 69 70 private Link() {} 71 72 static void open(Activity activity) { 73 if (session != null) { 74 launch(activity); 75 return; 76 } 77 pending = activity; 78 if (connection != null) { 79 return; 80 } 81 String provider = twaProvider(activity); 82 twa = provider != null; 83 if (provider == null) { 84 provider = CustomTabsClient.getPackageName(activity, null); 85 } 86 app = activity.getApplicationContext(); 87 connection = new CustomTabsServiceConnection() { 88 @Override 89 public void onCustomTabsServiceConnected(ComponentName name, CustomTabsClient client) { 90 client.warmup(0); 91 session = client.newSession(CALLBACK); 92 if (session != null) { 93 session.validateRelationship( 94 CustomTabsService.RELATION_USE_AS_ORIGIN, ORIGIN, null); 95 } 96 Activity waiting = pending; 97 pending = null; 98 if (waiting != null && !waiting.isFinishing()) { 99 launch(waiting); 100 } 101 } 102 103 @Override 104 public void onServiceDisconnected(ComponentName name) { 105 session = null; 106 } 107 }; 108 if (provider == null || !CustomTabsClient.bindCustomTabsService(app, provider, connection)) { 109 connection = null; 110 pending = null; 111 launch(activity); 112 } 113 } 114 115 /** Sends "hangup" to the page, if it is connected. */ 116 static void hangUp() { 117 if (session != null) { 118 Log.i(TAG, "hangup sent: " + session.postMessage("hangup", null)); 119 } 120 } 121 122 static void close() { 123 if (connection != null) { 124 app.unbindService(connection); 125 } 126 connection = null; 127 session = null; 128 pending = null; 129 } 130 131 /** In the session's browser; without one (no Custom Tabs browser) a plain Custom Tab. */ 132 private static void launch(Activity activity) { 133 Intent intent; 134 if (session == null) { 135 intent = new CustomTabsIntent.Builder().build().intent.setData(URL); 136 } else if (twa) { 137 intent = new TrustedWebActivityIntentBuilder(URL).build(session).getIntent(); 138 } else { 139 intent = new CustomTabsIntent.Builder(session).build().intent.setData(URL); 140 } 141 activity.startActivity(intent); 142 } 143 144 /** The first browser offering Trusted Web Activities, null if none does. */ 145 private static String twaProvider(Context context) { 146 Intent intent = new Intent(CustomTabsService.ACTION_CUSTOM_TABS_CONNECTION); 147 for (ResolveInfo info : 148 context.getPackageManager() 149 .queryIntentServices(intent, PackageManager.GET_RESOLVED_FILTER)) { 150 if (info.filter != null 151 && info.filter.hasCategory(CustomTabsService.TRUSTED_WEB_ACTIVITY_CATEGORY)) { 152 return info.serviceInfo.packageName; 153 } 154 } 155 return null; 156 } 157}