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