#!/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(""" %s |
""" % ( 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(""" """)
print_functions[i](out, row[0], row, cell)
out.write(""" |
""")
out.write("""
""")
out.write("""
""")
# Screens
out.write("
Screens
\n")
out.write("\n")
out.write("Screen ID | Rank |
\n")
for screen in screenData:
render_screen_info(out, screen)
out.write("
\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)