print_db.py 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313
  1. #!/usr/bin/env python2.5
  2. import cgi
  3. import codecs
  4. import os
  5. import pprint
  6. import re
  7. import shutil
  8. import sys
  9. import sqlite3
  10. SCREENS = 0
  11. COLUMNS = 4
  12. ROWS = 4
  13. HOTSEAT_SIZE = 4
  14. CELL_SIZE = 110
  15. CONTAINER_DESKTOP = -100
  16. CONTAINER_HOTSEAT = -101
  17. DIR = "db_files"
  18. AUTO_FILE = DIR + "/launcher.db"
  19. INDEX_FILE = DIR + "/index.html"
  20. def usage():
  21. print "usage: print_db.py launcher.db <4x4|5x5|5x6|...> -- prints a launcher.db with"
  22. print " the specified grid size (rows x cols)"
  23. print "usage: print_db.py <4x4|5x5|5x6|...> -- adb pulls a launcher.db from a device"
  24. print " and prints it with the specified grid size (rows x cols)"
  25. print
  26. print "The dump will be created in a directory called db_files in cwd."
  27. print "This script will delete any db_files directory you have now"
  28. def make_dir():
  29. shutil.rmtree(DIR, True)
  30. os.makedirs(DIR)
  31. def adb_root_remount():
  32. os.system("adb root")
  33. os.system("adb remount")
  34. def pull_file(fn):
  35. print "pull_file: " + fn
  36. rv = os.system("adb pull"
  37. + " /data/data/com.android.launcher3/databases/launcher.db"
  38. + " " + fn);
  39. if rv != 0:
  40. print "adb pull failed"
  41. sys.exit(1)
  42. def get_favorites(conn):
  43. c = conn.cursor()
  44. c.execute("SELECT * FROM favorites")
  45. columns = [d[0] for d in c.description]
  46. rows = []
  47. for row in c:
  48. rows.append(row)
  49. return columns,rows
  50. def get_screens(conn):
  51. c = conn.cursor()
  52. c.execute("SELECT * FROM workspaceScreens")
  53. columns = [d[0] for d in c.description]
  54. rows = []
  55. for row in c:
  56. rows.append(row)
  57. return columns,rows
  58. def print_intent(out, id, i, cell):
  59. if cell:
  60. out.write("""<span class="intent" title="%s">shortcut</span>""" % (
  61. cgi.escape(cell, True)
  62. ))
  63. def print_icon(out, id, i, cell):
  64. if cell:
  65. icon_fn = "icon_%d.png" % id
  66. out.write("""<img style="width: 3em; height: 3em;" src="%s">""" % ( icon_fn ))
  67. f = file(DIR + "/" + icon_fn, "w")
  68. f.write(cell)
  69. f.close()
  70. def print_icon_type(out, id, i, cell):
  71. if cell == 0:
  72. out.write("Application (%d)" % cell)
  73. elif cell == 1:
  74. out.write("Shortcut (%d)" % cell)
  75. elif cell == 2:
  76. out.write("Folder (%d)" % cell)
  77. elif cell == 4:
  78. out.write("Widget (%d)" % cell)
  79. elif cell:
  80. out.write("%d" % cell)
  81. def print_cell(out, id, i, cell):
  82. if not cell is None:
  83. out.write(cgi.escape(unicode(cell)))
  84. FUNCTIONS = {
  85. "intent": print_intent,
  86. "icon": print_icon,
  87. "iconType": print_icon_type
  88. }
  89. def render_cell_info(out, cell, occupied):
  90. if cell is None:
  91. out.write(" <td width=%d height=%d></td>\n" %
  92. (CELL_SIZE, CELL_SIZE))
  93. elif cell == occupied:
  94. pass
  95. else:
  96. cellX = cell["cellX"]
  97. cellY = cell["cellY"]
  98. spanX = cell["spanX"]
  99. spanY = cell["spanY"]
  100. intent = cell["intent"]
  101. if intent:
  102. title = "title=\"%s\"" % cgi.escape(cell["intent"], True)
  103. else:
  104. title = ""
  105. out.write((" <td colspan=%d rowspan=%d width=%d height=%d"
  106. + " bgcolor=#dddddd align=center valign=middle %s>") % (
  107. spanX, spanY,
  108. (CELL_SIZE*spanX), (CELL_SIZE*spanY),
  109. title))
  110. itemType = cell["itemType"]
  111. if itemType == 0:
  112. out.write("""<img style="width: 4em; height: 4em;" src="icon_%d.png">\n""" % ( cell["_id"] ))
  113. out.write("<br/>\n")
  114. out.write(cgi.escape(cell["title"]) + " <br/><i>(app)</i>")
  115. elif itemType == 1:
  116. out.write("""<img style="width: 4em; height: 4em;" src="icon_%d.png">\n""" % ( cell["_id"] ))
  117. out.write("<br/>\n")
  118. out.write(cgi.escape(cell["title"]) + " <br/><i>(shortcut)</i>")
  119. elif itemType == 2:
  120. out.write("""<i>folder</i>""")
  121. elif itemType == 4:
  122. out.write("<i>widget %d</i><br/>\n" % cell["appWidgetId"])
  123. else:
  124. out.write("<b>unknown type: %d</b>" % itemType)
  125. out.write("</td>\n")
  126. def render_screen_info(out, screen):
  127. out.write("<tr>")
  128. out.write("<td>%s</td>" % (screen["_id"]))
  129. out.write("<td>%s</td>" % (screen["screenRank"]))
  130. out.write("</tr>")
  131. def process_file(fn):
  132. global SCREENS, COLUMNS, ROWS, HOTSEAT_SIZE
  133. print "process_file: " + fn
  134. conn = sqlite3.connect(fn)
  135. columns,rows = get_favorites(conn)
  136. screenCols, screenRows = get_screens(conn)
  137. data = [dict(zip(columns,row)) for row in rows]
  138. screenData = [dict(zip(screenCols, screenRow)) for screenRow in screenRows]
  139. # Calculate the proper number of screens, columns, and rows in this db
  140. screensIdMap = []
  141. hotseatIdMap = []
  142. HOTSEAT_SIZE = 0
  143. for d in data:
  144. if d["spanX"] is None:
  145. d["spanX"] = 1
  146. if d["spanY"] is None:
  147. d["spanY"] = 1
  148. if d["container"] == CONTAINER_DESKTOP:
  149. if d["screen"] not in screensIdMap:
  150. screensIdMap.append(d["screen"])
  151. COLUMNS = max(COLUMNS, d["cellX"] + d["spanX"])
  152. ROWS = max(ROWS, d["cellX"] + d["spanX"])
  153. elif d["container"] == CONTAINER_HOTSEAT:
  154. hotseatIdMap.append(d["screen"])
  155. HOTSEAT_SIZE = max(HOTSEAT_SIZE, d["screen"] + 1)
  156. SCREENS = len(screensIdMap)
  157. out = codecs.open(INDEX_FILE, encoding="utf-8", mode="w")
  158. out.write("""<html>
  159. <head>
  160. <style type="text/css">
  161. .intent {
  162. font-style: italic;
  163. }
  164. </style>
  165. </head>
  166. <body>
  167. """)
  168. # Data table
  169. out.write("<b>Favorites table</b><br/>\n")
  170. out.write("""<html>
  171. <table border=1 cellspacing=0 cellpadding=4>
  172. <tr>
  173. """)
  174. print_functions = []
  175. for col in columns:
  176. print_functions.append(FUNCTIONS.get(col, print_cell))
  177. for i in range(0,len(columns)):
  178. col = columns[i]
  179. out.write(""" <th>%s</th>
  180. """ % ( col ))
  181. out.write("""
  182. </tr>
  183. """)
  184. for row in rows:
  185. out.write("""<tr>
  186. """)
  187. for i in range(0,len(row)):
  188. cell = row[i]
  189. # row[0] is always _id
  190. out.write(""" <td>""")
  191. print_functions[i](out, row[0], row, cell)
  192. out.write("""</td>
  193. """)
  194. out.write("""</tr>
  195. """)
  196. out.write("""</table>
  197. """)
  198. # Screens
  199. out.write("<br/><b>Screens</b><br/>\n")
  200. out.write("<table class=layout border=1 cellspacing=0 cellpadding=4>\n")
  201. out.write("<tr><td>Screen ID</td><td>Rank</td></tr>\n")
  202. for screen in screenData:
  203. render_screen_info(out, screen)
  204. out.write("</table>\n")
  205. # Hotseat
  206. hotseat = []
  207. for i in range(0, HOTSEAT_SIZE):
  208. hotseat.append(None)
  209. for row in data:
  210. if row["container"] != CONTAINER_HOTSEAT:
  211. continue
  212. screen = row["screen"]
  213. hotseat[screen] = row
  214. out.write("<br/><b>Hotseat</b><br/>\n")
  215. out.write("<table class=layout border=1 cellspacing=0 cellpadding=4>\n")
  216. for cell in hotseat:
  217. render_cell_info(out, cell, None)
  218. out.write("</table>\n")
  219. # Pages
  220. screens = []
  221. for i in range(0,SCREENS):
  222. screen = []
  223. for j in range(0,ROWS):
  224. m = []
  225. for k in range(0,COLUMNS):
  226. m.append(None)
  227. screen.append(m)
  228. screens.append(screen)
  229. occupied = "occupied"
  230. for row in data:
  231. # desktop
  232. if row["container"] != CONTAINER_DESKTOP:
  233. continue
  234. screen = screens[screensIdMap.index(row["screen"])]
  235. cellX = row["cellX"]
  236. cellY = row["cellY"]
  237. spanX = row["spanX"]
  238. spanY = row["spanY"]
  239. for j in range(cellY, cellY+spanY):
  240. for k in range(cellX, cellX+spanX):
  241. screen[j][k] = occupied
  242. screen[cellY][cellX] = row
  243. i=0
  244. for screen in screens:
  245. out.write("<br/><b>Screen %d</b><br/>\n" % i)
  246. out.write("<table class=layout border=1 cellspacing=0 cellpadding=4>\n")
  247. for m in screen:
  248. out.write(" <tr>\n")
  249. for cell in m:
  250. render_cell_info(out, cell, occupied)
  251. out.write("</tr>\n")
  252. out.write("</table>\n")
  253. i=i+1
  254. out.write("""
  255. </body>
  256. </html>
  257. """)
  258. out.close()
  259. def updateDeviceClassConstants(str):
  260. global SCREENS, COLUMNS, ROWS, HOTSEAT_SIZE
  261. match = re.search(r"(\d+)x(\d+)", str)
  262. if match:
  263. COLUMNS = int(match.group(1))
  264. ROWS = int(match.group(2))
  265. HOTSEAT_SIZE = 2 * int(COLUMNS / 2)
  266. return True
  267. return False
  268. def main(argv):
  269. if len(argv) == 1 or (len(argv) == 2 and updateDeviceClassConstants(argv[1])):
  270. make_dir()
  271. adb_root_remount()
  272. pull_file(AUTO_FILE)
  273. process_file(AUTO_FILE)
  274. elif len(argv) == 2 or (len(argv) == 3 and updateDeviceClassConstants(argv[2])):
  275. make_dir()
  276. process_file(argv[1])
  277. else:
  278. usage()
  279. if __name__=="__main__":
  280. main(sys.argv)