From 5807e5e9951e229d886c840c0792032c2a85caa8 Mon Sep 17 00:00:00 2001 From: Ibrahim Yilmaz Date: Wed, 28 Jun 2023 20:20:07 +0000 Subject: [PATCH] Create PrecomputedTextView for Notifications This CL adds PrecomputedTextView and pre computed text version of ImageFloatingTextView to SystemUI. These TextViews integrated to NotifLayoutInflaterFactory and configured with Feature Flag. Bug: 289250881 Test: atest TextPrecomputerTest Change-Id: I05d4814be67d7fce4e4befa766ac5d98709120ce --- .../row/PrecomputedImageFloatingTextView.kt | 32 +++++++ .../notification/row/PrecomputedTextView.kt | 34 +++++++ .../notification/row/TextPrecomputer.kt | 59 ++++++++++++ .../notification/row/TextPrecomputerTest.kt | 95 +++++++++++++++++++ 4 files changed, 220 insertions(+) create mode 100644 packages/SystemUI/src/com/android/systemui/statusbar/notification/row/PrecomputedImageFloatingTextView.kt create mode 100644 packages/SystemUI/src/com/android/systemui/statusbar/notification/row/PrecomputedTextView.kt create mode 100644 packages/SystemUI/src/com/android/systemui/statusbar/notification/row/TextPrecomputer.kt create mode 100644 packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/TextPrecomputerTest.kt diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/PrecomputedImageFloatingTextView.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/PrecomputedImageFloatingTextView.kt new file mode 100644 index 0000000000000..57c82dc8d47ae --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/PrecomputedImageFloatingTextView.kt @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2023 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.statusbar.notification.row + +import android.content.Context +import android.util.AttributeSet +import android.widget.RemoteViews +import com.android.internal.widget.ImageFloatingTextView + +/** Precomputed version of [ImageFloatingTextView] */ +@RemoteViews.RemoteView +class PrecomputedImageFloatingTextView +@JvmOverloads +constructor(context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0) : + ImageFloatingTextView(context, attrs, defStyleAttr), TextPrecomputer { + + override fun setTextAsync(text: CharSequence?): Runnable = precompute(this, text) +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/PrecomputedTextView.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/PrecomputedTextView.kt new file mode 100644 index 0000000000000..8508b1fd8e6bb --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/PrecomputedTextView.kt @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2023 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.statusbar.notification.row + +import android.content.Context +import android.util.AttributeSet +import android.widget.RemoteViews +import android.widget.TextView + +/** + * A user interface element that uses the PrecomputedText API to display text in a notification, + * with the help of RemoteViews. + */ +@RemoteViews.RemoteView +class PrecomputedTextView +@JvmOverloads +constructor(context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0) : + TextView(context, attrs, defStyleAttr), TextPrecomputer { + + override fun setTextAsync(text: CharSequence?): Runnable = precompute(this, text) +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/TextPrecomputer.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/TextPrecomputer.kt new file mode 100644 index 0000000000000..49f4a33ed3e98 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/TextPrecomputer.kt @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2023 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.statusbar.notification.row + +import android.text.PrecomputedText +import android.text.Spannable +import android.util.Log +import android.widget.TextView + +internal interface TextPrecomputer { + /** + * Creates PrecomputedText from given text and returns a runnable which sets precomputed text to + * the textview on main thread. + * + * @param text text to be converted to PrecomputedText + * @return Runnable that sets precomputed text on the main thread + */ + fun precompute( + textView: TextView, + text: CharSequence?, + logException: Boolean = true + ): Runnable { + val precomputedText: Spannable? = + text?.let { PrecomputedText.create(it, textView.textMetricsParams) } + + return Runnable { + try { + textView.text = precomputedText + } catch (exception: IllegalArgumentException) { + if (logException) { + Log.wtf( + /* tag = */ TAG, + /* msg = */ "PrecomputedText setText failed for TextView:$textView", + /* tr = */ exception + ) + } + textView.text = text + } + } + } + + private companion object { + private const val TAG = "TextPrecomputer" + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/TextPrecomputerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/TextPrecomputerTest.kt new file mode 100644 index 0000000000000..d46763df8a752 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/TextPrecomputerTest.kt @@ -0,0 +1,95 @@ +/* + * Copyright (C) 2023 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.statusbar.notification.row + +import android.testing.AndroidTestingRunner +import android.testing.TestableLooper.RunWithLooper +import android.text.PrecomputedText +import android.text.TextPaint +import android.widget.TextView +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.google.common.truth.Truth.assertThat +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith + +@SmallTest +@RunWith(AndroidTestingRunner::class) +@RunWithLooper +class TextPrecomputerTest : SysuiTestCase() { + + private lateinit var textPrecomputer: TextPrecomputer + + private lateinit var textView: TextView + + @Before + fun before() { + textPrecomputer = object : TextPrecomputer {} + textView = TextView(mContext) + } + + @Test + fun precompute_returnRunnable() { + // WHEN + val precomputeResult = textPrecomputer.precompute(textView, TEXT) + + // THEN + assertThat(precomputeResult).isInstanceOf(Runnable::class.java) + } + + @Test + fun precomputeRunnable_anyText_setPrecomputedText() { + // WHEN + textPrecomputer.precompute(textView, TEXT).run() + + // THEN + assertThat(textView.text).isInstanceOf(PrecomputedText::class.java) + } + + @Test + fun precomputeRunnable_differentPrecomputedTextConfig_notSetPrecomputedText() { + // GIVEN + val precomputedTextRunnable = + textPrecomputer.precompute(textView, TEXT, logException = false) + + // WHEN + textView.textMetricsParams = PrecomputedText.Params.Builder(PAINT).build() + precomputedTextRunnable.run() + + // THEN + assertThat(textView.text).isInstanceOf(String::class.java) + } + + @Test + fun precomputeRunnable_nullText_setNull() { + // GIVEN + textView.text = TEXT + val precomputedTextRunnable = textPrecomputer.precompute(textView, null) + + // WHEN + precomputedTextRunnable.run() + + // THEN + assertThat(textView.text).isEqualTo("") + } + + private companion object { + private val PAINT = TextPaint() + private const val TEXT = "Example Notification Test" + } +}