Android8.1 SystemUI source code analysis of the battery refresh clock

SystemUI source code analysis Related Articles

Android8.1 SystemUI source code analysis of the Notification Process

Before analyzing paste it StatusBar related class diagram

The battery icon Refresh

Get the battery icon corresponding layout from the analysis of the article is SystemUI \ src \ com \ android \ systemui \ BatteryMeterView.java

Start with the constructor to start

public BatteryMeterView(Context context, AttributeSet attrs, int defStyle) {
    super(context, attrs, defStyle);

    setOrientation(LinearLayout.HORIZONTAL);
    setGravity(Gravity.CENTER_VERTICAL | Gravity.START);

    TypedArray atts = context.obtainStyledAttributes(attrs, R.styleable.BatteryMeterView,
            defStyle, 0);
    final int frameColor = atts.getColor(R.styleable.BatteryMeterView_frameColor,
            context.getColor(R.color.meter_background_color));
    mDrawable = new BatteryMeterDrawableBase(context, frameColor);
    atts.recycle();

    mSettingObserver = new SettingObserver(new Handler(context.getMainLooper()));

    mSlotBattery = context.getString(
            com.android.internal.R.string.status_bar_battery);
    mBatteryIconView = new ImageView(context);
    mBatteryIconView.setImageDrawable(mDrawable);
    final MarginLayoutParams mlp = new MarginLayoutParams(
            getResources().getDimensionPixelSize(R.dimen.status_bar_battery_icon_width),
            getResources().getDimensionPixelSize(R.dimen.status_bar_battery_icon_height));
    mlp.setMargins(0, 0, 0,
            getResources().getDimensionPixelOffset(R.dimen.battery_margin_bottom));
    addView(mBatteryIconView, mlp);

    updateShowPercent();

    Context dualToneDarkTheme = new ContextThemeWrapper(context,
            Utils.getThemeAttr(context, R.attr.darkIconTheme));
    Context dualToneLightTheme = new ContextThemeWrapper(context,
            Utils.getThemeAttr(context, R.attr.lightIconTheme));
    mDarkModeBackgroundColor = Utils.getColorAttr(dualToneDarkTheme, R.attr.backgroundColor);
    mDarkModeFillColor = Utils.getColorAttr(dualToneDarkTheme, R.attr.fillColor);
    mLightModeBackgroundColor = Utils.getColorAttr(dualToneLightTheme, R.attr.backgroundColor);
    mLightModeFillColor = Utils.getColorAttr(dualToneLightTheme, R.attr.fillColor);

    // Init to not dark at all.
    onDarkChanged(new Rect(), 0, DarkIconDispatcher.DEFAULT_ICON_TINT);
    mUserTracker = new CurrentUserTracker(mContext) {
        @Override
        public void onUserSwitched(int newUserId) {
            mUser = newUserId;
            getContext().getContentResolver().unregisterContentObserver(mSettingObserver);
            getContext().getContentResolver().registerContentObserver(
                    Settings.System.getUriFor(SHOW_BATTERY_PERCENT), false, mSettingObserver,
                    newUserId);
        }
    };
}

Under the first BatteryMeterView inherited from LinearLayout, can be seen from the above construction method, we see the battery icon is composed of two parts,

Charge percentage (the TextView) and the battery level (ImageView), do the following main constructor several operations

  1. Initialization battery level icon, corresponding to drawable BatteryMeterDrawableBase, Packages \ Apps \ SettingsLib \ the src \ Android \ settingslib \ Graph \ BatteryMeterDrawableBase.java \ COM is added to the parent cell layout level
  2. Settings.System.SHOW_BATTERY_PERCENT monitor set, when the user clicks the display percentage of power switch, call updateShowPercent () method to add before the battery charge level percentage
  3. () Set default color theme cell layout by onDarkChanged, when a change relating to the status bar, the battery layout is accordingly replaced (light and dark colors handover)

Added DarkReceiver listening PhoneStatusBarView, the final call to BatteryMeterView of onDarkChanged () method

Modify the percentage of the brush color and background color and font color battery level

////// PhoneStatusBarView
@Override
protected void onAttachedToWindow() {
    super.onAttachedToWindow();
    // Always have Battery meters in the status bar observe the dark/light modes.
    Dependency.get(DarkIconDispatcher.class).addDarkReceiver(mBattery);
}

@Override
protected void onDetachedFromWindow() {
    super.onDetachedFromWindow();
    Dependency.get(DarkIconDispatcher.class).removeDarkReceiver(mBattery);
}

/////BatteryMeterView
public void onDarkChanged(Rect area, float darkIntensity, int tint) {
    mDarkIntensity = darkIntensity;
    float intensity = DarkIconDispatcher.isInArea(area, this) ? darkIntensity : 0;
    int foreground = getColorForDarkIntensity(intensity, mLightModeFillColor,
            mDarkModeFillColor);
    int background = getColorForDarkIntensity(intensity, mLightModeBackgroundColor,
            mDarkModeBackgroundColor);
    mDrawable.setColors(foreground, background);
    setTextColor(foreground);
}

BatteryMeterDrawableBase is a custom Drawable, drawn by the battery icon path, self research interest

BatteryMeterView added power to change the monitor, look at onBatteryLevelChanged ()

 @Override
public void onBatteryLevelChanged(int level, boolean pluggedIn, boolean charging) {
    mDrawable.setBatteryLevel(level);
    // M: In case battery protection, it stop charging, but still plugged, it will
    // also wrongly show the charging icon.
    mDrawable.setCharging(pluggedIn && charging);
    mLevel = level;
    updatePercentText();
    setContentDescription(
            getContext().getString(charging ? R.string.accessibility_battery_level_charging
                    : R.string.accessibility_battery_level, level));
}

@Override
public void onPowerSaveChanged(boolean isPowerSave) {
    mDrawable.setPowerSave(isPowerSave);
}

setBatteryLevel () calculating the percentage drawing path, setCharging () based on the current level / 100f whether to draw the shape of the icon charging lightning

Battery status change process

We all know the battery status change is accepted by way of broadcasting (Intent.ACTION_BATTERY_CHANGED), search to find BatteryControllerImpl

SystemUI\src\com\android\systemui\statusbar\policy\BatteryControllerImpl.java

 @Override
public void onReceive(final Context context, Intent intent) {
    final String action = intent.getAction();
    if (action.equals(Intent.ACTION_BATTERY_CHANGED)) {
        if (mTestmode && !intent.getBooleanExtra("testmode", false)) return;
        mHasReceivedBattery = true;
        mLevel = (int)(100f
                * intent.getIntExtra(BatteryManager.EXTRA_LEVEL, 0)
                / intent.getIntExtra(BatteryManager.EXTRA_SCALE, 100));
        mPluggedIn = intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, 0) != 0;

        final int status = intent.getIntExtra(BatteryManager.EXTRA_STATUS,
                BatteryManager.BATTERY_STATUS_UNKNOWN);
        mCharged = status == BatteryManager.BATTERY_STATUS_FULL;
        mCharging = mCharged || status == BatteryManager.BATTERY_STATUS_CHARGING;

        fireBatteryLevelChanged();
    }

    .......
}


 protected void fireBatteryLevelChanged() {
    synchronized (mChangeCallbacks) {
        final int N = mChangeCallbacks.size();
        for (int i = 0; i < N; i++) {
            mChangeCallbacks.get(i).onBatteryLevelChanged(mLevel, mPluggedIn, mCharging);
        }
    }
}

After receiving the broadcast by fireBatteryLevelChanged () traversal callback listeners to send status parameters. BatteryMeterView realized BatteryStateChangeCallback,

Receive change monitor onBatteryLevelChanged ()

Driver cell portion android system, inherited Power Supply Driver Architecture linux under conventional system, Battery sys driver generates the appropriate file system driver Power Supply,

Thereby providing an interface to the user various attributes of the battery space, and then traverse the entire folder, find the various attributes of each energy supply equipment

Battery driven Android Linux kernel will provide an interface to sysfs following framework:

/ Sys / class / power_supply / ac / onlineAC power supply connection state

/ Sys / class / power_supply / usb / onlineUSB power supply connection state

/ Sys / class / power_supply / battery / status state of charge

/ Sys / class / power_supply / battery / health status of the battery

/ Sys / class / power_supply / battery / present state of use

/sys/class/power_supply/battery/capacity 电池 level

/ Sys / class / power_supply / battery / batt_vol battery voltage

/ Sys / class / power_supply / battery / batt_temp battery temperature

/ Sys / class / power_supply / battery / technology Battery Technology

When the state of the power supply apparatus changes, driver updates those files, and then send information to the java android_server_BatteryService_update layer jni the native method.

After listening to the message power_supply change, nativeUpdate function will re-read the above sysfs file to obtain the current status.

In the user layer is in the battery BatteryService.java related attributes reported to an upper layer by using a broadcast manner app.

frameworks\base\services\core\java\com\android\server\BatteryService.java

BatteryService created in SystemServer.java, BatteryService is run when the system starts up,

Charging the battery and related services, mainly made the following few things: listening UEvent, read status in sysfs, issued a notice in the upper broadcast Intent.ACTION_BATTERY_CHANGED

BatteryService the start () registered BatteryListener, when the battery configuration changes, call the update ()

private final class BatteryListener extends IBatteryPropertiesListener.Stub {
    @Override public void batteryPropertiesChanged(BatteryProperties props) {
        final long identity = Binder.clearCallingIdentity();
        try {
            BatteryService.this.update(props);
        } finally {
            Binder.restoreCallingIdentity(identity);
        }
   }
}

private void update(BatteryProperties props) {
    synchronized (mLock) {
        if (!mUpdatesStopped) {
            mBatteryProps = props;
            // Process the new values.
            processValuesLocked(false);
        } else {
            mLastBatteryProps.set(props);
        }
    }
}


 private void processValuesLocked(boolean force) {
    boolean logOutlier = false;
    long dischargeDuration = 0;
    ...

    sendIntentLocked();
    .....
}

//发送 ACTION_BATTERY_CHANGED 广播
private void sendIntentLocked() {
    //  Pack up the values and broadcast them to everyone
    final Intent intent = new Intent(Intent.ACTION_BATTERY_CHANGED);
    intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY
            | Intent.FLAG_RECEIVER_REPLACE_PENDING);

    int icon = getIconLocked(mBatteryProps.batteryLevel);

    intent.putExtra(BatteryManager.EXTRA_SEQUENCE, mSequence);
    intent.putExtra(BatteryManager.EXTRA_STATUS, mBatteryProps.batteryStatus);
    intent.putExtra(BatteryManager.EXTRA_HEALTH, mBatteryProps.batteryHealth);
    intent.putExtra(BatteryManager.EXTRA_PRESENT, mBatteryProps.batteryPresent);
    intent.putExtra(BatteryManager.EXTRA_LEVEL, mBatteryProps.batteryLevel);
    intent.putExtra(BatteryManager.EXTRA_SCALE, BATTERY_SCALE);
    intent.putExtra(BatteryManager.EXTRA_ICON_SMALL, icon);
    intent.putExtra(BatteryManager.EXTRA_PLUGGED, mPlugType);
    intent.putExtra(BatteryManager.EXTRA_VOLTAGE, mBatteryProps.batteryVoltage);
    intent.putExtra(BatteryManager.EXTRA_TEMPERATURE, mBatteryProps.batteryTemperature);
    intent.putExtra(BatteryManager.EXTRA_TECHNOLOGY, mBatteryProps.batteryTechnology);
    intent.putExtra(BatteryManager.EXTRA_INVALID_CHARGER, mInvalidCharger);
    intent.putExtra(BatteryManager.EXTRA_MAX_CHARGING_CURRENT, mBatteryProps.maxChargingCurrent);
    intent.putExtra(BatteryManager.EXTRA_MAX_CHARGING_VOLTAGE, mBatteryProps.maxChargingVoltage);
    intent.putExtra(BatteryManager.EXTRA_CHARGE_COUNTER, mBatteryProps.batteryChargeCounter);

    mHandler.post(new Runnable() {
        @Override
        public void run() {
            ActivityManager.broadcastStickyIntent(intent, UserHandle.USER_ALL);
        }
    });
}

Reads the battery status cat / sys / class / power_supply / battery / uevent

Clock icon Refresh

Status_bar.xml clock is seen from a custom View, com.android.systemui.statusbar.policy.Clock

View Clock Source knows inherited from TextView, time updates via setText (), by listening on the following five kinds of broadcast Modified Shows

@Override
protected void onAttachedToWindow() {
    super.onAttachedToWindow();

    if (!mAttached) {
        mAttached = true;
        IntentFilter filter = new IntentFilter();

        filter.addAction(Intent.ACTION_TIME_TICK);
        filter.addAction(Intent.ACTION_TIME_CHANGED);
        filter.addAction(Intent.ACTION_TIMEZONE_CHANGED);
        filter.addAction(Intent.ACTION_CONFIGURATION_CHANGED);
        filter.addAction(Intent.ACTION_USER_SWITCHED);

        getContext().registerReceiverAsUser(mIntentReceiver, UserHandle.ALL, filter,
                null, Dependency.get(Dependency.TIME_TICK_HANDLER));
        Dependency.get(TunerService.class).addTunable(this, CLOCK_SECONDS,
                StatusBarIconController.ICON_BLACKLIST);
        SysUiServiceProvider.getComponent(getContext(), CommandQueue.class).addCallbacks(this);
        if (mShowDark) {
            Dependency.get(DarkIconDispatcher.class).addDarkReceiver(this);
        }
    }

    // NOTE: It's safe to do these after registering the receiver since the receiver always runs
    // in the main thread, therefore the receiver can't run before this method returns.

    // The time zone may have changed while the receiver wasn't registered, so update the Time
    mCalendar = Calendar.getInstance(TimeZone.getDefault());

    // Make sure we update to the current time
    updateClock();
    updateShowSeconds();
}

We can see mIntentReceiver monitor the five types of action

Intent.ACTION_TIME_TICK clock frequency 1 minutes

Intent.ACTION_TIME_CHANGED clock is changed, modified by the user to set time option in the settings

Intent.ACTION_TIMEZONE_CHANGED time zone changes, the user modifies the settings in the selection zone

Intent.ACTION_CONFIGURATION_CHANGED system configuration changes, such as modifying the language system, the system screen orientation changes

Intent.ACTION_USER_SWITCHED switch user to switch between the owner or other visitors

We see the use of the system setup interface 24-hour switch, the click time will immediately change the display is by transmitting broadcast ACTION_TIME_CHANGED,

Carry EXTRA_TIME_PREF_24_HOUR_FORMAT parameters, the following are the core code

vendor\mediatek\proprietary\packages\apps\MtkSettings\src\com\android\settings\datetime\TimeFormatPreferenceController.java

private void set24Hour(boolean is24Hour) {
    Settings.System.putString(mContext.getContentResolver(),
            Settings.System.TIME_12_24,
            is24Hour ? HOURS_24 : HOURS_12);
}

private void timeUpdated(boolean is24Hour) {
    Intent timeChanged = new Intent(Intent.ACTION_TIME_CHANGED);
    int timeFormatPreference =
            is24Hour ? Intent.EXTRA_TIME_PREF_VALUE_USE_24_HOUR
                    : Intent.EXTRA_TIME_PREF_VALUE_USE_12_HOUR;
    timeChanged.putExtra(Intent.EXTRA_TIME_PREF_24_HOUR_FORMAT, timeFormatPreference);
    mContext.sendBroadcast(timeChanged);
}

Back Clock.java found EXTRA_TIME_PREF_24_HOUR_FORMAT and not spend, continue to get to the bottom Code

 final void updateClock() {
    if (mDemoMode) return;
    mCalendar.setTimeInMillis(System.currentTimeMillis());
    setText(getSmallTime());
    setContentDescription(mContentDescriptionFormat.format(mCalendar.getTime()));
}

The final will be broadcast received calls updateClock (), you can see the real time is set by getSmallTime () method the core

private final CharSequence getSmallTime() {
    Context context = getContext();
    boolean is24 = DateFormat.is24HourFormat(context, ActivityManager.getCurrentUser());
    LocaleData d = LocaleData.get(context.getResources().getConfiguration().locale);

    final char MAGIC1 = '\uEF00';
    final char MAGIC2 = '\uEF01';

    SimpleDateFormat sdf;
    String format = mShowSeconds
            ? is24 ? d.timeFormat_Hms : d.timeFormat_hms
            : is24 ? d.timeFormat_Hm : d.timeFormat_hm;
    if (!format.equals(mClockFormatString)) {
        mContentDescriptionFormat = new SimpleDateFormat(format);
        /*
         * Search for an unquoted "a" in the format string, so we can
         * add dummy 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 && 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 && Character.isWhitespace(format.charAt(a-1))) {
                    a--;
                }
                format = format.substring(0, a) + MAGIC1 + format.substring(a, b)
                    + "a" + MAGIC2 + format.substring(b + 1);
            }
        }
        mClockFormat = sdf = new SimpleDateFormat(format);
        mClockFormatString = format;
    } else {
        sdf = mClockFormat;
    }
    String result = sdf.format(mCalendar.getTime());

    if (mAmPmStyle != AM_PM_STYLE_NORMAL) {
        int magic1 = result.indexOf(MAGIC1);
        int magic2 = result.indexOf(MAGIC2);
        if (magic1 >= 0 && 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);
                    formatted.setSpan(style, magic1, magic2,
                                      Spannable.SPAN_EXCLUSIVE_INCLUSIVE);
                }
                formatted.delete(magic2, magic2 + 1);
                formatted.delete(magic1, magic1 + 1);
            }
            return formatted;
        }
    }

    return result;

}

Method is a little long, we analyze the main challenges, DateFormat.is24HourFormat () by reading Settings.System.TIME_12_24 final value,

This value is just click switch 24 hours in the above TimeFormatPreferenceController changed, if this value is null, the local time by obtaining

Local format to obtain the current time, equal to 24 if it returns true, the method may be the source code into AS view midpoint, here not posted.

LocaleData format is a time management class, are frequently used in DateUtils.java and SimpleDateFormat.java

The next format is acquired d.timeFormat_Hm, provided to SimpleDateFormat (d.timeFormat_Hm)

String result = sdf.format (mCalendar.getTime ()); that is, the time display of current needs, what needs to be done here Format

mAmPmStyle by the constructor custom property assignment, and XML no value, the default value AM_PM_STYLE_GONE, take this code

formatted.delete (magic1, magic2 + 1); removing excess '\ uEF00' and '\ uEF01', the final display is formatted.

Reference article

https://blog.csdn.net/weilaideta/article/details/51760434

https://blog.csdn.net/W1107101310/article/details/80211885

Guess you like

Origin www.cnblogs.com/cczheng-666/p/10958920.html