| # Clock Plugins |
| |
| The clock appearing on the lock screen and always on display (AOD) can be customized via the |
| ClockProviderPlugin plugin interface. The ClockPlugin interface has been removed. |
| |
| ## Lock screen integration |
| The lockscreen code has two main components, a [clock customization library](../customization), and |
| the SystemUI [lockscreen host code](../src/com/android/keyguard). The customization library contains |
| the default clock, and some support code for managing clocks and picking the correct one to render. |
| It is used by both SystemUI for rendering and ThemePicker for selecting clocks. The SystemUI host is |
| responsible for maintaining the view within the hierarchy and propagating events to the rendered |
| clock controller. |
| |
| ### Clock Library Code |
| [ClockProvider and ClockController](../plugin/src/com/android/systemui/plugins/clocks/ClockProviderPlugin.kt) |
| serve as the interface between the lockscreen (or other host application) and the clock that is |
| being rendered. Implementing these interfaces is the primary integration point for rendering clocks |
| in SystemUI. Many of the methods have an empty default implementation and are optional for |
| implementations if the related event is not interesting to your use case. |
| |
| [DefaultClockProvider](../customization/src/com/android/systemui/shared/clocks/DefaultClockProvider.kt) and |
| [DefaultClockController](../customization/src/com/android/systemui/shared/clocks/DefaultClockController.kt) |
| implement these interfaces for the default lockscreen clock. They handle relevant events from the |
| lockscreen to update and control the small and large clock view as appropriate. |
| [AnimatableClockView](../customization/src/com/android/systemui/shared/clocks/AnimatableClockView.kt) |
| is the view that DefaultClockController uses to render both the small and large clock. |
| AnimatableClockView has moved location within the repo, but is largely unchanged from previous |
| versions of android. |
| |
| The [ClockRegistry](../customization/src/com/android/systemui/shared/clocks/ClockRegistry.kt) |
| determines which clock should be shown, and handles creating them. It does this by maintaining a |
| list of [ClockProviders](../plugin/src/com/android/systemui/plugins/clocks/ClockProviderPlugin.kt) and |
| delegating work to them as appropriate. The DefaultClockProvider is compiled in so that it is |
| guaranteed to be available, and additional ClockProviders are loaded at runtime via |
| [PluginManager](../plugin_core/src/com/android/systemui/plugins/PluginManager.java). |
| |
| [ClockPlugin](../plugin/src/com/android/systemui/plugins/clocks/ClockPlugin.java) is deprecated and no |
| longer used by keyguard to render clocks. The host code has been disabled but most of it is still |
| present in the source tree, although it will likely be removed in a later patch. |
| |
| ### Lockscreen Host |
| [ClockEventController](../src/com/android/keyguard/ClockEventController.kt) propagates events from |
| SystemUI event dispatchers to the clock controllers. It maintains a set of event listeners, but |
| otherwise attempts to do as little work as possible. It does maintain some state where necessary. |
| |
| [KeyguardClockSwitchController](../src/com/android/keyguard/KeyguardClockSwitchController.java) is |
| the primary controller for the [KeyguardClockSwitch](../src/com/android/keyguard/KeyguardClockSwitch.java), |
| which serves as the view parent within SystemUI. Together they ensure the correct clock (either |
| large or small) is shown, handle animation between clock sizes, and control some sizing/layout |
| parameters for the clocks. |
| |
| ### Creating a custom clock |
| In order to create a custom clock, a partner must: |
| - Write an implementation of ClockProviderPlugin and the subinterfaces relevant to your use-case. |
| - Build this into a seperate plugin apk, and deploy that apk to the device. |
| - Alternatively, it could be compiled directly into the customization lib like DefaultClockProvider. |
| - PluginManager should automatically notify ClockRegistry of your plugin apk when it arrives on |
| device. ClockRegistry will print info logs when it successfully loads a plugin. |
| - Set the clock either in ThemePicker or through adb: |
| `adb shell settings put secure lock_screen_custom_clock_face '''{\"clockId\":\"ID\"}'''` |
| - SystemUI should immediately load and render the new clock if it is available. |
| |
| ### Picker integration |
| Picker logic for choosing between clocks is available to our partners as part of the ThemePicker. |
| The clock picking UI will be enabled by default if there is more than 1 clock provided, otherwise |
| it will be hidden from the UI. |
| |
| ## System Health |
| |
| Clocks are high risk for battery consumption and screen burn-in because they modify the UI of AOD. |
| |
| To reduce battery consumption, it is recommended to target a maximum on-pixel-ratio (OPR) of 10%. |
| Clocks that are composed of large blocks of color that cause the OPR to exceed 10% should be |
| avoided, but this target will differ depending on the device hardware. |
| |
| To prevent screen burn-in, clocks should not be composed of large solid blocks of color, and the |
| clock should be moved around the screen to distribute the on pixels across a large number of pixels. |
| Software burn-in testing is a good starting point to assess the pixel shifting (clock movement) |
| scheme and shape of the clock. SystemUI currently treats all clocks the same in this regard using |
| [KeyguardClockPositionAlgorithm](../src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java) |
| |
| ### Software Burn-In Test |
| |
| The goal is to look for bright spots in the luminosity average over a period of time. It is |
| difficult to define a threshold where burn-in will occur. It is, therefore, recommended to compare |
| against an element on AOD that is known not to cause problems. |
| |
| For clock face that contain color, it is recommended to use an all white version of the face. Since |
| white has the highest luminosity, this version of the clock face represents the worst case scenario. |
| |
| To start, generate a sequence of screenshots for each minute over a 12 hr interval. |
| |
| ``` |
| serial = '84TY004MS' # serial number for the device |
| count = 1 |
| t = datetime.datetime(2019, 1, 1) |
| stop = t + datetime.timedelta(hours=12) |
| if not os.path.exists(OUTPUT_FOLDER): |
| raise RuntimeError('output folder "%s" does not exist' % OUTPUT_FOLDER) |
| while t <= stop: |
| os.system("adb -s %s shell 'date %s ; am broadcast -a android.intent.action.TIME_SET'" % (serial, t.strftime('%m%d%H%M%Y.%S'))) |
| os.system('adb -s %s shell screencap -p > %s/screencap_%06d.png' % (serial, OUTPUT_FOLDER, count)) |
| t += datetime.timedelta(minutes=1) |
| count += 1 |
| ``` |
| |
| Average the luminosity of the screenshots. |
| |
| ``` |
| #!python |
| import numpy |
| import scipy.ndimage |
| from imageio import imread, imwrite |
| import matplotlib.pylab as plt |
| import os |
| import os.path |
| |
| def images(path): |
| return [os.path.join(path, name) for name in os.listdir(path) if name.endswith('.png')] |
| |
| def average(images): |
| AVG = None |
| for name in images: |
| IM = scipy.ndimage.imread(name, mode='L') |
| A = numpy.array(IM, dtype=numpy.double) |
| if AVG is None: |
| AVG = A |
| else: |
| AVG += A |
| AVG /= len(images) |
| return numpy.array(AVG, dtype=numpy.uint8) |
| |
| def main(path): |
| ims = images(path) |
| if len(ims) == 0: |
| raise ValueError("folder '%s' doesn't contain any png files" % path) |
| AVG = average(ims) |
| imwrite('average.png', AVG) |
| plt.imshow(AVG) |
| plt.show() |
| |
| if __name__=='__main__': |
| import sys |
| main(sys.argv[1]) |
| ``` |
| |
| Look for bright spots in the luminosity average. If bright spots are found, action should be taken |
| to change the shape of the clock face or increase the amount of pixel shifting. |