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

Commit 26c01fe0 authored by Jeff DeCew's avatar Jeff DeCew Committed by Android (Google) Code Review
Browse files

Merge changes I91953608,I09b3ee97 into main

* changes:
  [RON] Inflate and bind Rich Ongoing Views
  [RON] Extract a RichOngoingContentModel from timer notifications for testing.
parents 454eac55 87d79dd9
Loading
Loading
Loading
Loading
+105 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2024 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.
 */

@file:OptIn(ExperimentalCoroutinesApi::class)

package com.android.systemui.statusbar.notification.row.ui.viewmodel

import android.app.PendingIntent
import android.platform.test.annotations.EnableFlags
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.kosmos.testScope
import com.android.systemui.statusbar.notification.row.data.repository.fakeNotificationRowRepository
import com.android.systemui.statusbar.notification.row.shared.IconModel
import com.android.systemui.statusbar.notification.row.shared.RichOngoingNotificationFlag
import com.android.systemui.statusbar.notification.row.shared.TimerContentModel
import com.android.systemui.statusbar.notification.row.shared.TimerContentModel.TimerState.Paused
import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
import java.time.Duration
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.kotlin.mock

@RunWith(AndroidJUnit4::class)
@SmallTest
@EnableFlags(RichOngoingNotificationFlag.FLAG_NAME)
class TimerViewModelTest : SysuiTestCase() {
    private val kosmos = testKosmos()
    private val testScope = kosmos.testScope
    private val repository = kosmos.fakeNotificationRowRepository

    private var contentModel: TimerContentModel?
        get() = repository.richOngoingContentModel.value as? TimerContentModel
        set(value) {
            repository.richOngoingContentModel.value = value
        }

    private lateinit var underTest: TimerViewModel

    @Before
    fun setup() {
        underTest = kosmos.getTimerViewModel(repository)
    }

    @Test
    fun labelShowsTheTimerName() =
        testScope.runTest {
            val label by collectLastValue(underTest.label)
            contentModel = pausedTimer(name = "Example Timer Name")
            assertThat(label).isEqualTo("Example Timer Name")
        }

    @Test
    fun pausedTimeRemainingFormatsWell() =
        testScope.runTest {
            val label by collectLastValue(underTest.pausedTime)
            contentModel = pausedTimer(timeRemaining = Duration.ofMinutes(3))
            assertThat(label).isEqualTo("3:00")
            contentModel = pausedTimer(timeRemaining = Duration.ofSeconds(119))
            assertThat(label).isEqualTo("1:59")
            contentModel = pausedTimer(timeRemaining = Duration.ofSeconds(121))
            assertThat(label).isEqualTo("2:01")
            contentModel = pausedTimer(timeRemaining = Duration.ofHours(1))
            assertThat(label).isEqualTo("1:00:00")
            contentModel = pausedTimer(timeRemaining = Duration.ofHours(24))
            assertThat(label).isEqualTo("24:00:00")
        }

    private fun pausedTimer(
        icon: IconModel = mock(),
        name: String = "example",
        timeRemaining: Duration = Duration.ofMinutes(3),
        resumeIntent: PendingIntent? = null,
        resetIntent: PendingIntent? = null
    ) =
        TimerContentModel(
            icon = icon,
            name = name,
            state =
                Paused(
                    timeRemaining = timeRemaining,
                    resumeIntent = resumeIntent,
                    resetIntent = resetIntent,
                )
        )
}
+116 −0
Original line number Diff line number Diff line
<?xml version="1.0" encoding="utf-8"?>
<!--
  ~ Copyright (C) 2024 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
  -->
<com.android.systemui.statusbar.notification.row.ui.view.TimerView
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    xmlns:app="http://schemas.android.com/apk/res-auto">

    <androidx.constraintlayout.widget.Guideline
        android:id="@+id/topBaseline"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        app:layout_constraintGuide_begin="22sp"
        />

    <ImageView
        android:id="@+id/icon"
        android:layout_width="24dp"
        android:layout_height="24dp"
        android:src="@drawable/ic_close"
        app:tint="@android:color/white"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toStartOf="@id/label"
        android:baseline="18dp"
        app:layout_constraintBaseline_toTopOf="@id/topBaseline"
        />
    <TextView
        android:id="@+id/label"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        app:layout_constraintStart_toEndOf="@id/icon"
        app:layout_constraintEnd_toStartOf="@id/chronoRemaining"
        android:singleLine="true"
        tools:text="15s Timer"
        app:layout_constraintBaseline_toTopOf="@id/topBaseline"
        android:paddingEnd="4dp"
        />
    <Chronometer
        android:id="@+id/chronoRemaining"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:singleLine="true"
        android:textSize="20sp"
        android:gravity="end"
        tools:text="0:12"
        app:layout_constraintBaseline_toTopOf="@id/topBaseline"
        app:layout_constraintEnd_toStartOf="@id/pausedTimeRemaining"
        app:layout_constraintStart_toEndOf="@id/label"
        android:countDown="true"
        android:paddingEnd="4dp"
        />
    <TextView
        android:id="@+id/pausedTimeRemaining"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:singleLine="true"
        android:textSize="20sp"
        android:gravity="end"
        tools:text="0:12"
        app:layout_constraintBaseline_toTopOf="@id/topBaseline"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toEndOf="@id/chronoRemaining"
        android:paddingEnd="4dp"
        />

    <androidx.constraintlayout.widget.Barrier
        android:id="@+id/bottomOfTop"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:barrierDirection="bottom"
        app:constraint_referenced_ids="icon,label,chronoRemaining,pausedTimeRemaining"
        />

    <com.android.systemui.statusbar.notification.row.ui.view.TimerButtonView
        android:id="@+id/mainButton"
        android:layout_width="124dp"
        android:layout_height="wrap_content"
        tools:text="Reset"
        tools:drawableStart="@android:drawable/ic_menu_add"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toStartOf="@id/altButton"
        app:layout_constraintTop_toBottomOf="@id/bottomOfTop"
        app:layout_constraintHorizontal_chainStyle="spread"
        android:paddingEnd="4dp"
        />

    <com.android.systemui.statusbar.notification.row.ui.view.TimerButtonView
        android:id="@+id/altButton"
        tools:text="Reset"
        tools:drawableStart="@android:drawable/ic_menu_add"
        android:drawablePadding="2dp"
        android:drawableTint="@android:color/white"
        android:layout_width="124dp"
        android:layout_height="wrap_content"
        app:layout_constraintTop_toBottomOf="@id/bottomOfTop"
        app:layout_constraintStart_toEndOf="@id/mainButton"
        app:layout_constraintEnd_toEndOf="parent"
        android:paddingEnd="4dp"
        />
</com.android.systemui.statusbar.notification.row.ui.view.TimerView>
 No newline at end of file
+14 −1
Original line number Diff line number Diff line
@@ -68,9 +68,11 @@ import com.android.systemui.statusbar.notification.icon.IconPack;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRowController;
import com.android.systemui.statusbar.notification.row.NotificationGuts;
import com.android.systemui.statusbar.notification.row.data.repository.NotificationRowRepository;
import com.android.systemui.statusbar.notification.row.shared.HeadsUpStatusBarModel;
import com.android.systemui.statusbar.notification.row.shared.NotificationContentModel;
import com.android.systemui.statusbar.notification.row.shared.NotificationRowContentBinderRefactor;
import com.android.systemui.statusbar.notification.row.shared.RichOngoingContentModel;
import com.android.systemui.statusbar.notification.stack.PriorityBucket;
import com.android.systemui.util.ListenerSet;

@@ -97,7 +99,7 @@ import java.util.Objects;
 * At the moment, there are many things here that shouldn't be and vice-versa. Hopefully we can
 * clean this up in the future.
 */
public final class NotificationEntry extends ListEntry {
public final class NotificationEntry extends ListEntry implements NotificationRowRepository {

    private final String mKey;
    private StatusBarNotification mSbn;
@@ -159,6 +161,8 @@ public final class NotificationEntry extends ListEntry {
            StateFlowKt.MutableStateFlow(null);
    private final MutableStateFlow<CharSequence> mHeadsUpStatusBarTextPublic =
            StateFlowKt.MutableStateFlow(null);
    private final MutableStateFlow<RichOngoingContentModel> mRichOngoingContentModel =
            StateFlowKt.MutableStateFlow(null);

    // indicates when this entry's view was first attached to a window
    // this value will reset when the view is completely removed from the shade (ie: filtered out)
@@ -945,6 +949,7 @@ public final class NotificationEntry extends ListEntry {
    }

    /** @see #setHeadsUpStatusBarText(CharSequence) */
    @NonNull
    public StateFlow<CharSequence> getHeadsUpStatusBarText() {
        return mHeadsUpStatusBarText;
    }
@@ -959,10 +964,17 @@ public final class NotificationEntry extends ListEntry {
    }

    /** @see #setHeadsUpStatusBarTextPublic(CharSequence) */
    @NonNull
    public StateFlow<CharSequence> getHeadsUpStatusBarTextPublic() {
        return mHeadsUpStatusBarTextPublic;
    }

    /** Gets the current RON content model, which may be null */
    @NonNull
    public StateFlow<RichOngoingContentModel> getRichOngoingContentModel() {
        return mRichOngoingContentModel;
    }

    /**
     * Sets the text to be displayed on the StatusBar, when this notification is the top pinned
     * heads up, and its content is sensitive right now.
@@ -1047,6 +1059,7 @@ public final class NotificationEntry extends ListEntry {
        HeadsUpStatusBarModel headsUpStatusBarModel = contentModel.getHeadsUpStatusBarModel();
        this.mHeadsUpStatusBarText.setValue(headsUpStatusBarModel.getPrivateText());
        this.mHeadsUpStatusBarTextPublic.setValue(headsUpStatusBarModel.getPublicText());
        this.mRichOngoingContentModel.setValue(contentModel.getRichOngoingContentModel());
    }

    /** Information about a suggestion that is being edited. */
+4 −0
Original line number Diff line number Diff line
@@ -72,6 +72,8 @@ import com.android.systemui.statusbar.policy.dagger.RemoteInputViewSubcomponent;
import com.android.systemui.util.Compile;
import com.android.systemui.util.DumpUtilsKt;

import kotlinx.coroutines.DisposableHandle;

import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Collections;
@@ -109,6 +111,8 @@ public class NotificationContentView extends FrameLayout implements Notification
    private View mHeadsUpChild;
    private HybridNotificationView mSingleLineView;

    @Nullable public DisposableHandle mContractedBinderHandle;

    private RemoteInputView mExpandedRemoteInput;
    private RemoteInputView mHeadsUpRemoteInput;

+173 −87

File changed.

Preview size limit exceeded, changes collapsed.

Loading