xref: /aosp_15_r20/frameworks/base/packages/SystemUI/docs/clock-plugins.md (revision d57664e9bc4670b3ecf6748a746a57c557b6bc9e)
1*d57664e9SAndroid Build Coastguard Worker# Clock Plugins
2*d57664e9SAndroid Build Coastguard Worker
3*d57664e9SAndroid Build Coastguard WorkerThe clock appearing on the lock screen and always on display (AOD) can be customized via the
4*d57664e9SAndroid Build Coastguard WorkerClockProviderPlugin plugin interface. The ClockPlugin interface has been removed.
5*d57664e9SAndroid Build Coastguard Worker
6*d57664e9SAndroid Build Coastguard Worker## Lock screen integration
7*d57664e9SAndroid Build Coastguard WorkerThe lockscreen code has two main components, a [clock customization library](../customization), and
8*d57664e9SAndroid Build Coastguard Workerthe SystemUI [lockscreen host code](../src/com/android/keyguard). The customization library contains
9*d57664e9SAndroid Build Coastguard Workerthe default clock, and some support code for managing clocks and picking the correct one to render.
10*d57664e9SAndroid Build Coastguard WorkerIt is used by both SystemUI for rendering and ThemePicker for selecting clocks. The SystemUI host is
11*d57664e9SAndroid Build Coastguard Workerresponsible for maintaining the view within the hierarchy and propagating events to the rendered
12*d57664e9SAndroid Build Coastguard Workerclock controller.
13*d57664e9SAndroid Build Coastguard Worker
14*d57664e9SAndroid Build Coastguard Worker### Clock Library Code
15*d57664e9SAndroid Build Coastguard Worker[ClockProvider and ClockController](../plugin/src/com/android/systemui/plugins/clocks/ClockProviderPlugin.kt)
16*d57664e9SAndroid Build Coastguard Workerserve as the interface between the lockscreen (or other host application) and the clock that is
17*d57664e9SAndroid Build Coastguard Workerbeing rendered. Implementing these interfaces is the primary integration point for rendering clocks
18*d57664e9SAndroid Build Coastguard Workerin SystemUI. Many of the methods have an empty default implementation and are optional for
19*d57664e9SAndroid Build Coastguard Workerimplementations if the related event is not interesting to your use case.
20*d57664e9SAndroid Build Coastguard Worker
21*d57664e9SAndroid Build Coastguard Worker[DefaultClockProvider](../customization/src/com/android/systemui/shared/clocks/DefaultClockProvider.kt) and
22*d57664e9SAndroid Build Coastguard Worker[DefaultClockController](../customization/src/com/android/systemui/shared/clocks/DefaultClockController.kt)
23*d57664e9SAndroid Build Coastguard Workerimplement these interfaces for the default lockscreen clock. They handle relevant events from the
24*d57664e9SAndroid Build Coastguard Workerlockscreen to update and control the small and large clock view as appropriate.
25*d57664e9SAndroid Build Coastguard Worker[AnimatableClockView](../customization/src/com/android/systemui/shared/clocks/AnimatableClockView.kt)
26*d57664e9SAndroid Build Coastguard Workeris the view that DefaultClockController uses to render both the small and large clock.
27*d57664e9SAndroid Build Coastguard WorkerAnimatableClockView has moved location within the repo, but is largely unchanged from previous
28*d57664e9SAndroid Build Coastguard Workerversions of android.
29*d57664e9SAndroid Build Coastguard Worker
30*d57664e9SAndroid Build Coastguard WorkerThe [ClockRegistry](../customization/src/com/android/systemui/shared/clocks/ClockRegistry.kt)
31*d57664e9SAndroid Build Coastguard Workerdetermines which clock should be shown, and handles creating them. It does this by maintaining a
32*d57664e9SAndroid Build Coastguard Workerlist of [ClockProviders](../plugin/src/com/android/systemui/plugins/clocks/ClockProviderPlugin.kt) and
33*d57664e9SAndroid Build Coastguard Workerdelegating work to them as appropriate. The DefaultClockProvider is compiled in so that it is
34*d57664e9SAndroid Build Coastguard Workerguaranteed to be available, and additional ClockProviders are loaded at runtime via
35*d57664e9SAndroid Build Coastguard Worker[PluginManager](../plugin_core/src/com/android/systemui/plugins/PluginManager.java).
36*d57664e9SAndroid Build Coastguard Worker
37*d57664e9SAndroid Build Coastguard Worker[ClockPlugin](../plugin/src/com/android/systemui/plugins/clocks/ClockPlugin.java) is deprecated and no
38*d57664e9SAndroid Build Coastguard Workerlonger used by keyguard to render clocks. The host code has been disabled but most of it is still
39*d57664e9SAndroid Build Coastguard Workerpresent in the source tree, although it will likely be removed in a later patch.
40*d57664e9SAndroid Build Coastguard Worker
41*d57664e9SAndroid Build Coastguard Worker### Lockscreen Host
42*d57664e9SAndroid Build Coastguard Worker[ClockEventController](../src/com/android/keyguard/ClockEventController.kt) propagates events from
43*d57664e9SAndroid Build Coastguard WorkerSystemUI event dispatchers to the clock controllers. It maintains a set of event listeners, but
44*d57664e9SAndroid Build Coastguard Workerotherwise attempts to do as little work as possible. It does maintain some state where necessary.
45*d57664e9SAndroid Build Coastguard Worker
46*d57664e9SAndroid Build Coastguard Worker[KeyguardClockSwitchController](../src/com/android/keyguard/KeyguardClockSwitchController.java) is
47*d57664e9SAndroid Build Coastguard Workerthe primary controller for the [KeyguardClockSwitch](../src/com/android/keyguard/KeyguardClockSwitch.java),
48*d57664e9SAndroid Build Coastguard Workerwhich serves as the view parent within SystemUI. Together they ensure the correct clock (either
49*d57664e9SAndroid Build Coastguard Workerlarge or small) is shown, handle animation between clock sizes, and control some sizing/layout
50*d57664e9SAndroid Build Coastguard Workerparameters for the clocks.
51*d57664e9SAndroid Build Coastguard Worker
52*d57664e9SAndroid Build Coastguard Worker### Creating a custom clock
53*d57664e9SAndroid Build Coastguard WorkerIn order to create a custom clock, a partner must:
54*d57664e9SAndroid Build Coastguard Worker - Write an implementation of ClockProviderPlugin and the subinterfaces relevant to your use-case.
55*d57664e9SAndroid Build Coastguard Worker - Build this into a seperate plugin apk, and deploy that apk to the device.
56*d57664e9SAndroid Build Coastguard Worker    - Alternatively, it could be compiled directly into the customization lib like DefaultClockProvider.
57*d57664e9SAndroid Build Coastguard Worker - PluginManager should automatically notify ClockRegistry of your plugin apk when it arrives on
58*d57664e9SAndroid Build Coastguard Worker      device. ClockRegistry will print info logs when it successfully loads a plugin.
59*d57664e9SAndroid Build Coastguard Worker - Set the clock either in ThemePicker or through adb:
60*d57664e9SAndroid Build Coastguard Worker      `adb shell settings put secure lock_screen_custom_clock_face '''{\"clockId\":\"ID\"}'''`
61*d57664e9SAndroid Build Coastguard Worker - SystemUI should immediately load and render the new clock if it is available.
62*d57664e9SAndroid Build Coastguard Worker
63*d57664e9SAndroid Build Coastguard Worker### Picker integration
64*d57664e9SAndroid Build Coastguard WorkerPicker logic for choosing between clocks is available to our partners as part of the ThemePicker.
65*d57664e9SAndroid Build Coastguard WorkerThe clock picking UI will be enabled by default if there is more than 1 clock provided, otherwise
66*d57664e9SAndroid Build Coastguard Workerit will be hidden from the UI.
67*d57664e9SAndroid Build Coastguard Worker
68*d57664e9SAndroid Build Coastguard Worker## System Health
69*d57664e9SAndroid Build Coastguard Worker
70*d57664e9SAndroid Build Coastguard WorkerClocks are high risk for battery consumption and screen burn-in because they modify the UI of AOD.
71*d57664e9SAndroid Build Coastguard Worker
72*d57664e9SAndroid Build Coastguard WorkerTo reduce battery consumption, it is recommended to target a maximum on-pixel-ratio (OPR) of 10%.
73*d57664e9SAndroid Build Coastguard WorkerClocks that are composed of large blocks of color that cause the OPR to exceed 10% should be
74*d57664e9SAndroid Build Coastguard Workeravoided, but this target will differ depending on the device hardware.
75*d57664e9SAndroid Build Coastguard Worker
76*d57664e9SAndroid Build Coastguard WorkerTo prevent screen burn-in, clocks should not be composed of large solid blocks of color, and the
77*d57664e9SAndroid Build Coastguard Workerclock should be moved around the screen to distribute the on pixels across a large number of pixels.
78*d57664e9SAndroid Build Coastguard WorkerSoftware burn-in testing is a good starting point to assess the pixel shifting (clock movement)
79*d57664e9SAndroid Build Coastguard Workerscheme and shape of the clock. SystemUI currently treats all clocks the same in this regard using
80*d57664e9SAndroid Build Coastguard Worker[KeyguardClockPositionAlgorithm](../src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java)
81*d57664e9SAndroid Build Coastguard Worker
82*d57664e9SAndroid Build Coastguard Worker### Software Burn-In Test
83*d57664e9SAndroid Build Coastguard Worker
84*d57664e9SAndroid Build Coastguard WorkerThe goal is to look for bright spots in the luminosity average over a period of time. It is
85*d57664e9SAndroid Build Coastguard Workerdifficult to define a threshold where burn-in will occur. It is, therefore, recommended to compare
86*d57664e9SAndroid Build Coastguard Workeragainst an element on AOD that is known not to cause problems.
87*d57664e9SAndroid Build Coastguard Worker
88*d57664e9SAndroid Build Coastguard WorkerFor clock face that contain color, it is recommended to use an all white version of the face. Since
89*d57664e9SAndroid Build Coastguard Workerwhite has the highest luminosity, this version of the clock face represents the worst case scenario.
90*d57664e9SAndroid Build Coastguard Worker
91*d57664e9SAndroid Build Coastguard WorkerTo start, generate a sequence of screenshots for each minute over a 12 hr interval.
92*d57664e9SAndroid Build Coastguard Worker
93*d57664e9SAndroid Build Coastguard Worker```
94*d57664e9SAndroid Build Coastguard Workerserial = '84TY004MS' # serial number for the device
95*d57664e9SAndroid Build Coastguard Workercount = 1
96*d57664e9SAndroid Build Coastguard Workert = datetime.datetime(2019, 1, 1)
97*d57664e9SAndroid Build Coastguard Workerstop = t + datetime.timedelta(hours=12)
98*d57664e9SAndroid Build Coastguard Workerif not os.path.exists(OUTPUT_FOLDER):
99*d57664e9SAndroid Build Coastguard Worker  raise RuntimeError('output folder "%s" does not exist' % OUTPUT_FOLDER)
100*d57664e9SAndroid Build Coastguard Workerwhile t <= stop:
101*d57664e9SAndroid Build Coastguard Worker  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')))
102*d57664e9SAndroid Build Coastguard Worker  os.system('adb -s %s shell screencap -p > %s/screencap_%06d.png' % (serial, OUTPUT_FOLDER, count))
103*d57664e9SAndroid Build Coastguard Worker  t += datetime.timedelta(minutes=1)
104*d57664e9SAndroid Build Coastguard Worker  count += 1
105*d57664e9SAndroid Build Coastguard Worker```
106*d57664e9SAndroid Build Coastguard Worker
107*d57664e9SAndroid Build Coastguard WorkerAverage the luminosity of the screenshots.
108*d57664e9SAndroid Build Coastguard Worker
109*d57664e9SAndroid Build Coastguard Worker```
110*d57664e9SAndroid Build Coastguard Worker#!python
111*d57664e9SAndroid Build Coastguard Workerimport numpy
112*d57664e9SAndroid Build Coastguard Workerimport scipy.ndimage
113*d57664e9SAndroid Build Coastguard Workerfrom imageio import imread, imwrite
114*d57664e9SAndroid Build Coastguard Workerimport matplotlib.pylab as plt
115*d57664e9SAndroid Build Coastguard Workerimport os
116*d57664e9SAndroid Build Coastguard Workerimport os.path
117*d57664e9SAndroid Build Coastguard Worker
118*d57664e9SAndroid Build Coastguard Workerdef images(path):
119*d57664e9SAndroid Build Coastguard Worker  return [os.path.join(path, name) for name in os.listdir(path) if name.endswith('.png')]
120*d57664e9SAndroid Build Coastguard Worker
121*d57664e9SAndroid Build Coastguard Workerdef average(images):
122*d57664e9SAndroid Build Coastguard Worker  AVG = None
123*d57664e9SAndroid Build Coastguard Worker  for name in images:
124*d57664e9SAndroid Build Coastguard Worker    IM = scipy.ndimage.imread(name, mode='L')
125*d57664e9SAndroid Build Coastguard Worker    A = numpy.array(IM, dtype=numpy.double)
126*d57664e9SAndroid Build Coastguard Worker    if AVG is None:
127*d57664e9SAndroid Build Coastguard Worker      AVG = A
128*d57664e9SAndroid Build Coastguard Worker    else:
129*d57664e9SAndroid Build Coastguard Worker      AVG += A
130*d57664e9SAndroid Build Coastguard Worker  AVG /= len(images)
131*d57664e9SAndroid Build Coastguard Worker  return numpy.array(AVG, dtype=numpy.uint8)
132*d57664e9SAndroid Build Coastguard Worker
133*d57664e9SAndroid Build Coastguard Workerdef main(path):
134*d57664e9SAndroid Build Coastguard Worker  ims = images(path)
135*d57664e9SAndroid Build Coastguard Worker  if len(ims) == 0:
136*d57664e9SAndroid Build Coastguard Worker    raise ValueError("folder '%s' doesn't contain any png files" % path)
137*d57664e9SAndroid Build Coastguard Worker  AVG = average(ims)
138*d57664e9SAndroid Build Coastguard Worker  imwrite('average.png', AVG)
139*d57664e9SAndroid Build Coastguard Worker  plt.imshow(AVG)
140*d57664e9SAndroid Build Coastguard Worker  plt.show()
141*d57664e9SAndroid Build Coastguard Worker
142*d57664e9SAndroid Build Coastguard Workerif __name__=='__main__':
143*d57664e9SAndroid Build Coastguard Worker  import sys
144*d57664e9SAndroid Build Coastguard Worker  main(sys.argv[1])
145*d57664e9SAndroid Build Coastguard Worker```
146*d57664e9SAndroid Build Coastguard Worker
147*d57664e9SAndroid Build Coastguard WorkerLook for bright spots in the luminosity average. If bright spots are found, action should be taken
148*d57664e9SAndroid Build Coastguard Workerto change the shape of the clock face or increase the amount of pixel shifting.
149