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

Commit d5187d6c authored by Ibrahim Yilmaz's avatar Ibrahim Yilmaz Committed by Android (Google) Code Review
Browse files

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

parents 649012b8 5807e5e9
Loading
Loading
Loading
Loading
+32 −0
Original line number Diff line number Diff line
/*
 * 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)
}
+34 −0
Original line number Diff line number Diff line
/*
 * 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)
}
+59 −0
Original line number Diff line number Diff line
/*
 * 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"
    }
}
+95 −0
Original line number Diff line number Diff line
/*
 * 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"
    }
}