Robert Snoeberger | aa18485 | 2019-04-19 17:35:10 -0400 | [diff] [blame] | 1 | # Clock Plugins |
| 2 | |
| 3 | ## Introduction |
| 4 | |
| 5 | The clock appearing on the lock screen and always on display (AOD) can be |
| 6 | customized via the ClockPlugin plugin interface. |
| 7 | |
| 8 | ## System Health |
| 9 | |
| 10 | Clocks are high risk for battery consumption and screen burn-in because they |
| 11 | modify the UI of AOD. |
| 12 | |
| 13 | To reduce battery consumption, it is recommended to |
| 14 | target a maximum on-pixel-ratio (OPR) of 5%. Clocks that are composed of |
| 15 | large blocks of color that cause the OPR to exceed 5% should be avoided. |
| 16 | |
| 17 | To prevent screen burn-in, clocks should not be composed of large solid |
| 18 | blocks of color, and the clock should be moved around the screen to |
| 19 | distribute the on pixels across a large number of pixels. Software |
| 20 | burn-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 | |
| 25 | The goal is to look for bright spots in the luminosity average over a period of |
| 26 | time. It is difficult to define a threshold where burn-in will occur. It is, |
| 27 | therefore, recommended to compare against an element on AOD that is known not |
| 28 | to cause problems. |
| 29 | |
| 30 | For clock face that contain color, it is recommended to use an all white |
| 31 | version of the face. Since white has the highest luminosity, this version of |
| 32 | the clock face represents the worst case scenario. |
| 33 | |
| 34 | To start, generate a sequence of screenshots for each minute over a 12 hr interval. |
| 35 | |
| 36 | ``` |
| 37 | serial = '84TY004MS' # serial number for the device |
| 38 | count = 1 |
| 39 | t = datetime.datetime(2019, 1, 1) |
| 40 | stop = t + datetime.timedelta(hours=12) |
| 41 | if not os.path.exists(OUTPUT_FOLDER): |
| 42 | raise RuntimeError('output folder "%s" does not exist' % OUTPUT_FOLDER) |
| 43 | while 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 | |
| 50 | Average the luminosity of the screenshots. |
| 51 | |
| 52 | ``` |
| 53 | #!python |
| 54 | import numpy |
| 55 | import scipy.ndimage |
| 56 | from imageio import imread, imwrite |
| 57 | import matplotlib.pylab as plt |
| 58 | import os |
| 59 | import os.path |
| 60 | |
| 61 | def images(path): |
| 62 | return [os.path.join(path, name) for name in os.listdir(path) if name.endswith('.png')] |
| 63 | |
| 64 | def 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 | |
| 76 | def 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 | |
| 85 | if __name__=='__main__': |
| 86 | import sys |
| 87 | main(sys.argv[1]) |
| 88 | ``` |
| 89 | |
| 90 | Look for bright spots in the luminosity average. If bright spots are found, |
| 91 | action should be taken to change the shape of the clock face or increase the |
| 92 | amount of pixel shifting. |