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