123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313 |
- #!/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("""<span class="intent" title="%s">shortcut</span>""" % (
- cgi.escape(cell, True)
- ))
- def print_icon(out, id, i, cell):
- if cell:
- icon_fn = "icon_%d.png" % id
- out.write("""<img style="width: 3em; height: 3em;" src="%s">""" % ( 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(" <td width=%d height=%d></td>\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((" <td colspan=%d rowspan=%d width=%d height=%d"
- + " bgcolor=#dddddd align=center valign=middle %s>") % (
- spanX, spanY,
- (CELL_SIZE*spanX), (CELL_SIZE*spanY),
- title))
- itemType = cell["itemType"]
- if itemType == 0:
- out.write("""<img style="width: 4em; height: 4em;" src="icon_%d.png">\n""" % ( cell["_id"] ))
- out.write("<br/>\n")
- out.write(cgi.escape(cell["title"]) + " <br/><i>(app)</i>")
- elif itemType == 1:
- out.write("""<img style="width: 4em; height: 4em;" src="icon_%d.png">\n""" % ( cell["_id"] ))
- out.write("<br/>\n")
- out.write(cgi.escape(cell["title"]) + " <br/><i>(shortcut)</i>")
- elif itemType == 2:
- out.write("""<i>folder</i>""")
- elif itemType == 4:
- out.write("<i>widget %d</i><br/>\n" % cell["appWidgetId"])
- else:
- out.write("<b>unknown type: %d</b>" % itemType)
- out.write("</td>\n")
- def render_screen_info(out, screen):
- out.write("<tr>")
- out.write("<td>%s</td>" % (screen["_id"]))
- out.write("<td>%s</td>" % (screen["screenRank"]))
- out.write("</tr>")
- 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("""<html>
- <head>
- <style type="text/css">
- .intent {
- font-style: italic;
- }
- </style>
- </head>
- <body>
- """)
- # Data table
- out.write("<b>Favorites table</b><br/>\n")
- out.write("""<html>
- <table border=1 cellspacing=0 cellpadding=4>
- <tr>
- """)
- 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(""" <th>%s</th>
- """ % ( col ))
- out.write("""
- </tr>
- """)
- for row in rows:
- out.write("""<tr>
- """)
- for i in range(0,len(row)):
- cell = row[i]
- # row[0] is always _id
- out.write(""" <td>""")
- print_functions[i](out, row[0], row, cell)
- out.write("""</td>
- """)
- out.write("""</tr>
- """)
- out.write("""</table>
- """)
- # Screens
- out.write("<br/><b>Screens</b><br/>\n")
- out.write("<table class=layout border=1 cellspacing=0 cellpadding=4>\n")
- out.write("<tr><td>Screen ID</td><td>Rank</td></tr>\n")
- for screen in screenData:
- render_screen_info(out, screen)
- out.write("</table>\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("<br/><b>Hotseat</b><br/>\n")
- out.write("<table class=layout border=1 cellspacing=0 cellpadding=4>\n")
- for cell in hotseat:
- render_cell_info(out, cell, None)
- out.write("</table>\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("<br/><b>Screen %d</b><br/>\n" % i)
- out.write("<table class=layout border=1 cellspacing=0 cellpadding=4>\n")
- for m in screen:
- out.write(" <tr>\n")
- for cell in m:
- render_cell_info(out, cell, occupied)
- out.write("</tr>\n")
- out.write("</table>\n")
- i=i+1
- out.write("""
- </body>
- </html>
- """)
- 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)
|