1# Clock Plugins
2
3## Introduction
4
5The clock appearing on the lock screen and always on display (AOD) can be
6customized via the ClockPlugin plugin interface.
7
8## System Health
9
10Clocks are high risk for battery consumption and screen burn-in because they
11modify the UI of AOD.
12
13To reduce battery consumption, it is recommended to
14target a maximum on-pixel-ratio (OPR) of 5%. Clocks that are composed of
15large blocks of color that cause the OPR to exceed 5% should be avoided.
16
17To prevent screen burn-in, clocks should not be composed of large solid
18blocks of color, and the clock should be moved around the screen to
19distribute the on pixels across a large number of pixels. Software
20burn-in testing is a good starting point to assess the pixel shifting
21(clock movement) scheme and shape of the clock.
22
23### Software Burn-In Test
24
25The goal is to look for bright spots in the luminosity average over a period of
26time. It is difficult to define a threshold where burn-in will occur. It is,
27therefore, recommended to compare against an element on AOD that is known not
28to cause problems.
29
30For clock face that contain color, it is recommended to use an all white
31version of the face. Since white has the highest luminosity, this version of
32the clock face represents the worst case scenario.
33
34To start, generate a sequence of screenshots for each minute over a 12 hr interval.
35
36```
37serial = '84TY004MS' # serial number for the device
38count = 1
39t = datetime.datetime(2019, 1, 1)
40stop = t + datetime.timedelta(hours=12)
41if not os.path.exists(OUTPUT_FOLDER):
42  raise RuntimeError('output folder "%s" does not exist' % OUTPUT_FOLDER)
43while t <= stop:
44  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')))
45  os.system('adb -s %s shell screencap -p > %s/screencap_%06d.png' % (serial, OUTPUT_FOLDER, count))
46  t += datetime.timedelta(minutes=1)
47  count += 1
48```
49
50Average the luminosity of the screenshots.
51
52```
53#!python
54import numpy
55import scipy.ndimage
56from imageio import imread, imwrite
57import matplotlib.pylab as plt
58import os
59import os.path
60
61def images(path):
62  return [os.path.join(path, name) for name in os.listdir(path) if name.endswith('.png')]
63
64def average(images):
65  AVG = None
66  for name in images:
67    IM = scipy.ndimage.imread(name, mode='L')
68    A = numpy.array(IM, dtype=numpy.double)
69    if AVG is None:
70      AVG = A
71    else:
72      AVG += A
73  AVG /= len(images)
74  return numpy.array(AVG, dtype=numpy.uint8)
75
76def main(path):
77  ims = images(path)
78  if len(ims) == 0:
79    raise ValueError("folder '%s' doesn't contain any png files" % path)
80  AVG = average(ims)
81  imwrite('average.png', AVG)
82  plt.imshow(AVG)
83  plt.show()
84
85if __name__=='__main__':
86  import sys
87  main(sys.argv[1])
88```
89
90Look for bright spots in the luminosity average. If bright spots are found,
91action should be taken to change the shape of the clock face or increase the
92amount of pixel shifting.
93