Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 1f1a8d92 authored by Ioana Alexandru's avatar Ioana Alexandru
Browse files

Create pod for Compose Notifs

The pod shouldn't depend on SystemUI, so we need to copy over some
utilities like DrawablePainter.

This includes a draft for an initial structure for the library,
including a simple implementation of the standard (null) template.

Note that the Expander implementation is based on the ExpansionControl
implementation from
frameworks/base/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/row/NotificationRowPrimitives.kt,
minus the rotation and STL parts for now.

Bug: 431222735
Test: manual in ComposeGallery
Flag: EXEMPT code not in production yet
Change-Id: I04562a33c319a85e78c99bbaa2c3ff1550dd643c
parent 0caaced7
Loading
Loading
Loading
Loading
+46 −0
Original line number Diff line number Diff line
//
// Copyright (C) 2025 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
//
//      http://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 {
    default_applicable_licenses: ["frameworks_base_packages_SystemUI_license"],
}

// Pod for all compose-based notification rendering.
// IMPORTANT: this is an alpha implementation of pods; please reach out to saff@ or caitlinshk@
// before planning another folder based on this model.
android_library {
    name: "com.android.systemui.notifications.ui.composable",
    visibility: [
        // TODO: b/431222735 - This is currently only used in the Gallery app, we may want to tweak
        //  the visibility or create an api library later.
        "//vendor:__subpackages__",
    ],
    srcs: [
        "**/*.kt",
    ],
    static_libs: [
        "com.android.systemui.notifications.ui.viewmodel-api",
        "PlatformComposeCore",
        "androidx.compose.foundation_foundation",
        "androidx.compose.foundation_foundation-layout",
        "androidx.compose.material_material",
        "androidx.compose.material_material-icons-extended",
        "androidx.compose.runtime_runtime",
    ],
    defaults: [
        "SystemUI_pod_defaults_impl",
    ],
}
+21 −0
Original line number Diff line number Diff line
<?xml version="1.0" encoding="utf-8"?>
<!--
  ~ Copyright (C) 2025 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
  ~
  ~      http://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"
    package="com.android.systemui.notifications.ui.composable">

</manifest>
 No newline at end of file
+64 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2025 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
 *
 *      http://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.android.systemui.notifications.ui.composable.component

import androidx.annotation.FloatRange
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.runtime.ReadOnlyComposable
import androidx.compose.ui.graphics.Color

// Note: This is forked from
// frameworks/base/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/row/NotificationRowColors.kt

// TODO: b/432249649 - Once we move the compose code for bundles into a pod, we should consolidate
//  these duplicated files.

@Composable
@ReadOnlyComposable
internal fun notificationProtectionColor(): Color {
    // Per android.app.Notification.Colors, this is a 90% blend
    // of materialColorOnSurface over materialColorSurfaceContainerHigh
    val background = MaterialTheme.colorScheme.surfaceContainerHigh
    val primaryText = MaterialTheme.colorScheme.onSurface
    return blendARGB(primaryText, background, 0.9f)
}

/**
 * Blend between two ARGB colors using the given ratio.
 *
 * A blend ratio of 0.0 will result in [color1], 0.5 will give an even blend, 1.0 will result in
 * [color2].
 *
 * @param color1 the first ARGB color
 * @param color2 the second ARGB color
 * @param ratio the blend ratio of [color1] to [color2]
 * @see [com.android.internal.graphics.ColorUtils.blendARGB]
 */
private fun blendARGB(
    color1: Color,
    color2: Color,
    @FloatRange(from = 0.0, to = 1.0) ratio: Float,
): Color {
    val inverseRatio = 1 - ratio
    return Color(
        red = color1.red * inverseRatio + color2.red * ratio,
        green = color1.green * inverseRatio + color2.green * ratio,
        blue = color1.blue * inverseRatio + color2.blue * ratio,
        alpha = color1.alpha * inverseRatio + color2.alpha * ratio,
    )
}
+77 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2025 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
 *
 *      http://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.android.systemui.notifications.ui.composable.component

import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.ExpandLess
import androidx.compose.material.icons.filled.ExpandMore
import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp

// TODO: b/432249649 - Once we move the compose code for bundles into a pod, we should consolidate
//  these elements with ExpansionControl used there.

@OptIn(ExperimentalMaterial3ExpressiveApi::class)
@Composable
internal fun Expander(expanded: Boolean, modifier: Modifier = Modifier, numberToShow: Int? = null) {
    val textColor = MaterialTheme.colorScheme.onSurface
    val surfaceColor = notificationProtectionColor()

    Box(modifier = modifier.background(surfaceColor, RoundedCornerShape(100.dp))) {
        Row(
            verticalAlignment = Alignment.CenterVertically,
            modifier = Modifier.padding(vertical = 2.dp, horizontal = 6.dp),
        ) {
            val iconSizeDp = with(LocalDensity.current) { 16.sp.toDp() }

            if (numberToShow != null) {
                Text(
                    text = numberToShow.toString(),
                    style = MaterialTheme.typography.labelSmallEmphasized,
                    color = textColor,
                    modifier = Modifier.padding(end = 2.dp),
                )
            }
            Chevron(expanded, modifier = Modifier.size(iconSizeDp), color = textColor)
        }
    }
}

@Composable
private fun Chevron(expanded: Boolean, color: Color, modifier: Modifier = Modifier) {
    Icon(
        imageVector = if (expanded) Icons.Default.ExpandLess else Icons.Default.ExpandMore,
        contentDescription = null,
        tint = color,
        modifier = modifier,
    )
}
+44 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2025 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
 *
 *      http://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.android.systemui.notifications.ui.composable.component

import android.graphics.drawable.Drawable
import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.unit.dp
import com.android.compose.ui.graphics.painter.rememberDrawablePainter

@Composable
internal fun AppIcon(drawable: Drawable, modifier: Modifier = Modifier) {
    Box(modifier = modifier.size(40.dp).clip(CircleShape).background(Color.White)) {
        Image(
            painter = rememberDrawablePainter(drawable),
            contentDescription = null,
            modifier = Modifier.fillMaxSize(),
            contentScale = ContentScale.Fit,
        )
    }
}
Loading