Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions compose/snippets/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ android {

dependencies {
implementation(libs.androidx.work.runtime.ktx)
implementation(libs.androidx.media3.session)
val composeBom = platform(libs.androidx.compose.bom)
implementation(composeBom)
androidTestImplementation(composeBom)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
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.
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<!-- [START android_manifest_config] -->
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

note: it's actually not required to put mainfest snippets in the repo, but up to you!

<uses-permission android:name="android.permission.POST_NOTIFICATIONS"/>
<application>
</application>
<!-- [END android_manifest_config] -->
</manifest>
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,32 @@

package com.example.compose.snippets.notifications

import android.Manifest
import android.app.NotificationChannelGroup
import android.app.NotificationManager
import android.app.PendingIntent
import android.content.Context
import android.content.Intent
import android.app.PendingIntent;
import android.graphics.Bitmap
import android.graphics.BitmapFactory
import android.graphics.drawable.Icon
import android.os.Build
import androidx.activity.compose.rememberLauncherForActivityResult
import androidx.activity.result.contract.ActivityResultContracts
import androidx.annotation.OptIn
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.platform.LocalContext
import androidx.core.app.NotificationCompat
import androidx.core.app.PendingIntentCompat
import androidx.core.app.Person
import androidx.core.content.ContextCompat
import androidx.media3.common.util.UnstableApi
import androidx.media3.exoplayer.ExoPlayer
import androidx.media3.session.MediaSession
import androidx.media3.session.MediaStyleNotificationHelper
import com.example.compose.snippets.R
import com.example.compose.snippets.touchinput.Button

@Composable
fun NotificationSnippets(context: Context) {
Expand All @@ -39,4 +59,172 @@ fun NotificationSnippets(context: Context) {
.setAuthenticationRequired(true)
.build()
// [END android_notification_authenticated_action]
}
}

@Composable
fun NotificationSnippetRequestPostPermission() {
// [START android_notification_request_post_permission]
val context = LocalContext.current
val permissionLauncher = rememberLauncherForActivityResult(
ActivityResultContracts.RequestPermission()
) { isGranted ->
if (isGranted) {
// Permission granted, you can now send notifications.
} else {
// Permission denied, handle accordingly.
}
}

// In the UI ...
Button(onClick = {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
permissionLauncher.launch(Manifest.permission.POST_NOTIFICATIONS)
}
}) {
Text("Enable Notifications")
}
// [END android_notification_request_post_permission]
}

fun deleteNotificationChannelId(context: Context, channelId: String) {
// Side effects like deleting a channel should be triggered by events or wrapped in a
// LaunchedEffect to avoid executing them on every recomposition. This is only
// for demonstrative purposes
// [START android_notification_delete_channel]
val notificationManager =
ContextCompat.getSystemService<NotificationManager>(context, NotificationManager::class.java)
notificationManager?.deleteNotificationChannel(channelId)
// [END android_notification_delete_channel]
}

// [START android_notification_create_group_channel]
fun createNotificationChannelGroup(context: Context, groupId: String, groupName: String) {
val notificationManager =
ContextCompat.getSystemService(context, NotificationManager::class.java)
notificationManager?.createNotificationChannelGroup(NotificationChannelGroup(groupId, groupName))
}
// [END android_notification_create_group_channel]

@OptIn(UnstableApi::class)
fun notificationStyles(context: Context) {
val bitmapImage = BitmapFactory.decodeResource(
context.resources,
R.drawable.dog
)
val CHANNEL_ID = "channelId"
val someVeryLongMessage = "This is a very long message"
// [START android_notification_big_picture_style]
var notification =
NotificationCompat.Builder(context, CHANNEL_ID)
.setSmallIcon(com.example.compose.snippets.R.drawable.ic_logo)
.setContentTitle("Title")
.setContentText("Content text")
.setStyle(
NotificationCompat.BigPictureStyle()
.bigPicture(bitmapImage)
)
.build()
// [END android_notification_big_picture_style]

// [START android_notification_big_picture_style_thumbnail]
notification = NotificationCompat.Builder(context, CHANNEL_ID)
.setSmallIcon(R.drawable.ic_logo)
.setContentTitle("Title")
.setContentText("Content text")
.setLargeIcon(Icon.createWithResource(context, R.drawable.dog))
.setStyle(
NotificationCompat.BigPictureStyle()
.bigPicture(bitmapImage)
.bigLargeIcon(null as Bitmap?)
)
.build()
// [END android_notification_big_picture_style_thumbnail]

// [START android_notification_bigtext_style]
notification = NotificationCompat.Builder(context, CHANNEL_ID)
.setSmallIcon(R.drawable.ic_logo)
.setContentTitle("Sender name")
.setContentText("Email subject")
.setLargeIcon(Icon.createWithResource(context, R.drawable.dog))
.setStyle(
NotificationCompat.BigTextStyle()
.bigText(someVeryLongMessage)
)
.build()
// [END android_notification_bigtext_style]

// [START android_notification_inbox_style]
notification = NotificationCompat.Builder(context, CHANNEL_ID)
.setSmallIcon(R.drawable.mail)
.setContentTitle("5 New mails from Frank")
.setContentText("Check them out")
.setLargeIcon(bitmapImage)
.setStyle(
NotificationCompat.InboxStyle()
.addLine("Re: Planning")
.addLine("Delivery on its way")
.addLine("Follow-up")
)
.build()
// [END android_notification_inbox_style]

// [START android_notification_messaging_style]
val message1 = NotificationCompat.MessagingStyle.Message(
messages[0].text,
messages[0].time,
messages[0].sender
)
val message2 = NotificationCompat.MessagingStyle.Message(
messages[1].text,
messages[1].time,
messages[1].sender
)
Comment thread
alabiaga marked this conversation as resolved.
notification = NotificationCompat.Builder(context, CHANNEL_ID)
.setSmallIcon(R.drawable.ic_logo)
.setStyle(
NotificationCompat.MessagingStyle(Person.Builder().setName("Me").build())
.addMessage(message1)
.addMessage(message2)
)
.build()
// [END android_notification_messaging_style]

val player = ExoPlayer.Builder(context).build()
val mediaSession = MediaSession.Builder(context, player).build()
Comment thread
alabiaga marked this conversation as resolved.
// [START android_notification_media_style]
notification = NotificationCompat.Builder(context, CHANNEL_ID)
// Show controls on lock screen even when user hides sensitive content.
.setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
.setSmallIcon(com.example.compose.snippets.R.drawable.play)
// Add media control buttons that invoke intents in your media service
.addAction(R.drawable.previous, "Previous", null /* Add valid intent */) // #0
.addAction(R.drawable.pause, "Pause", null /* Add valid intent */) // #1
.addAction(R.drawable.next, "Next", null /* Add valid intent */) // #2
// Apply the media style template.
.setStyle(MediaStyleNotificationHelper.MediaStyle(mediaSession)
.setShowActionsInCompactView(1 /* #1: pause button */))
.setContentTitle("Wonderful music")
.setContentText("My Awesome Band")
.setLargeIcon(bitmapImage)
.build()
// [END android_notification_media_style]
}

data class Message(
val text: String,
val time: Long,
val sender: Person
)

val messages = listOf(
Message(
"Check this out!",
System.currentTimeMillis(),
Person.Builder().setName("Frank").build()
),
Message(
"Planning for the weekend?",
System.currentTimeMillis(),
Person.Builder().setName("Frank").build()
)
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
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.
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<application>
<!-- [START android_manifest_config] -->
<activity
android:name="com.example.compose.snippets.pictureinpicture.NavOrVideoCallJpipActivity"
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is the Jpip crucial for meaning here? Accidental name?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it is not. removed

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

did you mean to remove Jpip from NavOrVideoCallJpipActivity?

android:configChanges="screenSize|smallestScreenSize|screenLayout|orientation"
android:supportsPictureInPicture="true" />
<!-- [END android_manifest_config] -->
</application>
</manifest>
Original file line number Diff line number Diff line change
Expand Up @@ -404,4 +404,3 @@ fun EnterPiPPre12(shouldEnterPipMode: Boolean) {
}
// [END android_compose_pip_pre12_should_enter_pip]
}

Binary file added compose/snippets/src/main/res/drawable/chat.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
26 changes: 26 additions & 0 deletions compose/snippets/src/main/res/drawable/mail.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
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.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="20dp"
android:height="20dp"
android:viewportWidth="960"
android:viewportHeight="960"
android:tint="?attr/colorControlNormal">
<path
android:fillColor="@android:color/white"
android:pathData="M168,768Q138.3,768 117.15,746.84Q96,725.68 96,695.96L96,263.72Q96,234 117.15,213Q138.3,192 168,192L792,192Q821.7,192 842.85,213.16Q864,234.32 864,264.04L864,696.28Q864,726 842.85,747Q821.7,768 792,768L168,768ZM480,528L168,349L168,696Q168,696 168,696Q168,696 168,696L792,696Q792,696 792,696Q792,696 792,696L792,349L480,528ZM480,443L792,264L168,264L480,443ZM168,349L168,264L168,264L168,349L168,696Q168,696 168,696Q168,696 168,696L168,696Q168,696 168,696Q168,696 168,696L168,349Z"/>
</vector>
26 changes: 26 additions & 0 deletions compose/snippets/src/main/res/drawable/next.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
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.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="20dp"
android:height="20dp"
android:viewportWidth="960"
android:viewportHeight="960"
android:tint="?attr/colorControlNormal">
<path
android:fillColor="@android:color/white"
android:pathData="M648,720L648,240L720,240L720,720L648,720ZM240,696L240,264L576,480L240,696ZM312,480L312,480L312,480ZM312,564L443,480L312,396L312,564Z"/>
</vector>
26 changes: 26 additions & 0 deletions compose/snippets/src/main/res/drawable/pause.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
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.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="20dp"
android:height="20dp"
android:viewportWidth="960"
android:viewportHeight="960"
android:tint="?attr/colorControlNormal">
<path
android:fillColor="@android:color/white"
android:pathData="M528,768L528,192L768,192L768,768L528,768ZM192,768L192,192L432,192L432,768L192,768ZM600,696L696,696L696,264L600,264L600,696ZM264,696L360,696L360,264L264,264L264,696ZM264,264L264,264L264,696L264,696L264,264ZM600,264L600,264L600,696L600,696L600,264Z"/>
</vector>
26 changes: 26 additions & 0 deletions compose/snippets/src/main/res/drawable/play.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
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.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="20dp"
android:height="20dp"
android:viewportWidth="960"
android:viewportHeight="960"
android:tint="?attr/colorControlNormal">
<path
android:fillColor="@android:color/white"
android:pathData="M336,744L336,216L744,480L336,744ZM409,479L409,479L409,479ZM408,612L612,480L408,348L408,612Z"/>
</vector>
Loading
Loading