Merge "Create PrecomputedTextView for Notifications" into udc-qpr-dev

This commit is contained in:
Ibrahim Yilmaz
2023-07-14 16:51:46 +00:00
committed by Android (Google) Code Review
4 changed files with 220 additions and 0 deletions

View File

@@ -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)
}

View File

@@ -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)
}

View File

@@ -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"
}
}

View File

@@ -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"
}
}