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.