77import android .content .Intent ;
88import android .net .Uri ;
99import android .os .Bundle ;
10+ import androidx .activity .result .ActivityResultLauncher ;
1011import androidx .annotation .NonNull ;
1112import androidx .annotation .Nullable ;
1213import androidx .browser .customtabs .*;
@@ -43,7 +44,8 @@ interface BrowserEventListener {
4344 private CustomTabsClient customTabsClient ;
4445 private CustomTabsSession browserSession ;
4546 private boolean isInitialLoad = false ;
46- private EventGroup group ;
47+ @ Nullable
48+ private ActivityResultLauncher <Intent > customTabLauncher ;
4749 private CustomTabsServiceConnection connection = new CustomTabsServiceConnection () {
4850 @ Override
4951 public void onCustomTabsServiceConnected (ComponentName name , CustomTabsClient client ) {
@@ -61,7 +63,6 @@ public void onServiceDisconnected(ComponentName name) {}
6163 */
6264 public Browser (@ NonNull Context context ) {
6365 this .context = context ;
64- this .group = new EventGroup (this ::handleGroupCompletion );
6566 }
6667
6768 /**
@@ -81,6 +82,16 @@ public BrowserEventListener getBrowserEventListenerListener() {
8182 return browserEventListener ;
8283 }
8384
85+ /**
86+ * Provide the ActivityResultLauncher used to open the Custom Tab. When
87+ * set, the Custom Tab is launched via this launcher so that
88+ * {@link #notifyBrowserFinished()} can be triggered from the launcher's
89+ * result callback (only when the tab activity actually terminates).
90+ */
91+ public void setCustomTabLauncher (@ Nullable ActivityResultLauncher <Intent > launcher ) {
92+ this .customTabLauncher = launcher ;
93+ }
94+
8495 /**
8596 * Open the browser to the specified URL.
8697 * @param url
@@ -108,8 +119,12 @@ public void open(Uri url, @Nullable Integer toolbarColor) {
108119 tabsIntent .intent .putExtra (Intent .EXTRA_REFERRER , Uri .parse (Intent .URI_ANDROID_APP_SCHEME + "//" + context .getPackageName ()));
109120
110121 isInitialLoad = true ;
111- group .reset ();
112- tabsIntent .launchUrl (context , url );
122+ if (customTabLauncher != null ) {
123+ tabsIntent .intent .setData (url );
124+ customTabLauncher .launch (tabsIntent .intent );
125+ } else {
126+ tabsIntent .launchUrl (context , url );
127+ }
113128 }
114129
115130 /**
@@ -120,17 +135,14 @@ public boolean bindService() {
120135 if (null == customTabPackageName ) {
121136 customTabPackageName = FALLBACK_CUSTOM_TAB_PACKAGE_NAME ;
122137 }
123- boolean result = CustomTabsClient .bindCustomTabsService (context , customTabPackageName , connection );
124- group .leave ();
125- return result ;
138+ return CustomTabsClient .bindCustomTabsService (context , customTabPackageName , connection );
126139 }
127140
128141 /**
129142 * Unbind the custom tabs service, required to be called in the `onPause` lifecycle event.
130143 */
131144 public void unbindService () {
132145 context .unbindService (connection );
133- group .enter ();
134146 }
135147
136148 private void handledNavigationEvent (int navigationEvent ) {
@@ -143,19 +155,13 @@ private void handledNavigationEvent(int navigationEvent) {
143155 isInitialLoad = false ;
144156 }
145157 break ;
146- case CustomTabsCallback .TAB_HIDDEN :
147- group .leave ();
148- break ;
149- case CustomTabsCallback .TAB_SHOWN :
150- group .enter ();
151- break ;
152158 }
153159 }
154160
155- private void handleGroupCompletion () {
156- // events such as TAB_HIDDEN and onPause can occur for multiple reasons and in
157- // different sequences so there is no single point to fire this. so we rely on the
158- // event group to track when it is safe to assume that the browser is done .
161+ public void notifyBrowserFinished () {
162+ // Notify listeners that the browser session has finished. Called by the
163+ // host activity when the Custom Tab activity actually returns a result
164+ // (i.e. the tab was truly dismissed, not just backgrounded or minimised) .
159165 if (browserEventListener != null ) {
160166 browserEventListener .onBrowserEvent (BROWSER_FINISHED );
161167 }
0 commit comments