You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
313 lines
8.0 KiB
313 lines
8.0 KiB
#!/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)
|
|
|