• We use android tools gradle plugin build to genereate ddmlib JAR file.

    Source

    You can get the source here.

    Clone the repo:

    $ git clone https://github.com/wiliamsouza/monitor.git

    Gradle

    Here you need to point to your local android tools gradle plugin repo.

    apply plugin: 'java'
    apply plugin: 'idea'
    
    sourceCompatibility = 1.5
    targetCompatibility = 1.5
    
    version = '0.1'
    
    repositories {
        maven {
            url uri('/home/wiliam/devel/aosp-tools/out/host/gradle/repo')
        }
    }
    
    dependencies {
        compile group: 'com.android.tools.ddms', name: 'ddmlib', version: '22.2.0-SNAPSHOT'
    }
    
    task runMonitor(dependsOn: 'classes', type: JavaExec) {
        main = 'com.github.wiliamsouza.monitor.Monitor'
        classpath = sourceSets.main.runtimeClasspath
    }
    

    build

    to build the project run:

    $ gradle build

    result:

    :compileJava
    warning: [options] bootstrap class path not set in conjunction with -source 1.5
    1 warning
    :processResources UP-TO-DATE
    :classes
    :jar
    :assemble
    :compileTestJava UP-TO-DATE
    :processTestResources UP-TO-DATE
    :testClasses UP-TO-DATE
    :test UP-TO-DATE
    :check UP-TO-DATE
    :build
    
    BUILD SUCCESSFUL
    
    Total time: 4.004 secs
    

    to build the project run:

    $ gradle runMonitor

    Output:

    :compileJava UP-TO-DATE
    :processResources UP-TO-DATE
    :classes UP-TO-DATE
    :runMonitor
    Demo using wait loop to ensure connection to ADB server and then enumerate devices synchronously
    - 0123456789ABCDEF
    
    BUILD SUCCESSFUL
    
    Total time: 4.785 secs
    
    21 Nov 2013
  • An example using Tornado, Motor and MongoDB GridFS to create a basic RESTful api.

    import json
    import yaml
    import http.client
    
    import tornado
    import tornado.web
    import tornado.gen
    import tornado.escape
    
    from bson import json_util
    
    import motor
    import motor.web
    
    client = motor.MotorClient('localhost', 27017).open_sync()
    database = client.automator
    
    
    class PackagesHandler(tornado.web.RequestHandler):
    
        @tornado.web.asynchronous
        @tornado.gen.engine
        def get(self):
            packages = []
            response = {'count': None, 'results': None}
            db = self.settings['database']
            cursor = db.fs.files.find().sort([('_id', -1)])
            while (yield cursor.fetch_next):
                fs = cursor.next_object()
                packages.append(fs)
    
            response['results'] = packages
            self.set_status(http.client.OK)
            self.write(json.dumps(response, default=json_util.default))
            self.finish()
    
        @tornado.web.asynchronous
        @tornado.gen.engine
        def put(self):
            response = {'count': None, 'results': None}
    
            package = self.request.files['package'][0]
            filename = package['filename']
    
            meta = self.request.files['meta'][0]
            metadata = yaml.load(meta['body'])
    
            db = self.settings['database']
            fs = yield motor.Op(motor.MotorGridFS(db).open)
    
            exists = yield motor.Op(fs.exists, {'filename': filename})
            if exists:
                msg = '{0}, already exist.'.format(filename)
                response['results'] = {'detail': msg}
                self.set_status(http.client.CONFLICT)
            else:
                gridfs = yield motor.Op(fs.new_file)
                yield motor.Op(gridfs.write, package['body'])
                yield motor.Op(gridfs.set, 'filename', filename)
                yield motor.Op(gridfs.set, 'content_type', package['content_type'])
                yield motor.Op(gridfs.set, 'metadata', metadata)
                yield motor.Op(gridfs.close)
    
                result = yield motor.Op(db.fs.files.find_one, {'_id': gridfs._id})
                response['results'] = result
                response['count'] = len(result)
                self.set_status(http.client.CREATED)
    
            self.write(json.dumps(response, default=json_util.default))
            self.finish()
    
    
    application = tornado.web.Application([
        (r'/packages', PackagesHandler),
        (r'/packages/(.*)', motor.web.GridFSHandler, {'database': database}),
    ], database=database)
    
    if __name__ == "__main__":
        application.listen(8888)
        tornado.ioloop.IOLoop.instance().start()
    

    Testing

    Lets upload a file:

    $ curl -vv --request PUT --form package=@myfile-0.1.tar.gz --form meta=@myfile.yaml http://localhost:8888/packages

    Result:

    * About to connect() to localhost port 8888 (#0)
    *   Trying 127.0.0.1... connected
    > PUT /packages HTTP/1.1
    > User-Agent: curl/7.22.0 (x86_64-pc-linux-gnu) libcurl/7.22.0 OpenSSL/1.0.1 zlib/1.2.3.4 libidn/1.23 librtmp/2.3
    > Host: localhost:8888
    > Accept: */*
    > Content-Length: 166919
    > Expect: 100-continue
    > Content-Type: multipart/form-data; boundary=----------------------------d2d58ff55a6a
    >
    < HTTP/1.1 100 (Continue)
    < HTTP/1.1 201 Created
    < Date: Thu, 07 Nov 2013 15:45:05 GMT
    < Content-Length: 717
    < Content-Type: text/html; charset=UTF-8
    < Server: TornadoServer/3.1.1
    <
    * Connection #0 to host localhost left intact
    * Closing connection #0
    {"count": null, "results": {"contentType": "application/octet-stream", "chunkSize": 262144, "metadata": {"execute": "adb shell uiautomator myfile", "name": "myfile", "summary": "", "vendors": [[1256, "samsung"]], "version": 0.1, "products": [[26720, "Galaxy Nexus"]], "install": "ant install", "packages": []}, "filename": "myfile-0.1.tar.gz", "length": 166117, "uploadDate": {"$date": 1383839105798}, "_id": {"$oid": "527bb581e169972527029d75"}, "md5": "b2e0fc19135b24c0c73283f52c4c83d5"}}
    

    Check it is realy added to mongodb:

    $ mongofiles --db automator list

    Output:

    connected to: 127.0.0.1
    myfile-0.1.tar.gz 166117
    

    Execute a GET request::

    $ curl -vv --request GET http://localhost:8888/packagesi

    Result:

    * About to connect() to localhost port 8888 (#0)
    *   Trying 127.0.0.1... Connection refused
    * couldn't connect to host
    * Closing connection #0
    curl: (7) couldn't connect to host
    wiliam@waa:~$ curl -vv --request GET http://localhost:8888/packages
    * About to connect() to localhost port 8888 (#0)
    *   Trying 127.0.0.1... connected
    > GET /packages HTTP/1.1
    > User-Agent: curl/7.22.0 (x86_64-pc-linux-gnu) libcurl/7.22.0 OpenSSL/1.0.1 zlib/1.2.3.4 libidn/1.23 librtmp/2.3
    > Host: localhost:8888
    > Accept: */*
    > 
    < HTTP/1.1 200 OK
    < Date: Thu, 07 Nov 2013 16:08:30 GMT
    < Content-Length: 719
    < Etag: "b9bafb261466d514204d124e3054148848c18a8a"
    < Content-Type: text/html; charset=UTF-8
    < Server: TornadoServer/3.1.1
    < 
    * Connection #0 to host localhost left intact
    * Closing connection #0
    {"count": null, "results": {"contentType": "application/octet-stream", "chunkSize": 262144, "metadata": {"execute": "adb shell uiautomator myfile", "name": "myfile", "summary": "", "vendors": [[1256, "samsung"]], "version": 0.1, "products": [[26720, "Galaxy Nexus"]], "install": "ant install", "packages": []}, "filename": "myfile-0.1.tar.gz", "length": 166117, "uploadDate": {"$date": 1383839105798}, "_id": {"$oid": "527bb581e169972527029d75"}, "md5": "b2e0fc19135b24c0c73283f52c4c83d5"}}
    
    07 Nov 2013
  • This will guide you through the steps to write your first uiautomator test using gradle as it build system.

    What is gradle?

    “Gradle combines the power and flexibility of Ant with the dependency management and conventions of Maven into a more effective way to build.”

    For more visit gradle page.

    What is uiautomator?

    “The uiautomator testing framework lets you test your user interface (UI) efficiently by creating automated functional UI testcases that can be run against your app on one or more devices.”

    For more visit uiautomator page.

    Java source code

    Create a directory named bluetooth:

    $ mkdir bluetooth

    Create a java project layout:

    $ cd bluetooth
    $ mkdir -p src/main/java/com/github/wiliamsouza/bluetooth

    Create a java file BluetoothTest.java with the following content:

    package com.github.wiliamsouza.bluetooth;
    
    import java.io.IOException;
    
    import com.android.uiautomator.core.UiObject;
    import com.android.uiautomator.core.UiObjectNotFoundException;
    import com.android.uiautomator.core.UiScrollable;
    import com.android.uiautomator.core.UiSelector;
    import com.android.uiautomator.testrunner.UiAutomatorTestCase;
    
    public class BluetoothTest extends UiAutomatorTestCase {
    
        public void setUp() throws UiObjectNotFoundException, IOException {
            getUiDevice().pressHome();
            String packageName = "com.android.settings";
            String component = packageName + "/.Settings";
            String action = "am start -a android.intent.action.MAIN -n ";
    
            // start settings application
            Runtime.getRuntime().exec(action + component);
    
            UiObject settingsUi = new UiObject(new UiSelector().packageName(packageName));
            assertTrue("Application settings not started", settingsUi.waitForExists(5000));
        }
    
        public void testBluetooth() throws UiObjectNotFoundException {
            UiScrollable scroll = new UiScrollable(new UiSelector().scrollable(true));
            UiObject layout = scroll.getChildByText(new UiSelector().className(android.widget.LinearLayout.class.getName()),"Bluetooth", true);
            UiObject switchBluetooth = layout.getChild(new UiSelector().className(android.widget.Switch.class.getName()));
            assertTrue("Unable to find bluetooth switch object", switchBluetooth.exists());
            switchTo(switchBluetooth, "Bluetooth");
        }
    
        private void switchTo(UiObject switchObject, String name) throws UiObjectNotFoundException {
            // Before start test ensure switch is off
            if (switchObject.isChecked()) {
                switchObject.click();
                sleep(3000);
            }
            
            switchObject.click();
            sleep(3000);
           	assertTrue("Unable to turn " + name + " on", switchObject.isChecked());
           	
        	switchObject.click();
            sleep(3000);
           	assertFalse("Unable to turn " + name + " off", switchObject.isChecked());
        }
    }
    

    Gradle

    Create a build.gradle with the following content:

    apply plugin: 'java'
    apply plugin: 'idea'
    
    sourceCompatibility = 1.5
    targetCompatibility = 1.5
    
    version = '0.1'
    
    project.ext {
        dexDir = new File('build/dex')
        distDir = new File('./dist')
    }
    
    repositories {
        mavenCentral()
    }
    
    dependencies {
        compile fileTree(dir: androidSdkHome + '/platforms/' + androidSdkTarget, include: '*.jar')
        compile group: 'junit', name: 'junit', version: '4.11'
    }
    
    jar {
        doLast {
            tasks.dex.execute()
        }
    }
    
    task dex(dependsOn: jar, type:Exec) {
        println 'Building dex...'
        project.dexDir.mkdirs()
        workingDir '.'
        commandLine androidSdkHome + '/' + androidSdkBuildToolsDir + '/' + 'dx', '--dex', '--no-strict', '--output=' + buildDir +'/dex/' + project.name + '.jar', jar.archivePath
        doLast {
            tasks.dist.execute()
        }
    }
    
    task dist(dependsOn:dex, type:Copy) {
        project.distDir.mkdirs()
        from(project.dexDir)
        into(project.distDir)
        include('*.jar')
    }
    

    Configure

    Now lets configure the build system, Create a file gradle.properties containing:

    androidSdkHome=/home/wiliam/bin/android-sdk-linux
    androidSdkTarget=android-18
    androidSdkBuildToolsDir=build-tools/18.1.1
    
    • androidSdkHome – Point to the root directory of android SDK.
    • androidSdkTarget – Is the target, points to a directory inside platforms.
    • androidSdkBuildToolsDir – Location of dx program.

    Build

    $ gradle build

    Install

    $ adb push dist/bluetooth.jar /data/local/tmp/

    Run

    $ adb shell uiautomator runtest bluetooth.jar -c com.github.wiliamsouza.bluetooth.BluetoothTest
    30 Oct 2013
  • OAuth 2.0 spec RFC 6749 define that client_id should be in the format %x20-7E.

    What this mean?

    Let me explain using some Python code.

    1 >>> chr(int(0x20)), chr(int(0x7E))
    2 (' ', '~')
    

    But wait it is a range of characters.

    1 >>> characters = []
    2 >>> for char in range(int(0x20), int(0x7E)):
    3 ...     characters.append(chr(char))
    4 ... 
    5 >>> characters
    6 [' ', '!', '"', '#', '$', '%', '&', "'", '(', ')', '*', '+', ',', '-', '.', '/', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ':', ';', '<', '=', '>', '?', '@', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '[', '\\', ']', '^', '_', '`', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '{', '|', '}']
    7 >>> ''.join(characters)
    8 ' !"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}'
    9 >>>
    

    So you can use the following code to create client_id for your RESTful API.

    1 >>> import random
    2 >>> rand = random.SystemRandom()
    3 >>> length=30
    4 >>> ''.join(rand.choice(characters) for x in range(length))
    5 '*?>tN7a00_FMu{DKMxI<!->&I4GuU7'
    6 >>> 
    

    This code will create a client_id with 30 characters length.

    27 Feb 2013
  • Django 1.5 adds a new feature named “configurable User model” and here is one of the ways you can use it.

     1 class Account(AbstractBaseUser):
     2     email = models.EmailField(max_length=40, unique=True, db_index=True)
     3     is_business = BooleanField(default=False)
     4     #... Other shared info is added here.
     5 
     6     USERNAME_FIELD = 'email'
     7     REQUIRED_FIELDS = ['is_business']
     8 
     9     # This is deprecated in 1.5 but we use it to get profiles extension
    10     def get_profile():
    11         if self.is_business:
    12             return BusinessProfile.objects.get(email=self.email) # or self.business_profile
    13         return UserProfile.objects.get(email=self.email) # or self.user_profile
    14 
    15 
    16 class BusinessProfile(models.Model):
    17     account = models.ForeignKey(Account, related_name='business_profile')
    18     #...
    19 
    20 
    21 class UserProfile(models.Model):
    22     account = models.ForeignKey(Account, related_name='user_profile')
    23     #...
    
    06 Dec 2012
  • It demonstrates how to connect to systemd dbus signal. run this and stop/start crond using systemctl.

     1 import dbus
     2 import dbus.mainloop.glib
     3 dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
     4 
     5 bus = dbus.SystemBus()
     6 
     7 proxy = bus.get_object(
     8     'org.freedesktop.systemd1',
     9     '/org/freedesktop/systemd1'
    10     )
    11 
    12 interface = dbus.Interface(proxy, 'org.freedesktop.systemd1.Manager')
    13 
    14 interface.Subscribe()
    15 
    16 def on_properties_changed(*args, **kargs):
    17     print 'Status Changed....'
    18     print args
    19     print kargs
    20 
    21 properties_proxy = bus.get_object(
    22     'org.freedesktop.systemd1',
    23     interface.GetUnit('crond.service')
    24     )
    25 
    26 properties_interface = dbus.Interface(properties_proxy,
    27                                       'org.freedesktop.DBus.Properties')
    28 
    29 properties_interface.connect_to_signal('PropertiesChanged',
    30                                        on_properties_changed)
    31 
    32 import gobject
    33 
    34 loop = gobject.MainLoop()
    35 loop.run()
    
    07 Apr 2011
  • chat recently played tracks from last.fm.

    import xchat
    import feedparser
    
    LASTFM_USER = 'wiliamsouza83'
    URL = 'http://ws.audioscrobbler.com/1.0/user/%s/recenttracks.rss' % LASTFM_USER
    
    def do_request(word, word_eol, userdata):
        if len(word) < 2:
            xchat.command('help LISTEN')
        elif not isinstance(int(word[1]), int):
            print 'Second arg must be an int!'
        else:
            rss = feedparser.parse(URL)
            for n in range(int(word[1])):
            xchat.command('me listen %s' % rss['entries'][n]['link'])
        return xchat.EAT_ALL
    
    xchat.hook_command('LISTEN',
                       do_request,
                       help='/LISTEN [number]. Show the latest played track from last.fm.')
    
    18 Oct 2007