Preview Android Boot Animations

I was looking for a new android boot animation after flashing a new ROM to my Evo Shift. There are a lot of animations posted in the xda forums, but unfortunately not all of them come with screenshots, and even the ones that do are usually static.

I did a quick search for a way to preview them on my computer and found a program on xda forums for just that. Unfortunately it was written in .NET so it’s not cross platform and can’t be used in linux.

In the end, I decided to refresh my pygtk knowledge and write my own simple script for previewing boot animations, code below. It can be used by “ ” or just launching it and dragging a boot animation .zip file onto the window.

import zipfile
import gtk
import glib
import sys

class Screen( gtk.DrawingArea ):
    """ This class is a Drawing Area"""
    def __init__(self, fps):
        ## Old fashioned way to connect expose. I don't savvy the gobject stuff.
        self.connect ( "expose_event", self.do_expose_event )
        ## This is what gives the animation life!
        dt = int(1./fps * 1000.)
        glib.timeout_add( dt, self.tick ) # Go call tick every 50 whatsits.
        self._running = True

    def tick ( self ):
        ## This invalidates the screen, causing the expose event to fire.
        self.alloc = self.get_allocation ( )
        rect = gtk.gdk.Rectangle ( self.alloc.x, self.alloc.y, self.alloc.width, self.alloc.height )
        self.window.invalidate_rect ( rect, True )        
        return self._running # Causes timeout to tick again.

    ## When expose event fires, this is run
    def do_expose_event( self, widget, event ): = self.window.cairo_create( )
        ## Call our draw function to do stuff.
        self.draw( *self.window.get_size( ) )

class Ani(Screen):
    '''Animation widget for displaying a list of sequence of frames (pixbufs)'''
    def __init__(self, anim_list, fps, loop):
        self.images = anim_list
        self.loop = loop
        Screen.__init__(self, fps)

        # init the generator        
        self._iter = self.iter()
    def iter(self):
        '''A generator to dole out the animation frames in the right 'order' '''
        p = 0
        for pixbuf_list in self.images:
            p += 1
            self.parent.set_title('Running: sequence {0}'.format(p))
            for pixbuf in pixbuf_list:
                yield pixbuf
        while self.loop:
            # loop final list
            self.parent.set_title('Looping: sequence {0}'.format(p))
            for pixbuf in self.images[-1]:
                yield pixbuf
        # no looping
        self._running = False
        while True:
            # but we may still get expose events
            yield self.images[-1][-1]
    def next(self):
        # return next image
    def draw(self, w, h):
        # draw image
        self.window.draw_pixbuf(self.get_style().fg_gc[gtk.STATE_NORMAL],, 0, 0, 0, 0)

class Main(gtk.Window):
    def __init__(self):
        super(Main, self).__init__()
        self.connect( "delete-event", gtk.main_quit )
        self.connect( "destroy", gtk.main_quit )
        self.widget = None
        self.set_size_request ( 400, 400 )

        ###### Drag n Drop Stuff ######
        self.TARGET_TYPE_URI_LIST = 80
        dnd_list = [ ( 'text/uri-list', 0, self.TARGET_TYPE_URI_LIST ) ]
        self.drag_dest_set( gtk.DEST_DEFAULT_MOTION |
                      gtk.DEST_DEFAULT_HIGHLIGHT | gtk.DEST_DEFAULT_DROP,
                      dnd_list, gtk.gdk.ACTION_COPY)    
        self.connect('drag-data-received', self.drag_data_received)

    def load_boot_animation(self, file):
        '''Load a boot animation .zip file, creating an animation widget
        and displaying it in the window.'''
        if not zipfile.is_zipfile(file):
            print 'invalid zip file', file
            raise KeyError('invalid zip file')

        zf = zipfile.ZipFile(file, 'r')
        except KeyError:
            print 'invalid zipfile, no desc.txt'
        # read the desc.txt file
        spec ='desc.txt')
        file_list = zf.namelist()
        w, h, fps = map(int, spec.split()[:3])
        anim_list = []
        loop = False

        # parse the lines containing directorys
        for line in spec.split('\n')[1:]:
            line = line.strip()
            if line == '':
                # ignore blank lines

            # load images in the directory
            dir = line.split()[3]
            names = [x for x in file_list if dir in x]
            pixbufs = load_images(zf, names)
            if line.split()[1] == '0': 
                # if this line is set to loop, nothing after will get displayed
                loop = True

        # clear old animation
        if self.widget is not None:
            import gc
            kid = self.widget
            self.widget = None
            kid._running = False
            del kid.images # make sure to remove references to loaded images
            del kid
            #gc.collect() # force freeing of image memory

        # create animation widget
        self.widget = Ani( anim_list, fps, loop )

        self.set_size_request(w, h)

    def drag_data_received(self, wdg, context, x, y, selection, target_type, time):
        '''Event handler for dropping files onto the window.
        Contains a list of filenames, only keep the last one.'''
        if target_type == self.TARGET_TYPE_URI_LIST:
            uri ='\n')[-1]
            file = uri_to_path(uri)

                # try to load it
            except KeyError:

def load_images(zf, names):
    '''Load a list of images from a ZipFile into pixbufs
        zf - ZipFile object containing images
        names - list of files to load 
    pixbufs = []
    for img in names:
            pbf = gtk.gdk.PixbufLoader()
        except Exception, s:
            print 'cannot load {0}:'.format(img),s
    return pixbufs

def uri_to_path(uri):
    '''Convert a file URI to a path'''
        # gio is easier to use
        import gio
        path = gio.File(uri).get_path()
    except ImportError:
        # if gio isn't available
        import urllib
        # get the path to file
        path = ""
        if uri.startswith('file:\\\\\\'): # windows
	        path = uri[8:] # 8 is len('file:///')
        elif uri.startswith('file://'): # nautilus, rox
	        path = uri[7:] # 7 is len('file://')
        elif uri.startswith('file:'): # xffm
	        path = uri[5:] # 5 is len('file:')

        path = urllib.url2pathname(path) # escape special chars
        path = path.strip('\r\n\x00') # remove \r\n and NULL

    return path
def run():
    main_window = Main()
    if len(sys.argv) <= 1:
        print 'no arguments passed'
        file = sys.argv[1]

        if not zipfile.is_zipfile(file):
            print 'invalid file passed on cli', file

    # initial window display
    main_window.present( )

    # start mainloop
    gtk.main( )
if __name__ == '__main__':

Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: