#!/usr/bin/env python2.5 import cgi import codecs import os import pprint import re import shutil import sys import sqlite3 SCREENS = 0 COLUMNS = 4 ROWS = 4 HOTSEAT_SIZE = 4 CELL_SIZE = 110 CONTAINER_DESKTOP = -100 CONTAINER_HOTSEAT = -101 DIR = "db_files" AUTO_FILE = DIR + "/launcher.db" INDEX_FILE = DIR + "/index.html" def usage(): print "usage: print_db.py launcher.db <4x4|5x5|5x6|...> -- prints a launcher.db with" print " the specified grid size (rows x cols)" print "usage: print_db.py <4x4|5x5|5x6|...> -- adb pulls a launcher.db from a device" print " and prints it with the specified grid size (rows x cols)" print print "The dump will be created in a directory called db_files in cwd." print "This script will delete any db_files directory you have now" def make_dir(): shutil.rmtree(DIR, True) os.makedirs(DIR) def adb_root_remount(): os.system("adb root") os.system("adb remount") def pull_file(fn): print "pull_file: " + fn rv = os.system("adb pull" + " /data/data/com.android.launcher3/databases/launcher.db" + " " + fn); if rv != 0: print "adb pull failed" sys.exit(1) def get_favorites(conn): c = conn.cursor() c.execute("SELECT * FROM favorites") columns = [d[0] for d in c.description] rows = [] for row in c: rows.append(row) return columns,rows def get_screens(conn): c = conn.cursor() c.execute("SELECT * FROM workspaceScreens") columns = [d[0] for d in c.description] rows = [] for row in c: rows.append(row) return columns,rows def print_intent(out, id, i, cell): if cell: out.write("""shortcut""" % ( cgi.escape(cell, True) )) def print_icon(out, id, i, cell): if cell: icon_fn = "icon_%d.png" % id out.write("""""" % ( icon_fn )) f = file(DIR + "/" + icon_fn, "w") f.write(cell) f.close() def print_icon_type(out, id, i, cell): if cell == 0: out.write("Application (%d)" % cell) elif cell == 1: out.write("Shortcut (%d)" % cell) elif cell == 2: out.write("Folder (%d)" % cell) elif cell == 4: out.write("Widget (%d)" % cell) elif cell: out.write("%d" % cell) def print_cell(out, id, i, cell): if not cell is None: out.write(cgi.escape(unicode(cell))) FUNCTIONS = { "intent": print_intent, "icon": print_icon, "iconType": print_icon_type } def render_cell_info(out, cell, occupied): if cell is None: out.write(" \n" % (CELL_SIZE, CELL_SIZE)) elif cell == occupied: pass else: cellX = cell["cellX"] cellY = cell["cellY"] spanX = cell["spanX"] spanY = cell["spanY"] intent = cell["intent"] if intent: title = "title=\"%s\"" % cgi.escape(cell["intent"], True) else: title = "" out.write((" ") % ( spanX, spanY, (CELL_SIZE*spanX), (CELL_SIZE*spanY), title)) itemType = cell["itemType"] if itemType == 0: out.write("""\n""" % ( cell["_id"] )) out.write("
\n") out.write(cgi.escape(cell["title"]) + "
(app)") elif itemType == 1: out.write("""\n""" % ( cell["_id"] )) out.write("
\n") out.write(cgi.escape(cell["title"]) + "
(shortcut)") elif itemType == 2: out.write("""folder""") elif itemType == 4: out.write("widget %d
\n" % cell["appWidgetId"]) else: out.write("unknown type: %d" % itemType) out.write("\n") def render_screen_info(out, screen): out.write("") out.write("%s" % (screen["_id"])) out.write("%s" % (screen["screenRank"])) out.write("") def process_file(fn): global SCREENS, COLUMNS, ROWS, HOTSEAT_SIZE print "process_file: " + fn conn = sqlite3.connect(fn) columns,rows = get_favorites(conn) screenCols, screenRows = get_screens(conn) data = [dict(zip(columns,row)) for row in rows] screenData = [dict(zip(screenCols, screenRow)) for screenRow in screenRows] # Calculate the proper number of screens, columns, and rows in this db screensIdMap = [] hotseatIdMap = [] HOTSEAT_SIZE = 0 for d in data: if d["spanX"] is None: d["spanX"] = 1 if d["spanY"] is None: d["spanY"] = 1 if d["container"] == CONTAINER_DESKTOP: if d["screen"] not in screensIdMap: screensIdMap.append(d["screen"]) COLUMNS = max(COLUMNS, d["cellX"] + d["spanX"]) ROWS = max(ROWS, d["cellX"] + d["spanX"]) elif d["container"] == CONTAINER_HOTSEAT: hotseatIdMap.append(d["screen"]) HOTSEAT_SIZE = max(HOTSEAT_SIZE, d["screen"] + 1) SCREENS = len(screensIdMap) out = codecs.open(INDEX_FILE, encoding="utf-8", mode="w") out.write(""" """) # Data table out.write("Favorites table
\n") out.write(""" """) print_functions = [] for col in columns: print_functions.append(FUNCTIONS.get(col, print_cell)) for i in range(0,len(columns)): col = columns[i] out.write(""" """ % ( col )) out.write(""" """) for row in rows: out.write(""" """) for i in range(0,len(row)): cell = row[i] # row[0] is always _id out.write(""" """) out.write(""" """) out.write("""
%s
""") print_functions[i](out, row[0], row, cell) out.write("""
""") # Screens out.write("
Screens
\n") out.write("\n") out.write("\n") for screen in screenData: render_screen_info(out, screen) out.write("
Screen IDRank
\n") # Hotseat hotseat = [] for i in range(0, HOTSEAT_SIZE): hotseat.append(None) for row in data: if row["container"] != CONTAINER_HOTSEAT: continue screen = row["screen"] hotseat[screen] = row out.write("
Hotseat
\n") out.write("\n") for cell in hotseat: render_cell_info(out, cell, None) out.write("
\n") # Pages screens = [] for i in range(0,SCREENS): screen = [] for j in range(0,ROWS): m = [] for k in range(0,COLUMNS): m.append(None) screen.append(m) screens.append(screen) occupied = "occupied" for row in data: # desktop if row["container"] != CONTAINER_DESKTOP: continue screen = screens[screensIdMap.index(row["screen"])] cellX = row["cellX"] cellY = row["cellY"] spanX = row["spanX"] spanY = row["spanY"] for j in range(cellY, cellY+spanY): for k in range(cellX, cellX+spanX): screen[j][k] = occupied screen[cellY][cellX] = row i=0 for screen in screens: out.write("
Screen %d
\n" % i) out.write("\n") for m in screen: out.write(" \n") for cell in m: render_cell_info(out, cell, occupied) out.write("\n") out.write("
\n") i=i+1 out.write(""" """) out.close() def updateDeviceClassConstants(str): global SCREENS, COLUMNS, ROWS, HOTSEAT_SIZE match = re.search(r"(\d+)x(\d+)", str) if match: COLUMNS = int(match.group(1)) ROWS = int(match.group(2)) HOTSEAT_SIZE = 2 * int(COLUMNS / 2) return True return False def main(argv): if len(argv) == 1 or (len(argv) == 2 and updateDeviceClassConstants(argv[1])): make_dir() adb_root_remount() pull_file(AUTO_FILE) process_file(AUTO_FILE) elif len(argv) == 2 or (len(argv) == 3 and updateDeviceClassConstants(argv[2])): make_dir() process_file(argv[1]) else: usage() if __name__=="__main__": main(sys.argv)