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

Commit 9411d73d authored by Fabián Kozynski's avatar Fabián Kozynski Committed by Fabian Kozynski
Browse files

Separate the default label from the app provided

In Tile, we had a single label field. This could be set either:
* By the app, if they wanted a custom label
* By SystemUI, if there was no label, with the label from ServiceInfo.

This could cause a race condition, as we had no way of distinguishing
who had set it, so a change in locale would have the old default label
overwrite the new one.

With this change, separate these fields. However, we maintain the API
contract of `Tile.getLabel` returning what the UI will display (as it
was before this change).

Test: atest TileTest
Test: atest CtsTileServiceTestCases android.host.systemui
Test: atest CustomTileTest CustomTileStatePersisterTest
Fixes: 293811062
Change-Id: I700c1ac0160f637552e7106d43b6c8abeec41729
parent 513dea53
Loading
Loading
Loading
Loading
+18 −0
Original line number Diff line number Diff line
@@ -64,6 +64,7 @@ public final class Tile implements Parcelable {
    private IBinder mToken;
    private Icon mIcon;
    private CharSequence mLabel;
    private CharSequence mDefaultLabel;
    private CharSequence mSubtitle;
    private CharSequence mContentDescription;
    private CharSequence mStateDescription;
@@ -142,9 +143,24 @@ public final class Tile implements Parcelable {
     * Gets the current label for the tile.
     */
    public CharSequence getLabel() {
        return mLabel != null ? mLabel : mDefaultLabel;
    }

    /**
     * @hide
     * @return
     */
    public CharSequence getCustomLabel() {
        return mLabel;
    }

    /**
     * @hide
     */
    public void setDefaultLabel(CharSequence defaultLabel) {
        mDefaultLabel = defaultLabel;
    }

    /**
     * Sets the current label for the tile.
     *
@@ -267,6 +283,7 @@ public final class Tile implements Parcelable {
        }
        dest.writeInt(mState);
        TextUtils.writeToParcel(mLabel, dest, flags);
        TextUtils.writeToParcel(mDefaultLabel, dest, flags);
        TextUtils.writeToParcel(mSubtitle, dest, flags);
        TextUtils.writeToParcel(mContentDescription, dest, flags);
        TextUtils.writeToParcel(mStateDescription, dest, flags);
@@ -285,6 +302,7 @@ public final class Tile implements Parcelable {
        }
        mState = source.readInt();
        mLabel = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(source);
        mDefaultLabel = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(source);
        mSubtitle = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(source);
        mContentDescription = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(source);
        mStateDescription = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(source);
+67 −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 android.service.quicksettings;

import static com.google.common.truth.Truth.assertThat;

import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;

import org.junit.Test;
import org.junit.runner.RunWith;

@SmallTest
@RunWith(AndroidJUnit4.class)
public class TileTest {

    private static final String DEFAULT_LABEL = "DEFAULT_LABEL";
    private static final String CUSTOM_APP_LABEL = "CUSTOM_LABEL";

    @Test
    public void testGetLabel_labelSet_usesCustomLabel() {
        Tile tile = new Tile();
        tile.setDefaultLabel(DEFAULT_LABEL);
        tile.setLabel(CUSTOM_APP_LABEL);

        assertThat(tile.getLabel()).isEqualTo(CUSTOM_APP_LABEL);
    }

    @Test
    public void testGetLabel_labelNotSet_usesDefaultLabel() {
        Tile tile = new Tile();
        tile.setDefaultLabel(DEFAULT_LABEL);

        assertThat(tile.getLabel()).isEqualTo(DEFAULT_LABEL);
    }

    @Test
    public void testGetCustomLabel_labelSet() {
        Tile tile = new Tile();
        tile.setDefaultLabel(DEFAULT_LABEL);
        tile.setLabel(CUSTOM_APP_LABEL);

        assertThat(tile.getCustomLabel()).isEqualTo(CUSTOM_APP_LABEL);
    }

    @Test
    public void testGetCustomLabel_labelNotSet() {
        Tile tile = new Tile();
        tile.setDefaultLabel(DEFAULT_LABEL);

        assertThat(tile.getCustomLabel()).isNull();
    }
}
+3 −9
Original line number Diff line number Diff line
@@ -36,7 +36,6 @@ import android.provider.Settings;
import android.service.quicksettings.IQSTileService;
import android.service.quicksettings.Tile;
import android.service.quicksettings.TileService;
import android.text.TextUtils;
import android.text.format.DateUtils;
import android.util.Log;
import android.view.IWindowManager;
@@ -190,13 +189,8 @@ public class CustomTile extends QSTileImpl<State> implements TileChangeListener
            if (updateIcon) {
                mTile.setIcon(mDefaultIcon);
            }
            // Update the label if there is no label or it is the default label.
            boolean updateLabel = mTile.getLabel() == null
                    || TextUtils.equals(mTile.getLabel(), mDefaultLabel);
            mDefaultLabel = info.loadLabel(pm);
            if (updateLabel) {
                mTile.setLabel(mDefaultLabel);
            }
            mTile.setDefaultLabel(mDefaultLabel);
        } catch (PackageManager.NameNotFoundException e) {
            mDefaultIcon = null;
            mDefaultLabel = null;
@@ -291,8 +285,8 @@ public class CustomTile extends QSTileImpl<State> implements TileChangeListener
        if (tile.getIcon() != null || overwriteNulls) {
            mTile.setIcon(tile.getIcon());
        }
        if (tile.getLabel() != null || overwriteNulls) {
            mTile.setLabel(tile.getLabel());
        if (tile.getCustomLabel() != null || overwriteNulls) {
            mTile.setLabel(tile.getCustomLabel());
        }
        if (tile.getSubtitle() != null || overwriteNulls) {
            mTile.setSubtitle(tile.getSubtitle());
+1 −1
Original line number Diff line number Diff line
@@ -113,7 +113,7 @@ internal fun writeToString(tile: Tile): String {
    return with(tile) {
        JSONObject()
            .put(STATE, state)
            .put(LABEL, label)
            .put(LABEL, customLabel)
            .put(SUBTITLE, subtitle)
            .put(CONTENT_DESCRIPTION, contentDescription)
            .put(STATE_DESCRIPTION, stateDescription)
+12 −1
Original line number Diff line number Diff line
@@ -36,8 +36,8 @@ import org.mockito.ArgumentMatchers.anyInt
import org.mockito.ArgumentMatchers.anyString
import org.mockito.Captor
import org.mockito.Mock
import org.mockito.Mockito.`when`
import org.mockito.Mockito.verify
import org.mockito.Mockito.`when`
import org.mockito.MockitoAnnotations

@SmallTest
@@ -54,6 +54,7 @@ class CustomTileStatePersisterTest : SysuiTestCase() {
        private const val TEST_SUBTITLE = "test_subtitle"
        private const val TEST_CONTENT_DESCRIPTION = "test_content_description"
        private const val TEST_STATE_DESCRIPTION = "test_state_description"
        private const val TEST_DEFAULT_LABEL = "default_label"

        private fun Tile.isEqualTo(other: Tile): Boolean {
            return state == other.state &&
@@ -156,4 +157,14 @@ class CustomTileStatePersisterTest : SysuiTestCase() {

        verify(editor).remove(KEY.toString())
    }

    @Test
    fun testWithDefaultLabel_notStored() {
        tile.setDefaultLabel(TEST_DEFAULT_LABEL)

        `when`(sharedPreferences.getString(eq(KEY.toString()), any()))
                .thenReturn(writeToString(tile))

        assertThat(customTileStatePersister.readState(KEY)!!.label).isNull()
    }
}
 No newline at end of file
Loading