From bedc9f226f6a8a0cb9f4ac4ff01dc6addff0dd04 Mon Sep 17 00:00:00 2001 From: Aaron Labiaga Date: Tue, 5 May 2026 13:03:35 -0600 Subject: [PATCH 1/5] add snippets for jetpack pip and notifications Change-Id: Ia5887b6f73f0af4729544e1a30c82f915a58c054 --- compose/snippets/build.gradle.kts | 2 + .../notifications/NotificationsSnippets.kt | 39 ++++++++++++++++ .../NavOrVideoCallJpipActivity.kt | 37 ++++++++++++++++ .../PictureInPictureSnippets.kt | 3 ++ .../VideoPlaybackJpipActivity.kt | 44 +++++++++++++++++++ gradle/libs.versions.toml | 4 +- 6 files changed, 128 insertions(+), 1 deletion(-) create mode 100644 compose/snippets/src/main/java/com/example/compose/snippets/notifications/NotificationsSnippets.kt create mode 100644 compose/snippets/src/main/java/com/example/compose/snippets/pictureinpicture/NavOrVideoCallJpipActivity.kt create mode 100644 compose/snippets/src/main/java/com/example/compose/snippets/pictureinpicture/VideoPlaybackJpipActivity.kt diff --git a/compose/snippets/build.gradle.kts b/compose/snippets/build.gradle.kts index f151d4cd9..f4a142c07 100644 --- a/compose/snippets/build.gradle.kts +++ b/compose/snippets/build.gradle.kts @@ -128,6 +128,8 @@ dependencies { implementation(libs.androidx.activity.compose) implementation(libs.androidx.appcompat) implementation(libs.androidx.core.ktx) + implementation(libs.androidx.core.pip) + implementation(libs.androidx.core.telecom) implementation(libs.androidx.lifecycle.runtime) implementation(libs.androidx.lifecycle.viewModelCompose) diff --git a/compose/snippets/src/main/java/com/example/compose/snippets/notifications/NotificationsSnippets.kt b/compose/snippets/src/main/java/com/example/compose/snippets/notifications/NotificationsSnippets.kt new file mode 100644 index 000000000..b0b5cfcf6 --- /dev/null +++ b/compose/snippets/src/main/java/com/example/compose/snippets/notifications/NotificationsSnippets.kt @@ -0,0 +1,39 @@ +/* + * Copyright 2026 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.example.compose.snippets.notifications + +import android.content.Context; +import android.content.Intent; +import android.app.PendingIntent; +import androidx.compose.runtime.Composable +import androidx.core.app.NotificationCompat; +import androidx.core.app.PendingIntentCompat + +@Composable +fun NotificationSnippets(context: Context) { + // [START android_notification_authenticated_action] + val intent = Intent(context, null) + val moreSecureNotification = NotificationCompat.Action.Builder( + 0, "Reply", + PendingIntentCompat.getActivity(context, 0, intent, PendingIntent.FLAG_NO_CREATE, false) + ) + // This notification always requests authentication when invoked + // from a lock screen. + .setAuthenticationRequired(true) + .build() + // [END android_notification_authenticated_action] +} \ No newline at end of file diff --git a/compose/snippets/src/main/java/com/example/compose/snippets/pictureinpicture/NavOrVideoCallJpipActivity.kt b/compose/snippets/src/main/java/com/example/compose/snippets/pictureinpicture/NavOrVideoCallJpipActivity.kt new file mode 100644 index 000000000..0cb7491d4 --- /dev/null +++ b/compose/snippets/src/main/java/com/example/compose/snippets/pictureinpicture/NavOrVideoCallJpipActivity.kt @@ -0,0 +1,37 @@ +package com.example.compose.snippets.pictureinpicture + +import android.content.res.Configuration +import android.os.Bundle +import androidx.activity.ComponentActivity +import androidx.activity.compose.setContent +import androidx.core.content.ContextCompat +import androidx.core.pip.BasicPictureInPicture +import androidx.core.pip.PictureInPictureDelegate + +// [START android_jpip_basic_impl] +class NavOrVideoCallJpipActivity : ComponentActivity(), PictureInPictureDelegate.OnPictureInPictureEventListener { + private lateinit var pictureInPictureImpl: BasicPictureInPicture + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + // BasicPictureInPicture is ideal for Navigation and Video call use cases. + pictureInPictureImpl.addOnPictureInPictureEventListener( + ContextCompat.getMainExecutor(this), + this + ) + setContent { + + } + } + override fun onPictureInPictureEvent( + event: PictureInPictureDelegate.Event, + config: Configuration? + ) { + when (event) { + PictureInPictureDelegate.Event.ENTERED -> { /* Toggle to PiP layout */ } + PictureInPictureDelegate.Event.EXITED -> { /* Toggle to Full-screen layout */ } + PictureInPictureDelegate.Event.STASHED -> { /* Optional: PiP is stashed */ } + PictureInPictureDelegate.Event.UNSTASHED -> { /* Optional: PiP is unstashed */ } + } + } +} +// [END android_jpip_basic_impl] \ No newline at end of file diff --git a/compose/snippets/src/main/java/com/example/compose/snippets/pictureinpicture/PictureInPictureSnippets.kt b/compose/snippets/src/main/java/com/example/compose/snippets/pictureinpicture/PictureInPictureSnippets.kt index 295894832..f9f9aef8f 100644 --- a/compose/snippets/src/main/java/com/example/compose/snippets/pictureinpicture/PictureInPictureSnippets.kt +++ b/compose/snippets/src/main/java/com/example/compose/snippets/pictureinpicture/PictureInPictureSnippets.kt @@ -24,6 +24,7 @@ import android.content.ContextWrapper import android.content.Intent import android.content.IntentFilter import android.os.Build +import android.os.Bundle import android.util.Log import android.util.Rational import androidx.activity.ComponentActivity @@ -45,6 +46,7 @@ import androidx.compose.ui.layout.onGloballyPositioned import androidx.compose.ui.platform.LocalContext import androidx.core.app.PictureInPictureModeChangedInfo import androidx.core.content.ContextCompat +import androidx.core.content.ContextCompat.getMainExecutor import androidx.core.graphics.toRect import androidx.core.util.Consumer import androidx.media3.common.Player @@ -404,3 +406,4 @@ fun EnterPiPPre12(shouldEnterPipMode: Boolean) { } // [END android_compose_pip_pre12_should_enter_pip] } + diff --git a/compose/snippets/src/main/java/com/example/compose/snippets/pictureinpicture/VideoPlaybackJpipActivity.kt b/compose/snippets/src/main/java/com/example/compose/snippets/pictureinpicture/VideoPlaybackJpipActivity.kt new file mode 100644 index 000000000..c4e3d117d --- /dev/null +++ b/compose/snippets/src/main/java/com/example/compose/snippets/pictureinpicture/VideoPlaybackJpipActivity.kt @@ -0,0 +1,44 @@ +package com.example.compose.snippets.pictureinpicture + +import android.content.res.Configuration +import android.os.Bundle +import androidx.activity.ComponentActivity +import androidx.activity.compose.setContent +import androidx.core.content.ContextCompat +import androidx.core.pip.PictureInPictureDelegate +import androidx.core.pip.VideoPlaybackPictureInPicture + +// [START android_jpip_video_playback_impl] +class VideoPlaybackJpipActivity : ComponentActivity(), PictureInPictureDelegate.OnPictureInPictureEventListener { + private lateinit var pictureInPictureImpl: VideoPlaybackPictureInPicture + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + // BasicPictureInPicture is ideal for Navigation and Video call use cases. + pictureInPictureImpl.addOnPictureInPictureEventListener( + ContextCompat.getMainExecutor(this), + this + ) + setContent { + } + } + override fun onPictureInPictureEvent( + event: PictureInPictureDelegate.Event, + config: Configuration? + ) { + when (event) { + PictureInPictureDelegate.Event.ENTER_ANIMATION_START -> { /* Hide overlays */ } + PictureInPictureDelegate.Event.ENTER_ANIMATION_END -> { /* Animation finished */ } + PictureInPictureDelegate.Event.ENTERED -> { /* Switch to PiP layout */ } + PictureInPictureDelegate.Event.STASHED -> { /* PiP stashed */ } + PictureInPictureDelegate.Event.UNSTASHED -> { /* PiP unstashed */ } + PictureInPictureDelegate.Event.EXITED -> { /* Return to full-screen */ } + } + } + + override fun onDestroy() { + super.onDestroy() + pictureInPictureImpl.close() + } + +} +// [END android_jpip_video_playback_impl] \ No newline at end of file diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 24f9342c5..75e676435 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -2,7 +2,7 @@ accompanist = "0.36.0" activityKtx = "1.13.0" android-googleid = "1.2.0" -androidGradlePlugin = "9.1.0" +androidGradlePlugin = "9.1.1" androidx-activity-compose = "1.13.0" androidx-appcompat = "1.7.1" androidx-appfunctions = "1.0.0-alpha08" @@ -15,6 +15,7 @@ androidx-constraintlayout = "2.2.1" androidx-constraintlayout-compose = "1.1.1" androidx-coordinator-layout = "1.3.0" androidx-corektx = "1.18.0" +androidx-corepip = "1.0.0-alpha02" androidx-credentials = "1.6.0-rc02" androidx-credentials-play-services-auth = "1.6.0-rc02" androidx-emoji2-views = "1.6.0" @@ -167,6 +168,7 @@ androidx-constraintlayout = { module = "androidx.constraintlayout:constraintlayo androidx-constraintlayout-compose = { module = "androidx.constraintlayout:constraintlayout-compose", version.ref = "androidx-constraintlayout-compose" } androidx-coordinator-layout = { module = "androidx.coordinatorlayout:coordinatorlayout", version.ref = "androidx-coordinator-layout" } androidx-core-ktx = { module = "androidx.core:core-ktx", version.ref = "androidx-corektx" } +androidx-core-pip = { module = "androidx.core:core-pip", version.ref = "androidx-corepip"} androidx-core-splashscreen = { module = "androidx.core:core-splashscreen", version.ref = "coreSplashscreen" } androidx-credentials = { module = "androidx.credentials:credentials", version.ref = "androidx-credentials" } androidx-credentials-play-services-auth = { module = "androidx.credentials:credentials-play-services-auth", version.ref = "androidx-credentials-play-services-auth" } From b7955ef36bbb12568b9bbb60a123013b45778b42 Mon Sep 17 00:00:00 2001 From: Aaron Labiaga Date: Tue, 5 May 2026 13:31:45 -0600 Subject: [PATCH 2/5] fixes from gemini assist code review Change-Id: I39e13958f811889e70b72c6e71256da7c3e75e72 --- .../notifications/NotificationsSnippets.kt | 6 +++--- .../NavOrVideoCallJpipActivity.kt | 20 ++++++++++++++++-- .../VideoPlaybackJpipActivity.kt | 21 ++++++++++++++++--- 3 files changed, 39 insertions(+), 8 deletions(-) diff --git a/compose/snippets/src/main/java/com/example/compose/snippets/notifications/NotificationsSnippets.kt b/compose/snippets/src/main/java/com/example/compose/snippets/notifications/NotificationsSnippets.kt index b0b5cfcf6..0eef5a8b4 100644 --- a/compose/snippets/src/main/java/com/example/compose/snippets/notifications/NotificationsSnippets.kt +++ b/compose/snippets/src/main/java/com/example/compose/snippets/notifications/NotificationsSnippets.kt @@ -16,11 +16,11 @@ package com.example.compose.snippets.notifications -import android.content.Context; -import android.content.Intent; +import android.content.Context +import android.content.Intent import android.app.PendingIntent; import androidx.compose.runtime.Composable -import androidx.core.app.NotificationCompat; +import androidx.core.app.NotificationCompat import androidx.core.app.PendingIntentCompat @Composable diff --git a/compose/snippets/src/main/java/com/example/compose/snippets/pictureinpicture/NavOrVideoCallJpipActivity.kt b/compose/snippets/src/main/java/com/example/compose/snippets/pictureinpicture/NavOrVideoCallJpipActivity.kt index 0cb7491d4..8d2f3fbf1 100644 --- a/compose/snippets/src/main/java/com/example/compose/snippets/pictureinpicture/NavOrVideoCallJpipActivity.kt +++ b/compose/snippets/src/main/java/com/example/compose/snippets/pictureinpicture/NavOrVideoCallJpipActivity.kt @@ -1,3 +1,19 @@ +/* + * Copyright 2026 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package com.example.compose.snippets.pictureinpicture import android.content.res.Configuration @@ -13,13 +29,13 @@ class NavOrVideoCallJpipActivity : ComponentActivity(), PictureInPictureDelegate private lateinit var pictureInPictureImpl: BasicPictureInPicture override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) + pictureInPictureImpl = BasicPictureInPicture(this) // BasicPictureInPicture is ideal for Navigation and Video call use cases. pictureInPictureImpl.addOnPictureInPictureEventListener( ContextCompat.getMainExecutor(this), this ) setContent { - } } override fun onPictureInPictureEvent( @@ -34,4 +50,4 @@ class NavOrVideoCallJpipActivity : ComponentActivity(), PictureInPictureDelegate } } } -// [END android_jpip_basic_impl] \ No newline at end of file +// [END android_jpip_basic_impl] diff --git a/compose/snippets/src/main/java/com/example/compose/snippets/pictureinpicture/VideoPlaybackJpipActivity.kt b/compose/snippets/src/main/java/com/example/compose/snippets/pictureinpicture/VideoPlaybackJpipActivity.kt index c4e3d117d..449c1a7dd 100644 --- a/compose/snippets/src/main/java/com/example/compose/snippets/pictureinpicture/VideoPlaybackJpipActivity.kt +++ b/compose/snippets/src/main/java/com/example/compose/snippets/pictureinpicture/VideoPlaybackJpipActivity.kt @@ -1,3 +1,19 @@ +/* + * Copyright 2026 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package com.example.compose.snippets.pictureinpicture import android.content.res.Configuration @@ -13,7 +29,7 @@ class VideoPlaybackJpipActivity : ComponentActivity(), PictureInPictureDelegate. private lateinit var pictureInPictureImpl: VideoPlaybackPictureInPicture override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - // BasicPictureInPicture is ideal for Navigation and Video call use cases. + pictureInPictureImpl = VideoPlaybackPictureInPicture(this) pictureInPictureImpl.addOnPictureInPictureEventListener( ContextCompat.getMainExecutor(this), this @@ -39,6 +55,5 @@ class VideoPlaybackJpipActivity : ComponentActivity(), PictureInPictureDelegate. super.onDestroy() pictureInPictureImpl.close() } - } -// [END android_jpip_video_playback_impl] \ No newline at end of file +// [END android_jpip_video_playback_impl] From 31de567406fb9c5b733b343f4a0f8bdb42f2c618 Mon Sep 17 00:00:00 2001 From: Aaron Labiaga Date: Tue, 5 May 2026 14:24:44 -0600 Subject: [PATCH 3/5] add comment to replace null w/ valid activity Change-Id: I4661f0db980c7fea5131c95101f9cefe147fd8ad --- .../compose/snippets/notifications/NotificationsSnippets.kt | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/compose/snippets/src/main/java/com/example/compose/snippets/notifications/NotificationsSnippets.kt b/compose/snippets/src/main/java/com/example/compose/snippets/notifications/NotificationsSnippets.kt index 0eef5a8b4..20fa35190 100644 --- a/compose/snippets/src/main/java/com/example/compose/snippets/notifications/NotificationsSnippets.kt +++ b/compose/snippets/src/main/java/com/example/compose/snippets/notifications/NotificationsSnippets.kt @@ -26,7 +26,10 @@ import androidx.core.app.PendingIntentCompat @Composable fun NotificationSnippets(context: Context) { // [START android_notification_authenticated_action] - val intent = Intent(context, null) + val intent = Intent( + context, null + /** Replace with a valid target activity */ + ) val moreSecureNotification = NotificationCompat.Action.Builder( 0, "Reply", PendingIntentCompat.getActivity(context, 0, intent, PendingIntent.FLAG_NO_CREATE, false) From 83162895172800f4ce193bb345930022b3b56098 Mon Sep 17 00:00:00 2001 From: Aaron Labiaga Date: Tue, 5 May 2026 15:09:54 -0600 Subject: [PATCH 4/5] fixes from review Change-Id: I2f1a9b5c2366c6d7bb416e146bebaa40f78fe414 --- compose/snippets/build.gradle.kts | 1 - .../pictureinpicture/PictureInPictureSnippets.kt | 1 - .../pictureinpicture/VideoPlaybackJpipActivity.kt | 13 ++++++++++--- 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/compose/snippets/build.gradle.kts b/compose/snippets/build.gradle.kts index f4a142c07..fbde90677 100644 --- a/compose/snippets/build.gradle.kts +++ b/compose/snippets/build.gradle.kts @@ -129,7 +129,6 @@ dependencies { implementation(libs.androidx.appcompat) implementation(libs.androidx.core.ktx) implementation(libs.androidx.core.pip) - implementation(libs.androidx.core.telecom) implementation(libs.androidx.lifecycle.runtime) implementation(libs.androidx.lifecycle.viewModelCompose) diff --git a/compose/snippets/src/main/java/com/example/compose/snippets/pictureinpicture/PictureInPictureSnippets.kt b/compose/snippets/src/main/java/com/example/compose/snippets/pictureinpicture/PictureInPictureSnippets.kt index f9f9aef8f..d79d673d9 100644 --- a/compose/snippets/src/main/java/com/example/compose/snippets/pictureinpicture/PictureInPictureSnippets.kt +++ b/compose/snippets/src/main/java/com/example/compose/snippets/pictureinpicture/PictureInPictureSnippets.kt @@ -24,7 +24,6 @@ import android.content.ContextWrapper import android.content.Intent import android.content.IntentFilter import android.os.Build -import android.os.Bundle import android.util.Log import android.util.Rational import androidx.activity.ComponentActivity diff --git a/compose/snippets/src/main/java/com/example/compose/snippets/pictureinpicture/VideoPlaybackJpipActivity.kt b/compose/snippets/src/main/java/com/example/compose/snippets/pictureinpicture/VideoPlaybackJpipActivity.kt index 449c1a7dd..21afdb91d 100644 --- a/compose/snippets/src/main/java/com/example/compose/snippets/pictureinpicture/VideoPlaybackJpipActivity.kt +++ b/compose/snippets/src/main/java/com/example/compose/snippets/pictureinpicture/VideoPlaybackJpipActivity.kt @@ -20,6 +20,8 @@ import android.content.res.Configuration import android.os.Bundle import androidx.activity.ComponentActivity import androidx.activity.compose.setContent +import androidx.compose.runtime.Composable +import androidx.compose.runtime.DisposableEffect import androidx.core.content.ContextCompat import androidx.core.pip.PictureInPictureDelegate import androidx.core.pip.VideoPlaybackPictureInPicture @@ -35,6 +37,7 @@ class VideoPlaybackJpipActivity : ComponentActivity(), PictureInPictureDelegate. this ) setContent { + ContentScreen(pictureInPictureImpl) } } override fun onPictureInPictureEvent( @@ -51,9 +54,13 @@ class VideoPlaybackJpipActivity : ComponentActivity(), PictureInPictureDelegate. } } - override fun onDestroy() { - super.onDestroy() - pictureInPictureImpl.close() + @Composable + fun ContentScreen(pipController: VideoPlaybackPictureInPicture) { + DisposableEffect(pipController) { + onDispose { + pipController.close() + } + } } } // [END android_jpip_video_playback_impl] From 7cb57c8ec6cf9cdf78e49418e9bdd816032c4bf8 Mon Sep 17 00:00:00 2001 From: Aaron Labiaga Date: Tue, 5 May 2026 16:58:43 -0600 Subject: [PATCH 5/5] remove unused import Change-Id: I46f5785f220e884135395ae05246a2756de91848 --- .../snippets/pictureinpicture/PictureInPictureSnippets.kt | 1 - 1 file changed, 1 deletion(-) diff --git a/compose/snippets/src/main/java/com/example/compose/snippets/pictureinpicture/PictureInPictureSnippets.kt b/compose/snippets/src/main/java/com/example/compose/snippets/pictureinpicture/PictureInPictureSnippets.kt index d79d673d9..e5756ec9e 100644 --- a/compose/snippets/src/main/java/com/example/compose/snippets/pictureinpicture/PictureInPictureSnippets.kt +++ b/compose/snippets/src/main/java/com/example/compose/snippets/pictureinpicture/PictureInPictureSnippets.kt @@ -45,7 +45,6 @@ import androidx.compose.ui.layout.onGloballyPositioned import androidx.compose.ui.platform.LocalContext import androidx.core.app.PictureInPictureModeChangedInfo import androidx.core.content.ContextCompat -import androidx.core.content.ContextCompat.getMainExecutor import androidx.core.graphics.toRect import androidx.core.util.Consumer import androidx.media3.common.Player