When the time is set to 12-hour format in Android14 MTK SystemUI, the status bar and lock screen interface display AM/PM

Article directory

  • 1. The status bar displays AM/PM
  • 2. The lock screen displays AM/PM

1. The status bar displays AM/PM

First check the layout file of the status bar: \vendor\mediatek\proprietary\packages\apps\SystemUI\res\layout\status_bar.xml
Find layouts related to time:

<com.android.systemui.statusbar.policy.Clock
                        android:id="@ + id/clock"
                        android:layout_width="wrap_content"
                        android:layout_height="match_parent"
                        android:textAppearance="@style/TextAppearance.StatusBar.Clock"
                        android:singleLine="true"
                        android:paddingStart="@dimen/status_bar_left_clock_starting_padding"
                        android:paddingEnd="@dimen/status_bar_left_clock_end_padding"
                        android:gravity="center_vertical|start"
                    />
                   

Then based on this customized java file:
\vendor\mediatek\proprietary\packages\apps\SystemUI\src\com\android\systemui\statusbar\policy\Clock.java
It is a custom clock layout file that inherits TextView, which defines three static global variables related to AM/PM display:

 private static final int AM_PM_STYLE_NORMAL = 0;//The size of AM/PM is consistent with the size of the previous time.
    private static final int AM_PM_STYLE_SMALL = 1;//The size of AM/PM is 0.7 times the size of the previous time, which is specifically implemented in the getSmallTime() method.
    private static final int AM_PM_STYLE_GONE = 2; // AM/PM is not visible

The modification method to display AM/PM in the status bar is as follows (modify in its constructor):

public Clock(Context context, AttributeSet attrs, int defStyle) {<!-- -->
        super(context, attrs, defStyle);
        mCommandQueue = Dependency.get(CommandQueue.class);
        TypedArray a = context.getTheme().obtainStyledAttributes(
                attrs,
                R.styleable.Clock,
                0, 0);
        try {<!-- -->
            mSystemUIFactoryBase = OpSystemUICustomizationFactoryBase.getOpFactory(context);
            mStatusBarExt = mSystemUIFactoryBase.makeSystemUIStatusBar(context);
            /*mAmPmStyle = mStatusBarExt.getClockAmPmStyle(a.getInt(R.styleable.Clock_amPmStyle,
                    AM_PM_STYLE_GONE));*/
            mAmPmStyle = mStatusBarExt.getClockAmPmStyle(a.getInt(R.styleable.Clock_amPmStyle,
                    AM_PM_STYLE_SMALL));//Set to display AM/PM relative time 0.7 times, specifically why it is 0.7 times, please refer to the method getSmallTime
            mNonAdaptedColor = getCurrentTextColor();
        } finally {<!-- -->
            a.recycle();
        }
        mBroadcastDispatcher = Dependency.get(BroadcastDispatcher.class);
        mUserTracker = Dependency.get(UserTracker.class);
    }

View the getSmallTime() method under Clock.java:

private final CharSequence getSmallTime() {<!-- -->
        Context context = getContext();
        boolean is24 = DateFormat.is24HourFormat(context, mCurrentUserId);
        if (mDateTimePatternGenerator == null) {<!-- -->
            // Despite its name, getInstance creates a cloned instance, so reuse the generator to
            // avoid unnecessary churn.
            mDateTimePatternGenerator = DateTimePatternGenerator.getInstance(
                context.getResources().getConfiguration().locale);
        }

        final char MAGIC1 = '\\?';
        final char MAGIC2 = '\\?';

        final String formatSkeleton = mShowSeconds
                ? is24 ? "Hms" : "hms"
                : is24 ? "Hm" : "hm";
        String format = mDateTimePatternGenerator.getBestPattern(formatSkeleton);
        if (!format.equals(mContentDescriptionFormatString)) {<!-- -->
            mContentDescriptionFormatString = format;
            mContentDescriptionFormat = new SimpleDateFormat(format);
            /*
             * Search for an unquoted "a" in the format string, so we can
             * add marker characters around it to let us find it again after
             * formatting and change its size.
             */
            if (mAmPmStyle != AM_PM_STYLE_NORMAL) {<!-- -->
                int a = -1;
                boolean quoted = false;
                for (int i = 0; i < format.length(); i + + ) {<!-- -->
                    char c = format.charAt(i);

                    if (c == '\'') {<!-- -->
                        quoted = !quoted;
                    }
                    if (!quoted & amp; & amp; c == 'a') {<!-- -->
                        a = i;
                        break;
                    }
                }

                if (a >= 0) {<!-- -->
                    // Move a back so any whitespace before AM/PM is also in the alternate size.
                    final int b = a;
                    while (a > 0 & amp; & amp; Character.isWhitespace(format.charAt(a-1))) {<!-- -->
                        a--;
                    }
                    format = format.substring(0, a) + MAGIC1 + format.substring(a, b)
                         + "a" + MAGIC2 + format.substring(b + 1);
                }
            }
            mClockFormat = new SimpleDateFormat(format);
        }
        String result = mClockFormat.format(mCalendar.getTime());

        if (mAmPmStyle != AM_PM_STYLE_NORMAL) {<!-- -->
            int magic1 = result.indexOf(MAGIC1);
            int magic2 = result.indexOf(MAGIC2);
            if (magic1 >= 0 & amp; & amp; magic2 > magic1) {<!-- -->
                SpannableStringBuilder formatted = new SpannableStringBuilder(result);
                if (mAmPmStyle == AM_PM_STYLE_GONE) {<!-- -->
                    formatted.delete(magic1, magic2 + 1);
                } else {<!-- -->
                    if (mAmPmStyle == AM_PM_STYLE_SMALL) {<!-- -->
                        CharacterStyle style = new RelativeSizeSpan(0.7f);// This RelativeSizeSpan is a style that implements the relative size of text, so the AM/PM mentioned above is 0.7 times the time size
                        formatted.setSpan(style, magic1, magic2,
                                          Spannable.SPAN_EXCLUSIVE_INCLUSIVE);
                    }
                    formatted.delete(magic2, magic2 + 1);
                    formatted.delete(magic1, magic1 + 1);
                }
                return formatted;
            }
        }

        return result;

    }

2. The lock screen displays AM/PM

The layout file corresponding to the lock screen interface: \SystemUI\res-keyguard\layout\keyguard_clock_switch.xml

# Time layout when notifications are displayed on the lock screen
<com.android.keyguard.KeyguardClockFrame
        android:id="@ + id/lockscreen_clock_view"
        android:layout_width="match_parent"
        android:layout_height="@dimen/small_clock_height"
        android:layout_alignParentStart="true"
        android:layout_alignParentTop="true"
        android:clipChildren="false"
        android:paddingStart="@dimen/clock_padding_start"
        android:visibility="invisible" />
# The lock screen interface does not display the time layout of notifications
<com.android.keyguard.KeyguardClockFrame
        android:id="@ + id/lockscreen_clock_view_large"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:clipChildren="false"
        android:visibility="gone" />

Query the location used in the specific layout based on the id: select the parameter corresponding to the id, Ctrl + N
Query the file: SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
Use the following method to load the layout:

@Override
    protected void onFinishInflate() {<!-- -->
        super.onFinishInflate();

        mSmallClockFrame = findViewById(R.id.lockscreen_clock_view);
        mLargeClockFrame = findViewById(R.id.lockscreen_clock_view_large);
        mStatusArea = findViewById(R.id.keyguard_status_area);

        onConfigChanged();
    }

Based on the query and tracking of the two parameters mSmallClockFrame and mLargeClockFrame in the above Java file, we found that the time layout is set in the following method:

void setClock(ClockController clock, int statusBarState) {<!-- -->
        mClock = clock;

        // Disconnect from existing plugin.
        mSmallClockFrame.removeAllViews();
        mLargeClockFrame.removeAllViews();

        if (clock == null) {<!-- -->
            if (mLogBuffer != null) {<!-- -->
                mLogBuffer.log(TAG, LogLevel.ERROR, "No clock being shown");
            }
            return;
        }

        // Attach small and big clock views to hierarchy.
        if (mLogBuffer != null) {<!-- -->
            mLogBuffer.log(TAG, LogLevel.INFO, "Attached new clock views to switch");
        }
        mSmallClockFrame.addView(clock.getSmallClock().getView());
        mLargeClockFrame.addView(clock.getLargeClock().getView());
        updateClockTargetRegions();
        updateStatusArea(/* animate= */false);
    }

After searching for ClockController, I found that it is an interface defined in the following file:
\vendor\mediatek\proprietary\packages\apps\SystemUI\plugin\src\com\android\systemui\plugins\ClockProviderPlugin.kt

/** Interface for controlling an active clock */
interface ClockController {<!-- -->
    /** A small version of the clock, appropriate for smaller viewports */
    val smallClock: ClockFaceController

    /** A large version of the clock, appropriate when a bigger viewport is available */
    val largeClock: ClockFaceController

    /** Determines the way the hosting app should behave when rendering either clock face */
    val config: ClockConfig

    /** Events that clocks may need to respond to */
    val events: ClockEvents

    /** Initializes various rendering parameters. If never called, provides reasonable defaults. */
    fun initialize(
        resources: Resources,
        dozeFraction: Float,
        foldFraction: Float,
    )

    /** Optional method for dumping debug information */
    fun dump(pw: PrintWriter)
}

After tracing the source of ClockFaceController, we found that it has an internal class defined in the following file:
\vendor\mediatek\proprietary\packages\apps\SystemUI\customization\src\com\android\systemui\shared\clocks\DefaultClockController.kt
Looking at this file, the init method is as follows:

init {<!-- -->
        val parent = FrameLayout(ctx)
        smallClock =
            DefaultClockFaceController(
                layoutInflater.inflate(R.layout.clock_default_small, parent, false)
                    as AnimatableClockView,
                settings?.seedColor
            )
        largeClock =
            LargeClockFaceController(
                layoutInflater.inflate(R.layout.clock_default_large, parent, false)
                    as AnimatableClockView,
                settings?.seedColor
            )
        clocks = listOf(smallClock.view, largeClock.view)

        events = DefaultClockEvents()
        events.onLocaleChanged(Locale.getDefault())
    }

The lock screen time layout when all notifications are displayed is: \vendor\mediatek\proprietary\packages\apps\SystemUI\customization\res\layout\clock_default_small.xml
The lock screen time layout without displaying notifications is:
\vendor\mediatek\proprietary\packages\apps\SystemUI\customization\res\layout\clock_default_large.xml
View clock_default_small.xml below:

<com.android.systemui.shared.clocks.AnimatableClockView
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_gravity="start"
    android:gravity="start"
    android:textSize="@dimen/small_clock_text_size"
    android:fontFamily="@*android:string/config_clockFontFamily"
    android:elegantTextHeight="false"
    android:ellipsize="none"
    android:singleLine="true"
    android:fontFeatureSettings="pnum"
    chargeAnimationDelay="350"
    dozeWeight="200"
    lockScreenWeight="400" />

It is also a custom layout: \vendor\mediatek\proprietary\packages\apps\SystemUI\customization\src\com\android\systemui\shared\clocks\AnimatableClockView. kt
The specific modifications to display AM/PM on the lock screen are as follows:

fun refreshTime() {<!-- -->
        time.timeInMillis = timeOverrideInMillis ?: System.currentTimeMillis()
        contentDescription = DateFormat.format(descFormat, time)
        //add code
        var formattedText = DateFormat.format(format, time)
        val formattedTextStr = trimAllWhitespace(formattedText.toString())
        if (format!!.contains("a")) {<!-- -->
            var formatted = SpannableStringBuilder(formattedTextStr)
            val ampm = DateFormat.format("a", time).toString()
            val style = RelativeSizeSpan(0.5f) //Refer to Clock.java to set the font style
            val ampmStart = formattedTextStr!!.indexOf(ampm, 0)
            valampmLength = ampm.length
            formatted.setSpan(style, ampmStart, ampmStart + ampmLength, Spannable.SPAN_EXCLUSIVE_INCLUSIVE)
            formattedText = formatted
        }
        //end code
        logBuffer?.log(TAG, DEBUG,
                {<!-- --> str1 = formattedText?.toString() },
                {<!-- --> "refreshTime: new formattedText=$str1" }
        )
        // Setting text actually triggers a layout pass (because the text view is set to
        // wrap_content width and TextView always relayouts for this). Avoid needless
        // relayout if the text didn't actually change.
        if (!TextUtils.equals(text, formattedText)) {<!-- -->
            text = formattedText
            logBuffer?.log(TAG, DEBUG,
                    {<!-- --> str1 = formattedText?.toString() },
                    {<!-- --> "refreshTime: done setting new time text to: $str1" }
            )
            // Because the TextLayout may mutate under the hood as a result of the new text, we
            // notify the TextAnimator that it may have changed and request a measure/layout. A
            // crash will occur on the next invocation of setTextStyle if the layout is mutated
            // without being notified TextInterpolator being notified.
            if (layout != null) {<!-- -->
                textAnimator?.updateLayout(layout)
                logBuffer?.log(TAG, DEBUG, "refreshTime: done updating textAnimator layout")
            }
            requestLayout()
            logBuffer?.log(TAG, DEBUG, "refreshTime: after requestLayout")
        }
    }
    // add function removes the blank string in the middle of the parsed time format string
    fun trimAllWhitespace(str: String?): String? {<!-- -->
        if (str != null) {<!-- -->
            vallen = str.length
            if (len > 0) {<!-- -->
                val dest = CharArray(len)
                var destPos = 0
                for (i in 0 until len) {<!-- -->
                    val c = str[i]
                    if (!java.lang.Character.isWhitespace(c)) {<!-- -->
                        dest[destPos + + ] = c
                    }
                }
                return String(dest, 0, destPos)
            }
        }
        return str
    }

After reaching this point, it still cannot be solved. You need to modify the following parameters, or modify the following parameters in AnimatableClockView.kt:

@VisibleForTesting
    var isAnimationEnabled: Boolean = false//true

Cancel the animation, otherwise the font will still be displayed as the same size as the time (because the requirement is to set the AM/PM ratio of the lock screen interface to be smaller than the time).
If the display size is too large, you need to modify the following parameters:
\vendor\mediatek\proprietary\packages\apps\SystemUI\customization\res\values\dimens.xml

<resources>
    <!-- Clock maximum font size (dp is intentional, to prevent any further scaling) -->
    <dimen name="large_clock_text_size">150dp</dimen>
    <dimen name="small_clock_text_size">60dp</dimen>

    <!-- Default line spacing multiplier between hours and minutes of the keyguard clock -->
    <item name="keyguard_clock_line_spacing_scale" type="dimen" format="float">.7</item>
    <!-- Burmese line spacing multiplier between hours and minutes of the keyguard clock -->
    <item name="keyguard_clock_line_spacing_scale_burmese" type="dimen" format="float">1</item>
</resources>

Modify the above large_clock_text_size and small_clock_text_size.
Finally compile and run to solve the problem.