Home | History | Annotate | Download | only in Launcher3
      1 #!/usr/bin/env python2.5
      2 
      3 import cgi
      4 import codecs
      5 import os
      6 import pprint
      7 import re
      8 import shutil
      9 import sys
     10 import sqlite3
     11 
     12 SCREENS = 0
     13 COLUMNS = 4
     14 ROWS = 4
     15 HOTSEAT_SIZE = 4
     16 CELL_SIZE = 110
     17 
     18 CONTAINER_DESKTOP = -100
     19 CONTAINER_HOTSEAT = -101
     20 
     21 DIR = "db_files"
     22 AUTO_FILE = DIR + "/launcher.db"
     23 INDEX_FILE = DIR + "/index.html"
     24 
     25 def 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 
     35 def make_dir():
     36   shutil.rmtree(DIR, True)
     37   os.makedirs(DIR)
     38 
     39 def adb_root_remount():
     40   os.system("adb root")
     41   os.system("adb remount")
     42 
     43 def 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 
     52 def 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 
     61 def 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 
     70 def 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 
     77 def 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 
     85 def 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 
     97 def print_cell(out, id, i, cell):
     98   if not cell is None:
     99     out.write(cgi.escape(unicode(cell)))
    100 
    101 FUNCTIONS = {
    102   "intent": print_intent,
    103   "icon": print_icon,
    104   "iconType": print_icon_type
    105 }
    106 
    107 def 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 
    145 def 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 
    151 def 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 
    290 def 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 
    300 def 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 
    312 if __name__=="__main__":
    313   main(sys.argv)
    314