Export Call Log Example

This example shows how to export call log entries on a suitably equipped device.

A screenshot of the application.

We begin by importing the classes and modules needed by our application.

from java.io import File, FileWriter
from java.lang import Runnable, Thread
from java.util import HashMap
from android.content import Context
from android.net import Uri
from android.os import Environment, Handler, Looper
from android.provider import CallLog
from android.view import View, ViewGroup
from android.widget import Button, ScrollView, TextView

We also use a custom activity class and a convenience widget from the serpentine package.

from serpentine.activities import Activity
from serpentine.widgets import VBox

The activity class represents the application. It will be used to present a graphical interface to the user.

class ExportCallLogActivity(Activity):

    __interfaces__ = [Runnable, View.OnClickListener]
    
    ADD_LOG_TEXT = 0
    STOP_WORKER = 1

The initialisation method only needs to call the corresponding method in the base class.

    def __init__(self):
    
        Activity.__init__(self)

The onCreate method is called when the activity is created. Our implementation creates an output directory to store entries in, and constructs a user interface.

    def onCreate(self, bundle):
    
        Activity.onCreate(self, bundle)
        
        self.outputDir = self.createDir()
        
        self.exportButton = Button(self)
        self.exportButton.setText("Export call log")
        self.exportButton.setEnabled(True)
        self.exportButton.setOnClickListener(self)
        
        scrollView = ScrollView(self)
        self.textView = TextView(self)
        scrollView.addView(self.textView)

The widgets are added to a convenience widget from the serpentine package.

        vbox = VBox(self)
        vbox.addView(self.exportButton)
        vbox.addView(scrollView)

The VBox widget is used as the main user interface in the activity.

        self.setContentView(vbox)

We also create a handler that will receive messages sent from the worker thread and initialise an attribute that records whether the worker thread is running.

        self.handler = MessageHandler(Looper.getMainLooper(), self)
        self.running = False

We implement the following method to implement the View.OnClickListener interface. The method is called when the user clicks the send button.

    def onClick(self, view):
    
        if not self.running:
            self.exportButton.setText("Stop")
            self.textView.setText("")
            
            self.thread = Thread(self)
            self.thread.start()
        else:
            self.stopWorker()
    
    @args(void, [str])
    def addLogText(self, new_text):
    
        self.textView.setText(new_text)
    
    def stopWorker(self):
    
        self.running = False
        self.thread.join()
        self.exportButton.setText("Export call log")
        
        if self.outputDir != "":
            self.textView.setText("Entries saved in " + self.outputDir)

We define methods that run in the worker thread. The first of these sets an instance attribute to indicate that it is running and iterates over the events to be exported.

    def run(self):
    
        resolver = self.getContentResolver()
        cursor = resolver.query(CallLog.Calls.CONTENT_URI, None, None, None, None)
        
        self.running = True
        l = cursor.getCount()
        i = 0
        
        while i < l and self.running:
            cursor.moveToPosition(i)
            columns = cursor.getColumnCount()
            
            d = {}
            
            j = 0
            while j < columns:
                name = cursor.getColumnName(j)
                value = cursor.getString(j)
                
                # Avoid storing null values in the dictionary to prevent later
                # problems extracting them.
                if value != None:
                    d[name] = value
                
                j += 1
            i += 1
            
            self.saveFile(d["date"], d)
            
            message = self.handler.obtainMessage(
                self.ADD_LOG_TEXT, "Entries saved: " + str(i))
            message.sendToTarget()
        
        message = self.handler.obtainMessage(self.STOP_WORKER, None)
        message.sendToTarget()

The following method creates a directory for this example in the external storage.

    @args(str, [])
    def createDir(self):
    
        if Environment.getExternalStorageState() != Environment.MEDIA_MOUNTED:
            return ""
        
        storageDir = Environment.getExternalStoragePublicDirectory(
            Environment.DIRECTORY_DOWNLOADS)
        
        dir = File(storageDir, "ExportCallLog")
        if not dir.exists():
            if not dir.mkdirs():
                return ""
        
        return str(dir)

The saveFile method is a simple utility method that creates and writes a file in the device's external storage area, creating a directory to hold the file if required.

    @args(void, [str, HashMap(str, str)])
    def saveFile(self, name, data):
    
        f = File(self.outputDir, name)
        
        stream = FileWriter(f, False)
        
        for key in data.keySet().toArray():
            
            stream.write(key + "\n")
            value = data[key]
            if value != None:
                stream.write(value + "\n\n")
        
        stream.flush()
        stream.close()


class MessageHandler(Handler):

    @args(void, [Looper, ExportCallLogActivity])
    def __init__(self, looper, activity):
    
        Handler.__init__(self, looper)
        self.activity = activity
    
    def handleMessage(self, inputMessage):
    
        if inputMessage.what == ExportCallLogActivity.ADD_LOG_TEXT:
            self.activity.addLogText(str(inputMessage.obj))
        
        elif inputMessage.what == ExportCallLogActivity.STOP_WORKER:
            self.activity.stopWorker()

Files