<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Wolf Paulus &#187; Journal</title>
	<atom:link href="http://wolfpaulus.com/category/journal/feed" rel="self" type="application/rss+xml" />
	<link>http://wolfpaulus.com</link>
	<description>Journal</description>
	<lastBuildDate>Sun, 18 Dec 2011 22:10:21 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.3.1</generator>
		<item>
		<title>Android and OCR</title>
		<link>http://wolfpaulus.com/journal/android-and-ocr?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=android-and-ocr</link>
		<comments>http://wolfpaulus.com/journal/android-and-ocr#comments</comments>
		<pubDate>Sun, 18 Dec 2011 22:10:21 +0000</pubDate>
		<dc:creator>Wolf Paulus</dc:creator>
				<category><![CDATA[Android]]></category>
		<category><![CDATA[Journal]]></category>
		<category><![CDATA[android]]></category>
		<category><![CDATA[Linux]]></category>
		<category><![CDATA[OCR]]></category>
		<category><![CDATA[Tesseract]]></category>
		<category><![CDATA[Tornado]]></category>
		<category><![CDATA[Ubuntu]]></category>

		<guid isPermaLink="false">http://wolfpaulus.com/?p=1725</guid>
		<description><![CDATA[I&#8217;m still remembering it well, the first piece of software I wrote when I came to the US was a de-skewing algorithm. Deskewing an image helps a lot, if you want to do OCR, OMR, barcode detect, or just improve the readability of scanned images. At the time, I was working for a small software [...]]]></description>
			<content:encoded><![CDATA[<div>
I&#8217;m still remembering it well, the first piece of software I wrote when I came to the US was a de-skewing algorithm. Deskewing an image helps a lot, if you want to do OCR, OMR, barcode detect, or just improve the readability of scanned images.<br />
At the time, I was working for a small software company, developing TeleForm, an application that reads data from paper forms and stores that data in previously created databases. The <a href="http://www.cardiff.com/products/teleform/" target="_blank">Cardiff TeleForm</a> product was later re-branded Verity-TeleForm for a brief period in 2004 and 2005 when Verity Inc. acquired Cardiff Software. In 2005, when Autonomy acquired Verity, the Cardiff brand was reintroduced as Autonomy Cardiff (<a href="http://www.cardiff.com" target="_blank">http://www.cardiff.com</a>); more recently, Autonomy was acquired by HP.</p>
<p style="text-align: left;"><div class='et-box et-shadow'>
					<div class='et-box-content'>Optical character recognition, usually abbreviated to <strong>OCR</strong>, is the mechanical or electronic translation of scanned images of handwritten, typewritten, or printed text into machine-encoded text.</p>
<p style="text-align: left;">Image <strong>Deskew</strong> is the process of removing skew from images (especially bitmaps created using a scanner). Skew is an artifact that can occur in scanned images because of the camera being misaligned, imperfections in the scanning or surface, or simply because the paper was not placed completely flat when scanned.</div></div></p>
<p>Now most of the data entry or origination happens on the Web, where most of the forms processing has been moved to as well, i.e. OCR hasn’t been in vogue for quite a while. However, the popularity of smartphones, combined with built-in high-quality cameras has created a new category of mobile applications, benefiting greatly from OCR. Take Word-Lens (<a href="http://questvisual.com" target="_blank">http://questvisual.com</a>) as an example: an augmented reality translation application that tries to find out what the letters are in an image and then looks in a dictionary, to eventually draws the words back on the screen in translation.</p>
<h2>On Device or In The Cloud ?</h2>
<p>Before deciding on an OCR library, one needs to decide, where the OCR process should take place: on the Smartphone or in the Cloud. Each approach has its advantages.<br />
On device OCR can be performed without requiring an Internet connection and instead of sending a photo, which can potentially be huge (many phones have 8 or 12 Mega-Pixel cameras now), the text is recognized by an on-board OCR-engine.<br />
<span id="more-1725"></span>However, OCR-libraries tend to be large, i.e. the mobile application will be of considerable size. Depending on the amount of text that needs to be recognized and the available data transfer speed, a cloud-service may provide the result faster. A cloud-service can be updated more easily but individually optimizing (training) an OCR engine may work better when done locally on the device.</p>
<h2>Which OCR Library to choose ?</h2>
<ul>
<li>Wikipedia has a “non-exhaustive” but still very broad comparison of optical character recognition software here: <a href="http://en.wikipedia.org/wiki/ List_of_optical_character_recognition_software" target="_blank">http://en.wikipedia.org/wiki/ List_of_optical_character_recognition_software</a></li>
<li>A comparison of some of the more popular OCR-Engines can be found here: <a href="http://www.freewaregenius.com/2011/11/01/how-to-extract-text-from-images-a- comparison-of-free-ocr-tools/" target="_blank">http://www.freewaregenius.com/2011/11/01/how-to-extract-text-from-images-a- comparison-of-free-ocr-tools/</a></li>
<li>Linux OCR software comparison with a strong focus on accuracy:<a href=" http://www.splitbrain.org/blog/2010-06/15-linux_ocr_software_comparison" target="_blank"> http://www.splitbrain.org/blog/2010-06/15-linux_ocr_software_comparison</a></li>
</ul>
<p>After taking a closer look at the all comparisons, <a href="http://en.wikipedia.org/wiki/Tesseract_(software)" target="_blank">Tesseract</a> stands out. It provides good accuracy, it’s open-source and Apache-Licensed, and has broad language support. It was created by HP and is now developed by Google.</p>
<p>Also, since Tesseract is open source and Apache- Licensed, we can take the source and port it to the Android platform, or put it on a Web-server to run our very own Cloud-service.<br />
<div class='et-box et-shadow'>
					<div class='et-box-content'>A Tesseract is a four- dimensional object, much like a cube is a three-dimensional object. A square has two dimensions. You can make a cube from six squares. A cube has three dimensions. The tesseract is made in the same way, but in four dimensions.</div></div></p>
<h2>1. Tesseract</h2>
<p>The Tesseract OCR engine was developed at Hewlett Packard Labs and is currently sponsored by Google. It was among the top three OCR engines in terms of character accuracy in 1995. <a href="http://code.google.com/p/tesseract-ocr/" target="_blank">http://code.google.com/p/tesseract-ocr/</a></p>
<h2>1.1. Running Tesseract locally on a Mac</h2>
<p>Like with so make other Unix and Linux tools, <em>Homebrew</em> (<a href="http://mxcl.github.com/homebrew/" target="_blank">http://mxcl.github.com/homebrew/</a>) is the easiest and most flexible way to install the UNIX tools Apple didn&#8217;t include with OS X. Once Homebrew is installed (<a href="https://github.com/mxcl/homebrew/wiki/installation" target="_blank">https://github.com/mxcl/homebrew/wiki/installation</a>), Tesseract can be installed on OS X as easy as:<br />
<code>  $ brew install tessertact</code><br />
Once installed,<br />
<code>  $ brew info tesseract</code>   will return something like this:<br />
<code><br />
tesseract 3.00</p>
<p>http://code.google.com/p/tesseract-ocr/</p>
<p>Depends on: libtiff<br />
/usr/local/Cellar/tesseract/3.00 (316 files, 11M)<br />
Tesseract is an OCR (Optical Character Recognition) engine.<br />
The easiest way to use it is to convert the source to a Grayscale tiff:<br />
  `convert source.png -type Grayscale terre_input.tif`<br />
then run tesseract:<br />
  `tesseract terre_input.tif output`</p>
<p>http://github.com/mxcl/homebrew/commits/master/Library/Formula/tesseract.rb</p>
<p></code><br />
Tesseract doesn&#8217;t come with a GUI and instead runs from a command-line interface. To OCR a TIFF-encoded image located on your desktop, you would do something like this:<br />
<code>$ tesseract ~/Desktop/cox.tiff ~/Desktop/cox</code><br />
Using the image below, Tesseract wrote with perfect accuracy the resulting text into<br />
<code>~/Desktop/cox.txt</code><br />
<img class="aligncenter size-full wp-image-1744" title="ocr1" src="http://wolfpaulus.com/wp-content/uploads/2011/12/ocr11.png" alt="" width="625" height="172" /></p>
<p>There are at least two projects, providing a GUI-front-end for Tesseract on OS X</p>
<ol>
<li>TesseractGUI, a native OSX client: <a href="http://download.dv8.ro/files/TesseractGUI/" target="_blank">http://download.dv8.ro/files/TesseractGUI/</a></li>
<li>VietOCR, a Java Client: <a href="http://vietocr.sourceforge.net/" target="_blank">http://vietocr.sourceforge.net/</a></li>
</ol>
<p><div id="attachment_1812" class="wp-caption aligncenter" style="width: 635px"><img src="http://wolfpaulus.com/wp-content/uploads/2011/12/tessgui.png" alt="" title="tessgui" width="625" height="330" class="size-full wp-image-1812" /><p class="wp-caption-text">TesseractGUI, a native OSX Client for Tesseract</p></div><br />
<div id="attachment_1747" class="wp-caption aligncenter" style="width: 635px"><img class="size-full wp-image-1747" title="s3" src="http://wolfpaulus.com/wp-content/uploads/2011/12/s3.png" alt="" width="625" height="380" /><p class="wp-caption-text">VietOCR, a Java Client for Tesseract</p></div></p>
<h2>1.2. Running Tesseract as a Could-Service on a Linux Server</h2>
<p>One of the fastest and easiest ways to deploy Tesseract as a Web-service, uses Tornado (http://www.tornadoweb.org/), an open source (Apache Licensed) Python non-blocking web server. Since Tesseract accepts TIFF encoded images but our Cloud-Service should rather work with the more popular JPEG image format, we also need to deploy the free Python Imaging Library (http://www.pythonware.com/products/pil/), license terms are here: http://www.pythonware.com/products/pil/license.htm</p>
<p>The deployment on Ubuntu 11.10 64-bit server looks something like this:</p>
<pre>sudo apt-get install python-tornado
sudo apt-get install python-imaging
sudo apt-get install tesseract-ocr</pre>
<h2>1.2.1. The HTTP Server-Script for port 8080</h2>
<pre class="brush: python; title: ; notranslate">
#!/usr/bin/env python
import tornado.httpserver
import tornado.ioloop
import tornado.web
import pprint
import Image
from tesseract import image_to_string
import StringIO
import os.path
import uuid

class MainHandler(tornado.web.RequestHandler):
    def get(self):
        self.write('&lt;/pre&gt;
&lt;form action=&quot;/&quot; method=&quot;post&quot; enctype=&quot;multipart/form-data&quot;&gt;' '
&lt;input type=&quot;file&quot; name=&quot;the_file&quot; /&gt;' '
&lt;input type=&quot;submit&quot; value=&quot;Submit&quot; /&gt;' '&lt;/form&gt;
&lt;pre class=&quot;prettyprint&quot;&gt;')

    def post(self):
        self.set_header(&quot;Content-Type&quot;, &quot;text/html&quot;)
	self.write(&quot;&quot;) # create a unique ID file
        tempname = str(uuid.uuid4()) + &quot;.jpg&quot;
        myimg = Image.open(StringIO.StringIO(self.request.files.items()[0][1][0  ['body']))
        myfilename = os.path.join(os.path.dirname(__file__),&quot;static&quot;,tempname); 

        # save image to file as JPEG
        myimg.save(myfilename) 

        # do OCR, print result
        self.write(image_to_string(myimg))
        self.write(&quot;&quot;)

settings = {
    &quot;static_path&quot;: os.path.join(os.path.dirname(__file__), &quot;static&quot;),
}

application = tornado.web.Application([
    (r&quot;/&quot;, MainHandler),
], **settings)

if __name__ == &quot;__main__&quot;:
    http_server = tornado.httpserver.HTTPServer(application)
    http_server.listen(8080)
    tornado.ioloop.IOLoop.instance().start()
</pre>
<p>The Server receives a JPEG image file and stores it locally in the ./static directory, before calling image_to_string, which is defined in the Python script below:</p>
<h2>1.2.2. image_to_string function implementation</h2>
<pre class="brush: python; title: ; notranslate">#!/usr/bin/env python

tesseract_cmd = 'tesseract'

import Image
import StringIO
import subprocess
import sys
import os

__all__ = ['image_to_string']

def run_tesseract(input_filename, output_filename_base, lang=None, boxes=False):
    '''
    runs the command:
        `tesseract_cmd` `input_filename` `output_filename_base`

    returns the exit status of tesseract, as well as tesseract's stderr output

    '''

    command = [tesseract_cmd, input_filename, output_filename_base]

    if lang is not None:
        command += ['-l', lang]

    if boxes:
        command += ['batch.nochop', 'makebox']

    proc = subprocess.Popen(command,
            stderr=subprocess.PIPE)
    return (proc.wait(), proc.stderr.read())

def cleanup(filename):
    ''' tries to remove the given filename. Ignores non-existent files '''
    try:
        os.remove(filename)
    except OSError:
        pass

def get_errors(error_string):
    '''
    returns all lines in the error_string that start with the string &quot;error&quot;

    '''

    lines = error_string.splitlines()
    error_lines = tuple(line for line in lines if line.find('Error') &gt;= 0)
    if len(error_lines) &gt; 0:
        return '\n'.join(error_lines)
    else:
        return error_string.strip()

def tempnam():
    ''' returns a temporary file-name '''

    # prevent os.tmpname from printing an error...
    stderr = sys.stderr
    try:
        sys.stderr = StringIO.StringIO()
        return os.tempnam(None, 'tess_')
    finally:
        sys.stderr = stderr

class TesseractError(Exception):
    def __init__(self, status, message):
        self.status = status
        self.message = message
        self.args = (status, message)

def image_to_string(image, lang=None, boxes=False):
    '''
    Runs tesseract on the specified image. First, the image is written to disk,
    and then the tesseract command is run on the image. Resseract's result is
    read, and the temporary files are erased.

    '''

    input_file_name = '%s.bmp' % tempnam()
    output_file_name_base = tempnam()
    if not boxes:
        output_file_name = '%s.txt' % output_file_name_base
    else:
        output_file_name = '%s.box' % output_file_name_base
    try:
        image.save(input_file_name)
        status, error_string = run_tesseract(input_file_name,
                                             output_file_name_base,
                                             lang=lang,
                                             boxes=boxes)
        if status:
            errors = get_errors(error_string)
            raise TesseractError(status, errors)
        f = file(output_file_name)
        try:
            return f.read().strip()
        finally:
            f.close()
    finally:
        cleanup(input_file_name)
        cleanup(output_file_name)

if __name__ == '__main__':
    if len(sys.argv) == 2:
        filename = sys.argv[1]
        try:
            image = Image.open(filename)
        except IOError:
            sys.stderr.write('ERROR: Could not open file &quot;%s&quot;\n' % filename)
            exit(1)
        print image_to_string(image)
    elif len(sys.argv) == 4 and sys.argv[1] == '-l':
        lang = sys.argv[2]
        filename = sys.argv[3]
        try:
            image = Image.open(filename)
        except IOError:
            sys.stderr.write('ERROR: Could not open file &quot;%s&quot;\n' % filename)
            exit(1)
        print image_to_string(image, lang=lang)
    else:
        sys.stderr.write('Usage: python tesseract.py [-l language] input_file\n')
        exit(2)
</pre>
<h2>1.2.3. The Service deploy/start Script</h2>
<pre class="brush: bash; title: ; notranslate">
description  &quot;OCR WebService&quot;

start on runlevel [2345]
stop on runlevel [!2345]

pre-start script
mkdir /tmp/ocr

mkdir /tmp/ocr/static

cp /usr/share/ocr/*.py /tmp/ocr

end script
exec /tmp/ocr/tesserver.py
</pre>
<p>After the service has been started, it can be accessed through a Web browser like shown here: http://proton.techcasita.com:8080 I’m currently running tesseract 3.01 on Ubuntu Linux 11.10 64-bit, please be gentle, it runs on an Intel Atom CPU 330 @ 1.60GHz, 4 cores (typically found in Netbooks) <img class="aligncenter size-full wp-image-1753" title="s4" src="http://wolfpaulus.com/wp-content/uploads/2011/12/s4.png" alt="" width="600" height="472" />The HTML encoded result looks something like this:</p>
<pre class="brush: xml; title: ; notranslate">
&lt;html&gt;&lt;body&gt;Contact Us
www. cox.com
Customer Serv 760-788-9000
Repair 76Oâ€”788~71O0
Cox Telephone 888-222-7743&lt;/body&gt;&lt;/html&gt;
</pre>
<h2>1.3 Accessing the Tesseract Cloud-Service from Android</h2>
<p>The <em>OCRTaskActivity</em> below utilizes Android&#8217;s built-in <em>AsyncTask</em> as well as Apache Software Foundation&#8217;s HttpComponent library <em>HttpClient</em>4.1.2, available here: <a href="http://hc.apache.org/httpcomponents-client-ga/index.html" target="_blank">http://hc.apache.org/httpcomponents-client-ga/index.html</a> OCRTaskActivity expects the image to be passed in as the Intent Extra &#8220;ByteArray&#8221; of type ByteArray. The OCR result is returned to the calling Activity as OCR_TEXT, like shown here:</p>
<pre>setResult(Activity.RESULT_OK, getIntent().putExtra("OCR_TEXT", result));</pre>
<pre class="brush: java; title: ; notranslate">
import android.app.Activity;
import android.graphics.BitmapFactory;
import android.os.AsyncTask;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.ImageView;
import android.widget.ProgressBar;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.mime.HttpMultipartMode;
import org.apache.http.entity.mime.MultipartEntity;
import org.apache.http.entity.mime.content.ByteArrayBody;
import org.apache.http.entity.mime.content.StringBody;
import org.apache.http.impl.client.DefaultHttpClient;

import java.io.BufferedReader;
import java.io.InputStreamReader;

public class OCRTaskActivity extends Activity {
    private static String LOG_TAG = OCRAsyncTaskActivity.class.getSimpleName();
    private static String[] URL_STRINGS = {&quot;http://proton.techcasita.com:8080&quot;};

    private byte[] mBA;
    private ProgressBar mProgressBar;

    @Override
    public void onCreate(final Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.ocr);
        mBA = getIntent().getExtras().getByteArray(&quot;ByteArray&quot;);
        ImageView iv = (ImageView) findViewById(R.id.ImageView);
        iv.setImageBitmap(BitmapFactory.decodeByteArray(mBA, 0, mBA.length));
        mProgressBar = (ProgressBar) findViewById(R.id.progressBar);
        OCRTask task = new OCRTask();
        task.execute(URL_STRINGS);
    }

    private class OCRTask extends AsyncTask {
        @Override
        protected String doInBackground(final String... urls) {
            String response = &quot;&quot;;
            for (String url : urls) {
                try {
                    response = executeMultipartPost(url, mBA);
                    Log.v(LOG_TAG, &quot;Response:&quot; + response);
                    break;
                } catch (Throwable ex) {
                    Log.e(LOG_TAG, &quot;error: &quot; + ex.getMessage());
                }
            }
            return response;
        }

        @Override
        protected void onPostExecute(final String result) {
            mProgressBar.setVisibility(View.GONE);
            setResult(Activity.RESULT_OK, getIntent().putExtra(&quot;OCR_TEXT&quot;, result));
            finish();
        }
    }

    private String executeMultipartPost(final String stringUrl, final byte[] bm) throws Exception {
        HttpClient httpClient = new DefaultHttpClient();
        HttpPost postRequest = new HttpPost(stringUrl);
        ByteArrayBody bab = new ByteArrayBody(bm, &quot;the_image.jpg&quot;);
        MultipartEntity reqEntity = new MultipartEntity(HttpMultipartMode.BROWSER_COMPATIBLE);
        reqEntity.addPart(&quot;uploaded&quot;, bab);
        reqEntity.addPart(&quot;name&quot;, new StringBody(&quot;the_file&quot;));
        postRequest.setEntity(reqEntity);
        HttpResponse response = httpClient.execute(postRequest);
        BufferedReader reader = new BufferedReader(new InputStreamReader(response.getEntity().getContent(), &quot;UTF-8&quot;));
        String sResponse;
        StringBuilder s = new StringBuilder();

        while ((sResponse = reader.readLine()) != null) {
            s = s.append(sResponse).append('\n');
        }
        int i = s.indexOf(&quot;body&quot;);
        int j = s.lastIndexOf(&quot;body&quot;);
        return s.substring(i + 5, j - 2);
    }
}
</pre>
<div id="attachment_1756" class="wp-caption aligncenter" style="width: 437px"><img class="size-full wp-image-1756" title="ocrclient" src="http://wolfpaulus.com/wp-content/uploads/2011/12/ocrclient.png" alt="" width="427" height="600" /><p class="wp-caption-text">This sample Android app has an Activity that sends a small JPEG image to the Cloud-Service, which is running the Tesseract OCR engine.</p></div>
<pre></pre>
<h2>1.4. Building a Tesseract native Android Library to be bundled with an Android App</h2>
<p>This approach allow an Android application to perform OCR even without a network connection. I.e. the OCR engine is on-board. There are currently two source-bases to start from, the original Tesseract project here:</p>
<ol>
<li>Tesseract Tools for Android is a set of Android APIs and build files for the Tesseract OCR and Leptonica image processing libraries:
<pre>svn checkout http://tesseract-android-tools.googlecode.com/svn/trunk/ tesseract-android-tools</pre>
</li>
<li>A fork of Tesseract Tools for Android (tesseract-android-tools) that adds some additional functions:
<pre>git clone git://github.com/rmtheis/tess-two.git</pre>
</li>
</ol>
<p>&#8230; I went with option 2.</p>
<h3>1.4.1. Building the native lib</h3>
<p>Each project can be build with the same build steps (see below) and neither works with Android’s NDK r7. However, going back to NDK r6b solved that problem. Here are the build steps. It takes a little while, even on a fast machine.</p>
<pre class="brush: bash; title: ; notranslate">
cd &lt;project-directory&gt;/tess-two
export TESSERACT_PATH=${PWD}/external/tesseract-3.01
export LEPTONICA_PATH=${PWD}/external/leptonica-1.68
export LIBJPEG_PATH=${PWD}/external/libjpeg
ndk-build
android update project --path .
ant release
</pre>
</div>
<p>The build-steps create the native libraries in the <em>libs/armabi</em> and <em>libs/armabi-v7a</em> directories.</p>
<p>The tess-two project can now be included as a library-project into an Android project and with the JNI layer in place, calling into the native OCR library now looks something like this:<br />
<div id="attachment_1825" class="wp-caption aligncenter" style="width: 361px"><img src="http://wolfpaulus.com/wp-content/uploads/2011/12/libsfolder.png" alt="" title="libsfolder" width="351" height="289" class="size-full wp-image-1825" /><p class="wp-caption-text">JNI and Native Libraries</p></div></p>
<h3>1.4.2. Developing a simple Android App with built-in OCR capabilities</h3>
<pre class="brush: bash; title: ; notranslate">
...
TessBaseAPI baseApi = new TessBaseAPI();
baseApi.init(DATA_PATH, LANG);
baseApi.setImage(bitmap);
String recognizedText = baseApi.getUTF8Text();
baseApi.end();
...
</pre>
<h4>1.4.2.1. Libraries / TrainedData / App Size</h4>
<p>The native libraries are about 3 MBytes in size. Additionally, a language and font depending training resource files is needed.<br />
The eng.traineddata file (e.g. available with the desktop version of Tesseract) is placed into the main android’s assers/tessdata folder and deployed with the application, adding another 2 MBytes to the app. However, due to compression, the actual downloadable Android application is &#8220;only&#8221; about 4.1 MBytes.</p>
<p>During the first start of the application, the eng.traineddata resource file is copied to the phone’s SDCard. </p>
<p>The ocr() method for the sample app may look something like this:</p>
<pre class="brush: java; title: ; notranslate">
protected void ocr() { 

        BitmapFactory.Options options = new BitmapFactory.Options();
        options.inSampleSize = 2;
        Bitmap bitmap = BitmapFactory.decodeFile(IMAGE_PATH, options); 

        try {
            ExifInterface exif = new ExifInterface(IMAGE_PATH);
            int exifOrientation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL); 

            Log.v(LOG_TAG, &quot;Orient: &quot; + exifOrientation); 

            int rotate = 0;
            switch (exifOrientation) {
                case ExifInterface.ORIENTATION_ROTATE_90:
                    rotate = 90;
                    break;
                case ExifInterface.ORIENTATION_ROTATE_180:
                    rotate = 180;
                    break;
                case ExifInterface.ORIENTATION_ROTATE_270:
                    rotate = 270;
                    break;
            } 

            Log.v(LOG_TAG, &quot;Rotation: &quot; + rotate); 

            if (rotate != 0) { 

                // Getting width &amp; height of the given image.
                int w = bitmap.getWidth();
                int h = bitmap.getHeight(); 

                // Setting pre rotate
                Matrix mtx = new Matrix();
                mtx.preRotate(rotate); 

                // Rotating Bitmap
                bitmap = Bitmap.createBitmap(bitmap, 0, 0, w, h, mtx, false);
                // tesseract req. ARGB_8888
                bitmap = bitmap.copy(Bitmap.Config.ARGB_8888, true);
            } 

        } catch (IOException e) {
            Log.e(LOG_TAG, &quot;Rotate or coversion failed: &quot; + e.toString());
        } 

        ImageView iv = (ImageView) findViewById(R.id.image);
        iv.setImageBitmap(bitmap);
        iv.setVisibility(View.VISIBLE); 

        Log.v(LOG_TAG, &quot;Before baseApi&quot;); 

        TessBaseAPI baseApi = new TessBaseAPI();
        baseApi.setDebug(true);
        baseApi.init(DATA_PATH, LANG);
        baseApi.setImage(bitmap);
        String recognizedText = baseApi.getUTF8Text();
        baseApi.end(); 

        Log.v(LOG_TAG, &quot;OCR Result: &quot; + recognizedText); 

        // clean up and show
        if (LANG.equalsIgnoreCase(&quot;eng&quot;)) {
            recognizedText = recognizedText.replaceAll(&quot;[^a-zA-Z0-9]+&quot;, &quot; &quot;);
        }
        if (recognizedText.length() != 0) {
            ((TextView) findViewById(R.id.field)).setText(recognizedText.trim());
        }
    }
</pre>
<h2>OCR on Android</h2>
<p>The popularity of smartphones, combined with built-in high-quality cameras has created a new category of mobile applications, benefiting greatly from OCR.</p>
<p>OCR is very mature technology with a broad range of available libraries to chose from. There are Apache and BSD licensed, fast and accurate solutions available from the open-source community, I have taken a closer look at Tesseract, which is developed by HP and Google.</p>
<p>Tesseract can be used to build a Desktop application, a CloudService, and even baked into a mobile Android application, performing on-board OCR. All three variation of OCR with the Tesseract library have been demonstrated above.  </p>
<p>Focussing on mobile applications, however, it became very clear that even on phones with a 5MP camera, the accuracy of the results still vary greatly, depending on lighting conditions, font, and font-sizes, as well as surrounding artifact.</p>
<p>Just like with the TeleForm application, even the best OCR engines perform purely, if the input-image has not been prepared correctly. To make OCR work on a mobile device, no matter if the OCR will eventually be run onboard or in the cloud, much development time needs to be spend to train the engine &#8211; but even more importantly, to select and prepare the image areas that will be provided as input to the OCR engine &#8211; it’s going to be all about the pre-processing.</p>
<div id="attachment_1827" class="wp-caption aligncenter" style="width: 510px"><img src="http://wolfpaulus.com/wp-content/uploads/2011/12/cap2ocr.png" alt="" title="cap2ocr" width="500" height="703" class="size-full wp-image-1827" /><p class="wp-caption-text">This shows my Capture OCR sample Android-OCR application (with Tesseract OCR engine built-in), after it performed the OCR on a just taken photo of a book cover.  </p></div>
]]></content:encoded>
			<wfw:commentRss>http://wolfpaulus.com/journal/android-and-ocr/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Android ICS Source Code for your IDE</title>
		<link>http://wolfpaulus.com/journal/android-journal/androidicssource?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=androidicssource</link>
		<comments>http://wolfpaulus.com/journal/android-journal/androidicssource#comments</comments>
		<pubDate>Tue, 15 Nov 2011 04:08:42 +0000</pubDate>
		<dc:creator>Wolf Paulus</dc:creator>
				<category><![CDATA[Android]]></category>
		<category><![CDATA[android]]></category>
		<category><![CDATA[droid]]></category>
		<category><![CDATA[ICS]]></category>

		<guid isPermaLink="false">http://wolfpaulus.com/?p=1712</guid>
		<description><![CDATA[&#8220;Your father&#8217;s light saber. This is the weapon of a Jedi Knight. Not as clumsy or random as a blaster; an elegant weapon for a more civilized age. For over a thousand generations, the Jedi Knights were the guardians of peace and justice in the Old Republic. Before the dark times&#8230; before the Empire.&#8221; While [...]]]></description>
			<content:encoded><![CDATA[<blockquote><p>&#8220;Your father&#8217;s light saber. This is the weapon of a Jedi Knight. Not as clumsy or random as a blaster; an elegant weapon for a more civilized age. For over a thousand generations, the Jedi Knights were the guardians of peace and justice in the Old Republic. Before the dark times&#8230; before the Empire.&#8221;</p></blockquote>
<p>
While the Android 4.0 SDK comes with a complete set of <em>javadocs</em>, the source code of the SDK is missing in the SDK distribution. This is very unfortunate, since you cannot easily debug into SDK methods (at least not without running into de-compiled code) nor can you see how things actually work.</p>
<div id="attachment_1001" class="wp-caption aligncenter" style="width: 310px"><a href="http://wolfpaulus.com/wp-content/uploads/2010/01/SourceNotFound.png" rel="lightbox[1712]"><img class="size-medium wp-image-1001" title="SourceNotFound" src="http://wolfpaulus.com/wp-content/uploads/2010/01/SourceNotFound-300x133.png" alt="" width="300" height="133" /></a><p class="wp-caption-text">Eclipse - Source Not Found</p></div>
<p>However, there is a quick fix to that problem. I downloaded the complete Android source including the Linux, drivers, libs, etc., like explained here: <a href="http://source.android.com/source/download.html" target="_blank">http://source.android.com/source/download.html</a> and ran small Java program on the source tree. I used to this with a simple bash script but over the last couple of Android Releases, the java source locations got a little more diverse and I started missing a couple files. So instead, this Java program walks the source tree and looks for java source files. All those will then be copied into a new location, considering their package name. Finally, the jar tool gets called to put all the source into a single bundle for easier handling.<span id="more-1712"></span></p>
<pre class="brush: java; title: ; notranslate">
package com.techcasita.tools;
import java.io.*;
/**
 * Find all of Android's Java Sources, consider their package names,
 * and place it in a jar file.
 *
 * @author &lt;a href=&quot;mailto:wolf@wolfpaulus.com&quot;&gt;Wolf Paulus&lt;/a&gt;
 */
public class DroidSource {
    private static String TargetPath;

    public static void main(String[] args) {
        if (args.length == 2 &amp;amp;&amp;amp; new File(args[0]).isDirectory()) {
            DroidSource.TargetPath = args[1];
            File t = new File(args[1]);
            if ((t.exists() &amp;amp;&amp;amp; t.isDirectory() &amp;amp;&amp;amp; t.listFiles().length == 0) || t.mkdir()) {
                traverse(new File(args[0]));
                try {
                    Runtime.getRuntime().exec(&quot;jar cf &quot; + t.getParentFile().getAbsolutePath()
+ File.separatorChar + &quot;android-10-src.jar -C &quot; + TargetPath + File.separatorChar + &quot; .&quot;);
                } catch (IOException e) {
                    e.printStackTrace();
                }
            } else {
                System.out.println(&quot;Usage: &quot; + DroidSource.class.getName()
+ &quot;  &quot;);
                System.out.println(&quot; should exist and be empty&quot;);
            }
        } else {
            System.out.println(&quot;Usage: &quot; + DroidSource.class.getName()
+ &quot;  &quot;);
        }
    }

    static void traverse(File file) {
        if (file != null &amp;amp;&amp;amp; !file.isHidden()) {
            if (file.isDirectory()) {
                if (!&quot;out&quot;.equals(file.getName())) {
                    File[] files = file.listFiles();
                    for (File f : files) {
                        traverse(f);
                    }
                }
            } else if (file.getName().endsWith(&quot;java&quot;)) {
                String path = findPackage(file);

                if (path != null) {
                    path = path.replace('.', File.separatorChar);
                    createDirs(path);
                    copyFile(file.getAbsolutePath(), TargetPath + File.separatorChar
+ path + File.separatorChar + file.getName());
                }
            }
        }
    }

    private static String findPackage(File file) {
        final String TAG0 = &quot;package &quot;;
        final String TAG1 = &quot;;&quot;;
        char[] buffer = new char[2048];

        try {
            FileReader r = new FileReader(file);
            r.read(buffer);
            StringBuffer sb = new StringBuffer();
            sb.append(buffer);
            int t0 = sb.indexOf(TAG0);
            if (t0 &amp;lt; 0) return null;
            t0 += TAG0.length();
            int t1 = sb.indexOf(TAG1, t0);
            if (t1 &amp;lt; 0) return null;
            if (t1 &amp;gt; sb.indexOf(&quot; &quot;, t0)) return null;
            return sb.substring(t0, t1);
        } catch (IOException e) {
            return null;
        }
    }

    private static void createDirs(String path) {
        String[] sa = path.split(&quot;&quot; + File.separatorChar);
        String name = TargetPath;
        for (String s : sa) {
            name += File.separatorChar + s;
            File f = new File(name);
            if (!f.exists()) {
                f.mkdir();
            }
        }
    }

    private static void copyFile(String srFile, String dtFile) {
        try {
            File f1 = new File(srFile);
            File f2 = new File(dtFile);
            InputStream in = new FileInputStream(f1);
            OutputStream out = new FileOutputStream(f2);

            byte[] buf = new byte[1024];
            int len;
            while ((len = in.read(buf)) &amp;gt; 0) {
                out.write(buf, 0, len);
            }
            in.close();
            out.close();
        } catch (FileNotFoundException ex) {
            System.out.println(ex.getMessage() + &quot; in the specified directory.&quot;);
            System.exit(0);
        } catch (IOException e) {
            System.out.println(e.getMessage());
        }
    }
}
</pre>
<p>This will generate this <a href="http://wolfpaulus.com/misc/android-14-src.jar">android-14-src.jar</a>, (50 MB) containing all the relevant java sources and after attaching it to the SDK classes in your favorite IDE <em>(IntelliJ in my case)</em> you can easily step into the code, for instance during a debug session&#8230; The jar was generated from the <code>android-4.0.1_r1</code> tag here <a href="http://android.git.kernel.org" target="_blank">http://android.git.kernel.org</a> on Nov. 14, 2011.</p>
]]></content:encoded>
			<wfw:commentRss>http://wolfpaulus.com/journal/android-journal/androidicssource/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Running Errands with Google Wallet</title>
		<link>http://wolfpaulus.com/journal/life/googlewallet?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=googlewallet</link>
		<comments>http://wolfpaulus.com/journal/life/googlewallet#comments</comments>
		<pubDate>Mon, 07 Nov 2011 18:50:51 +0000</pubDate>
		<dc:creator>Wolf Paulus</dc:creator>
				<category><![CDATA[Life]]></category>
		<category><![CDATA[android]]></category>
		<category><![CDATA[droid]]></category>
		<category><![CDATA[google]]></category>
		<category><![CDATA[nfc]]></category>
		<category><![CDATA[wallet]]></category>

		<guid isPermaLink="false">http://wolfpaulus.com/?p=1578</guid>
		<description><![CDATA[Running errands with Google Wallet in San Diego&#8217;s Carmel Valley / Rancho Bernardo area (92128 / 92129 / 92130) Tap &#38; Go a.k.a PayPass, is a new simple way of paying. PayPass is a payment method that lets you make purchases without having to swipe your card or provide your signature. A simple tap with [...]]]></description>
			<content:encoded><![CDATA[<p><em>Running errands with Google Wallet in San Diego&#8217;s Carmel Valley / Rancho Bernardo area (92128 / 92129 / 92130)</em></p>
<p>Tap &amp; Go a.k.a PayPass, is a new simple way of paying. PayPass is a payment method that lets you make purchases without having to swipe your card or provide your signature. A simple tap with a card, key fob, or mobile phone is all it takes to pay at checkout.</p>
<p>So this Saturday morning, I took paying with a mobile phone to the test &#8211; the only method of payment available to me was the Google Wallet application on a Samsung Nexus S Android Phone running on Sprint&#8217;s 4G Network.</p>
<p>Google Wallet can be linked to a Citi MasterCard, or like I did, used as a prepaid card, funded with any of my existing credit cards.</p>
<p><span id="more-1578"></span></p>
<p style="text-align: center;"><a href="http://wolfpaulus.com/wp-content/uploads/2011/11/map.png" rel="lightbox[1578]"><img class="size-full wp-image-1604 aligncenter" title="map" src="http://wolfpaulus.com/wp-content/uploads/2011/11/map.png" alt="" width="600" height="242" /></a></p>
<p>I started buying a small cup of coffee at the <strong>7-11 at 13985 Torrey Del Mar Dr</strong>, San Diego, CA 92130<br />
Both checkouts were equipped with PayWave contact-less hand readers and the clerk seemed quite familiar with the process.<br />
<div class='et-box et-shadow'>
					<div class='et-box-content'>It&#8217;s best to have the phone ready (i.e. unlocked display) when holding its back close to the reader.
Holding the phone to the reader launches the Google Wallet app, which immediately prompts you to enter your 4 digit PIN. After entering the PIN, the amount you&#8217;re going to be charged, appears on the phone&#8217;s screen and after you again place the phone close to the reader, the payment will be sent.</div></div></p>
<p>Buying a small cup of joe for $1.49 at 7-11 was quick and eventless. Two touches with the phone and I was on my way.</p>
<p>Next up, the <strong>CVS/pharmacy at 9225 Twin Trails Dr</strong>, San Diego, CA 92129. I bought a toothbrush for $3.22 and once again, the checkout clerk was familiar with this form of payment, recognizing the Google Wallet app. Two touches with the phone and I was on my way to a nearby Rite Aid.</p>
<p>What goes hand in hand with a toothbrush &#8230; toothpaste of course. At the <strong>Rite Aid at 13167 Black Mountain Rd</strong>, San Diego, CA 92129, I bought a tube of Colgate for $5.16. All cash registers were equipped with the PayWave contact-less readers and the clerk was not surprised seeing me pay with a phone.</p>
<p>There is <strong>Stater Brothers Market at 9909 Carmel Mountain Rd.</strong> nearby, where I wanted to get a baguette. Unfortunately, they weren&#8217;t too keen about us filming the checkout process, but paying with the phone was almost as easy as at all previous locations. This time however, an additional interaction with the PayWave station was required, I needed to tap the &#8220;Accept&#8221; button on the reader&#8217;s small touch-screen display.</p>
<p>Four for four; not too bad so far. Let&#8217;s move on to some bigger stores in the immediate vicinity. At the <strong>Best Buy at 11160 Rancho Carmel Dr</strong>, San Diego, CA 92128, I bought a cool <a href="http://www.rocketfishproducts.com/products/computer-accessories/RF-BSTLY.html" target="_blank">RocketFish Stylus</a> for tablets like the iPad. Of course, it works extremely well on Android Tablets like the Xoom or the Samsung Gallaxy Tab 10.1 as well. You should try it someday with apps like <a href="https://market.android.com/details?id=com.techcasita.android.artist" target="_blank">Artist Pro on Android</a>.</p>

		<div class='et-image-slider alignright size-full wp-image-1591 et_sliderfx_fade et_sliderauto_true et_sliderauto_speed_4000 et_slidertype_images' id='et-image-slider543'>
			<div class='et-image-slides'>
				<div class='et-image' style='background: url(http://wolfpaulus.com/wp-content/uploads/2011/11/nexushome.png) no-repeat; width: 240px; height: 400px;'><span class='et-image-overlay'> </span></div>

		<div class='et-image' style='background: url(http://wolfpaulus.com/wp-content/uploads/2011/11/pin.png) no-repeat; width: 240px; height: 400px;'><span class='et-image-overlay'> </span></div>

		<div class='et-image' style='background: url(http://wolfpaulus.com/wp-content/uploads/2011/11/prepaid.png) no-repeat; width: 800px; height: 400px;'><span class='et-image-overlay'> </span></div>

		<div class='et-image' style='background: url(http://wolfpaulus.com/wp-content/uploads/2011/11/transactions.png) no-repeat; width: 240px; height: 400px;'><span class='et-image-overlay'> </span></div>
			</div>
			
			<div class='et-image-shadow'></div>
			<div class='et-image-shadowleft'></div>
			<div class='et-image-shadowright'></div>
		</div> <!-- .et-image-slider -->
		
<p>$18.30 would be my biggest purchase for the day and I should mention, <em>&#8220;paying without signature&#8221;</em> is limited to payments under $50. While all cash registers were equipped with PayWave, the one I was using was broken. However, the guys at Best Buy swiftly moved me to another line and after two touches with the phone and I was on my way.</p>
<p>Next stop, <strong>Sports Authority at 11690 Carmel Mountain Rd</strong>, San Diego, CA 92128. I bought a baseball for $3.22 and while the clerk was telling me that this was the first time that he saw someone paying with a phone, it worked flawlessly. Again, tap &#8211; enter Pin &#8211; tap and I was on my way .. to the Home Depot.</p>
<p>Last stop for the day was the <strong>Home Depot at 12185 Carmel Mountain Rd</strong>, San Diego, CA 92128, were I wanted to get a strap wrench. Only one (Number 11) of the 14 registers was equipped with a PayWave reader and the clerk seemed completely clue-less. I attempted to pay just like I did in all the other stores but the payment was rejected three times, after which I left the store empty-handed and a little disillusioned.</p>
<p>To be fair, we did our homework before we started. We researched, which nearby stores accepted PayPasse and planned the route accordingly. Six out of seven therefore sounds a little better then it actually is. Paying with Tap &amp; Go is convenient, no question about it. However, don&#8217;t leave your house without a card just yet.</p>
<p>&nbsp;</p>
<h2>At a Glance</h2>

			<div class='tabs-left et_sliderfx_fade et_sliderauto_true et_sliderauto_speed_5000 et_slidertype_left_tabs' id='tabs-left277'>
				<ul class='et-tabs-control'>
			<li><a href='#'>
			7-11
		</a></li>

		<li><a href='#'>
			CVS
		</a></li>

		<li><a href='#'>
			Rite Aid
		</a></li>

		<li><a href='#'>
			Stater Bros
		</a></li>

		<li><a href='#'>
			Sports Authority
		</a></li>

		<li><a href='#'>
			Best Buy
		</a></li>

		<li><a href='#'>
			Home Depot
		</a></li>
		</ul> <!-- .et-tabs-control -->

		<div class='et-tabs-content'>
			<div class='et_slidecontent'>
			Perfect place for first time PayPass users. All cash registers are equipped and
employees are very used to customers paying via Tap &amp; Go. Chances to embarrass yourself are rather low here.
		</div>

		<div class='et_slidecontent'>
			Long lines seem to be common here. Be prepared when starting the checkout process. Clerks may not have the patience you would like to see as a new PayPass user.
		</div>

		<div class='et_slidecontent'>
			After 7-11, this maybe the second-best place to try paying with Tap and Go. All cash register are equipped and employees are very used to customers paying with PayPass.
		</div>

		<div class='et_slidecontent'>
			Staff in the checkout line maybe over-worked and don&#8217;t show the patience you would like to see as a new PayPass user. Once you have done it a couple of times however, this is a good place to pay with your phone.
		</div>

		<div class='et_slidecontent'>
			Not recommended for first timers. Employees are not used to customers paying with PayPass. Once you have done it a couple of times however, this is a pretty good place to pay with your phone.
		</div>

		<div class='et_slidecontent'>
			Great place to pay with PayPass. While one of the readers is currently broken, staff is extremely helpful and as you would expect from a <em>geeky</em> place, likes to see you pay with PayPass.
		</div>

		<div class='et_slidecontent'>
			If you want to try paying with PayWave, look for cash register 11.
Don&#8217;t expect it to work and come prepared to pay with an alternative method.
Staff is not used to be confronted with customers wanting to pay with a phone.
		</div>
		</div>
			</div> <!-- .tabs-left -->
<p>&nbsp;</p>
<p>Now, don&#8217;t think just because PayPass is not ubiquitous yet, it&#8217;s not cool. We think it&#8217;s <strong>awesome</strong> and to let you experience the cool ways of <em>Tap &amp; Go</em> with us, we produced this short firm, showing <em>&#8220;A Saturday Morning with Google wallet&#8221;</em>. We really hope you are going to like it: .. click to play ..<br />
	<script src="http://www.apple.com/library/quicktime/scripts/ac_quicktime.js" language="JavaScript" type="text/javascript"></script><br />
	<script src="http://www.apple.com/library/quicktime/scripts/qtp_library.js" language="JavaScript" type="text/javascript"></script></p>
<link href="http://www.apple.com/library/quicktime/stylesheets/qtp_library.css" rel="StyleSheet" type="text/css" />
<script type="text/javascript"><!--
	QT_WritePoster_XHTML('Click to Play', '/GoogleWallet/GoogleWallet-poster.jpg',
		'/GoogleWallet/GoogleWallet.mov',
		'640', '372', '',
		'controller', 'true',
		'autoplay', 'true',
		'bgcolor', 'black',
		'scale', 'aspect');
//-->
</script><br />
<noscript><br />
<object width="640" height="372" classid="clsid:02BF25D5-8C17-4B23-BC80-D3488ABDDC6B" codebase="http://www.apple.com/qtactivex/qtplugin.cab"><param name="src" value="/GoogleWallet/GoogleWallet-poster.jpg" /><param name="href" value="/GoogleWallet/GoogleWallet.mov" /><param name="target" value="myself" /><param name="controller" value="false" /><param name="autoplay" value="false" /><param name="scale" value="aspect" /><embed width="640" height="372" type="video/quicktime" pluginspage="http://www.apple.com/quicktime/download/"<br />
		src="/GoogleWallet/GoogleWallet-poster.jpg"<br />
		href="/GoogleWallet/GoogleWallet.mov"<br />
		target="myself"<br />
		controller="false"<br />
		autoplay="false"<br />
		scale="aspect"></embed></object><br />
</noscript><br />
&nbsp;</p>
<h4>Direct Movie Link</h4>
<p><a href="http://wolfpaulus.com/GoogleWallet/GoogleWallet.mov">http://wolfpaulus.com/GoogleWallet/GoogleWallet.mov</a><br />
&nbsp;<br />
&nbsp;</p>
<hr/>
]]></content:encoded>
			<wfw:commentRss>http://wolfpaulus.com/journal/life/googlewallet/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
<enclosure url="http://wolfpaulus.com/GoogleWallet/GoogleWallet.mov" length="181" type="video/quicktime" />
		</item>
		<item>
		<title>Tiffany meets Android</title>
		<link>http://wolfpaulus.com/journal/android-journal/android_screencasting?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=android_screencasting</link>
		<comments>http://wolfpaulus.com/journal/android-journal/android_screencasting#comments</comments>
		<pubDate>Thu, 27 Oct 2011 03:53:51 +0000</pubDate>
		<dc:creator>Wolf Paulus</dc:creator>
				<category><![CDATA[Android]]></category>
		<category><![CDATA[android]]></category>
		<category><![CDATA[droid]]></category>
		<category><![CDATA[TiffanyScreens]]></category>
		<category><![CDATA[usb]]></category>

		<guid isPermaLink="false">http://wolfpaulus.com/?p=1539</guid>
		<description><![CDATA[Ever since I started working on the Android platform and Android phone and tablet applications, I found it challenging to show my ideas, designs, and prototypes to a group of people, no matter how small that group was. Naturally, I wanted to not just explain concepts and behaviors but to show a live demo on [...]]]></description>
			<content:encoded><![CDATA[<p>Ever since I started working on the Android platform and Android phone and tablet applications, I found it challenging to show my ideas, designs, and prototypes to a group of people, no matter how small that group was. Naturally, I wanted to not just explain concepts and behaviors but to show a live demo on a phone. However, the screen-size of a phone can be a serious obstacle when presenting to a small group. Moreover, the phone often gets covered by my hand when interacting with a mobile application.<br />
I have tried to capture the phone&#8217;s screen with a video-camera and showing the live-view on a bigger monitor but reflections, glare, and insufficient lighting resulted almost always in an even worse experience and only in an controlled environment (light-box etc.), filming worked well and I was able to capture footage with a reasonable quality.</p>
<p>For the last couple of weeks now, we have started experimenting with USB-tethered Android screen capturing in combination with <a title="TiffanyScreens" href="http://www.tiffanyscreens.com" target="_blank">TiffanyScreens</a>.</p>
<p><span id="more-1539"></span></p>
<p style="text-align: center;"><a href="http://wolfpaulus.com/wp-content/uploads/2011/10/ts4020.png" rel="lightbox[1539]"><img class="size-full wp-image-1550 aligncenter" title="TiffanyScreens Application" src="http://wolfpaulus.com/wp-content/uploads/2011/10/ts4020.png" alt="" width="450" height="386" /></a><br />
<div class='et-box et-shadow'>
					<div class='et-box-content'>TiffanyScreens is a presentation tool, capturing the content of the presenter&#8217;s screen and sending it to multiple other computer screens at the same time. Any computer can seamlessly become the presenting computer, no matter if connected wirelessly or through an Ethernet cable.
The self-contained solution does neither require nor use a server. It automatically detects all computers on a local network, running the software and those computers can watch the presentation projected onto their screens. Best of all, with a single button click, any computer can switch from watching into presenting mode.</div></div></p>
<p>&nbsp;</p>
<p>Today we are announcing</p>
<h2>TiffanyScreens with Android ScreenCasting.</h2>
<p>TiffanyScreens can now share the screen content of an Android phone that is connected via USB-cable to your PC or Mac, with every one else in the room, running TiffanyScreens. Think how this is going to change your UI/UX design meetings, your mobile engineering meetings, your product management / marketing meeting, or your seminars and demos.</p>
<p style="text-align: center;"><a href="http://wolfpaulus.com/wp-content/uploads/2011/10/ts40211.png" rel="lightbox[1539]"><img class="size-full wp-image-1548 aligncenter" title="TiffanyScreens Screen/Phone Content Broadcasting" src="http://wolfpaulus.com/wp-content/uploads/2011/10/ts40211.png" alt="" width="550" height="200" /></a></p>
<p>&nbsp;</p>
<p>You don&#8217;t need to have an Android SDK installed on your computer nor do you need to run any special application on your Android phone. If you present from a Windows PC, you may need to install the appropriate USB driver and USB-Debugging needs to be enabled on the phone.<br />
With these small pre-requisites out of the way, a presenter will see a small drop-down on the lower-right, allowing him to easily switch the presentation&#8217;s source, between computer and phone screens.</p>
<p style="text-align: center;"><a href="http://wolfpaulus.com/wp-content/uploads/2011/10/ts4022.png" rel="lightbox[1539]"><img class="size-full wp-image-1549 aligncenter" title="TiffanyScreens Receiving an Android Screencast" src="http://wolfpaulus.com/wp-content/uploads/2011/10/ts4022.png" alt="" width="400" height="703" /></a></p>
]]></content:encoded>
			<wfw:commentRss>http://wolfpaulus.com/journal/android-journal/android_screencasting/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Remembrance</title>
		<link>http://wolfpaulus.com/journal/life/remembrance_knr?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=remembrance_knr</link>
		<comments>http://wolfpaulus.com/journal/life/remembrance_knr#comments</comments>
		<pubDate>Sat, 22 Oct 2011 03:45:11 +0000</pubDate>
		<dc:creator>Wolf Paulus</dc:creator>
				<category><![CDATA[Life]]></category>

		<guid isPermaLink="false">http://wolfpaulus.com/?p=1533</guid>
		<description><![CDATA[&#8220;The C Programming Language&#8221; originally published in 1977. My German copy was printed in 1983.]]></description>
			<content:encoded><![CDATA[<p style="text-align: center;"><a href="http://wolfpaulus.com/wp-content/uploads/2011/10/KNR.jpg" rel="lightbox[1533]"><img class="size-full wp-image-1534 aligncenter" title="KNR" src="http://wolfpaulus.com/wp-content/uploads/2011/10/KNR.jpg" alt="" width="600" height="400" /></a></p>
<p>&#8220;The C Programming Language&#8221; originally published in 1977. My German copy was printed in 1983.</p>
]]></content:encoded>
			<wfw:commentRss>http://wolfpaulus.com/journal/life/remembrance_knr/feed</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>TiffanyScreens &#8211; Astoundingly Faster</title>
		<link>http://wolfpaulus.com/journal/tiffanyscreens-astounding?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=tiffanyscreens-astounding</link>
		<comments>http://wolfpaulus.com/journal/tiffanyscreens-astounding#comments</comments>
		<pubDate>Thu, 20 Oct 2011 23:05:52 +0000</pubDate>
		<dc:creator>Wolf Paulus</dc:creator>
				<category><![CDATA[Java]]></category>
		<category><![CDATA[Journal]]></category>
		<category><![CDATA[java]]></category>
		<category><![CDATA[JNI]]></category>
		<category><![CDATA[Mac OS X]]></category>
		<category><![CDATA[TiffanyScreens]]></category>

		<guid isPermaLink="false">http://wolfpaulus.com/?p=1505</guid>
		<description><![CDATA[I never really liked those meetings, sitting in a room with slightly dimmed lights, participants facing a wall, watching power-point slides, and trying to follow a mostly boring presentation. A discussion &#8211; if at all &#8211; would most likely happen after the presentation. Changing laptops is too cumbersome with all the disconnecting and re-connecting of cables [...]]]></description>
			<content:encoded><![CDATA[<p>I never really liked those meetings, sitting in a room with slightly dimmed lights, participants facing a wall, watching power-point slides, and trying to follow a mostly boring presentation. A discussion &#8211; if at all &#8211; would most likely happen after the presentation. Changing laptops is too cumbersome with all the disconnecting and re-connecting of cables involved &#8211; not to talk about syncing the laptop to the projector&#8217;s max resolution.</p>
<p>Since most participants seem to bring a laptop, to those meetings to check Email, IM, etc. I had the idea to instead of the wall, use the laptops&#8217; screens as a presentation canvas, i.e. we could all sit on a round table again, facing each other, instead of the wall.</p>
<p>I wrote a small self-contained (doesn&#8217;t use a server) multi-platform application a.k.a <a href="http://www.tiffanyscreens.com" target="_blank">TiffanyScreens</a>, to capture the presenter&#8217;s screen content and send it to the other participants. Best of all, with a simple push of a button, an observing meeting participant becomes a presenter, showing her screen&#8217;s content to the other team members.</p>
<p>Up to now, <a href="http://www.tiffanyscreens.com" target="_blank">TiffanyScreens</a> was implemented in <em>pure</em> Java. While the installer was implemented and compiled on the native platform, the application itself did not take advantage of OS native capabilities.</p>
<p><span id="more-1505"></span><img class="alignleft size-full wp-image-1518" title="bsplash40" src="http://wolfpaulus.com/wp-content/uploads/2011/10/bsplash40.png" alt="" width="509" height="300" /></p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>Up to now that is. TiffanyScreens 4.0 taps into native libraries and the results are stunning. Instead of using Java&#8217;s slow screen capture support, TiffanyScreen now uses Mac OS X native foundation to capture and send up 30 frames per second, marking a three-fold increase. Using native OS functionality also allows the selection of the monitor to capture, which may become important, if you have a 2nd monitor connected to your laptop.</p>
<p style="text-align: center;"><a href="http://wolfpaulus.com/wp-content/uploads/2011/10/tiffy41.png" rel="lightbox[1505]"><img class="size-full wp-image-1515 aligncenter" title="tiffy41" src="http://wolfpaulus.com/wp-content/uploads/2011/10/tiffy41.png" alt="" width="566" height="201" /></a></p>
<p>By default and on systems were native support is not yet available, TiffanyScreens still uses Java&#8217;s inherent screen capture functionality. A small drop-down on the bottom-right, available when broadcasting allows the presenter to select native screen-capturing, and also to select the source monitor. Switching to native, taxes the presenters machine a little less and also improves the experience for the watching audience, e.g. by showing smoother transitions.</p>
<p style="text-align: center;"><a href="http://wolfpaulus.com/wp-content/uploads/2011/10/tiffy2.png" rel="lightbox[1505]"><img class="size-full wp-image-1514 aligncenter" title="tiffy2" src="http://wolfpaulus.com/wp-content/uploads/2011/10/tiffy2.png" alt="" width="566" height="201" /></a></p>
<p style="text-align: center;"><a href="http://wolfpaulus.com/wp-content/uploads/2011/10/tiffy43.png" rel="lightbox[1505]"><img class="size-full wp-image-1511 aligncenter" title="tiffy43" src="http://wolfpaulus.com/wp-content/uploads/2011/10/tiffy43.png" alt="" width="566" height="201" /></a></p>
<p><code><br />
JNIEXPORT jbyteArray JNICALL<br />
Java_com_carlsbadcubes_tiffany_send_ScreenCapture_getScreenShot ( JNIEnv* env, jobject thiz, jint ndid );</code></p>
<p>JNIEXPORT jint JNICALL<br />
Java_com_carlsbadcubes_tiffany_send_ScreenCapture_getNumberOfScreens( JNIEnv* env, jobject thiz);</p>
<p>&#8230;</p>
<p>&nbsp;</p>
]]></content:encoded>
			<wfw:commentRss>http://wolfpaulus.com/journal/tiffanyscreens-astounding/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Remembrance</title>
		<link>http://wolfpaulus.com/journal/life/remembrance?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=remembrance</link>
		<comments>http://wolfpaulus.com/journal/life/remembrance#comments</comments>
		<pubDate>Thu, 06 Oct 2011 03:48:19 +0000</pubDate>
		<dc:creator>Wolf Paulus</dc:creator>
				<category><![CDATA[Life]]></category>
		<category><![CDATA[Apple]]></category>

		<guid isPermaLink="false">http://wolfpaulus.com/?p=1462</guid>
		<description><![CDATA[. Apple Store NYC, iDay -3 July 2007 in NYC &#8211; 3 days before the iPhone launch &#160;]]></description>
			<content:encoded><![CDATA[<p><a href="http://wolfpaulus.com/wp-content/uploads/2011/10/nycstore.jpeg" rel="lightbox[1462]"><img class="alignright size-full wp-image-1464" title="nycstore" src="http://wolfpaulus.com/wp-content/uploads/2011/10/nycstore.jpeg" alt="" width="600" height="400" /></a></p>
<h1 id="title_div702781584"></h1>
<h1></h1>
<h2></h2>
<h2></h2>
<h2></h2>
<h2></h2>
<h2></h2>
<h2></h2>
<h2></h2>
<h2></h2>
<h2></h2>
<h2></h2>
<p>.</p>
<h2>Apple Store NYC, iDay -3</h2>
<p>July 2007 in NYC &#8211; 3 days before the iPhone launch</p>
<p>&nbsp;</p>
]]></content:encoded>
			<wfw:commentRss>http://wolfpaulus.com/journal/life/remembrance/feed</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Installing Tomcat 7.0.x on OS X</title>
		<link>http://wolfpaulus.com/journal/mac/tomcat7?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=tomcat7</link>
		<comments>http://wolfpaulus.com/journal/mac/tomcat7#comments</comments>
		<pubDate>Sun, 14 Aug 2011 22:20:43 +0000</pubDate>
		<dc:creator>Wolf Paulus</dc:creator>
				<category><![CDATA[Mac OS X]]></category>
		<category><![CDATA[java]]></category>
		<category><![CDATA[Servlet]]></category>
		<category><![CDATA[Tomcat]]></category>

		<guid isPermaLink="false">http://wolfpaulus.com/?p=760</guid>
		<description><![CDATA[Tomcat 7 is the first Apache Tomcat release to support the Servlet 3.0, JSP 2.2, and EL 2.2 specifications. Please note that Tomcat 7 requires Java 1.6 or better, but that shouldn&#8217;t be a problem, if you are running OS X 10.5 or 10.6. On OS X 10.7 (Lion) however, Java is initially not installed [...]]]></description>
			<content:encoded><![CDATA[<p>Tomcat 7 is the first Apache Tomcat release to support the Servlet 3.0, JSP 2.2, and EL 2.2 specifications. Please note that Tomcat 7 requires Java 1.6 or better, but that shouldn&#8217;t be a problem, if you are running OS X 10.5 or 10.6.</p>
<p><a href="http://wolfpaulus.com/wp-content/uploads/2010/06/lion.png" rel="lightbox[760]"><img class="alignright size-full wp-image-1427" title="lion" src="http://wolfpaulus.com/wp-content/uploads/2010/06/lion.png" alt="" width="178" height="152" /></a>On OS X 10.7 (Lion) however, Java is initially not installed anymore. You can find the installer on Apple&#8217;s support side <a title="Java for OS X Lion" href="http://support.apple.com/kb/DL1421" target="_blank">here</a> or follow this installation <a title="Install the Java runtime (JRE) | Mac OS 10.7 Lion" href="http://kb2.adobe.com/cps/909/cpsid_90908.html" target="_blank">guide</a>, provided by Adobe. Whatever you do, when opening Terminal and running java -version, you should see something like this.</p>
<pre>java version "1.6.0_26"
Java(TM) SE Runtime Environment (build 1.6.0_26-b03-383-11A511)
Java HotSpot(TM) 64-Bit Server VM (build 20.1-b02-383, mixed mode)</pre>
<p>Here are the easy to follow steps to get it up and running on your Mac<span id="more-760"></span></p>
<ol>
<li>Download a binary distribution of the core module: apache-tomcat-7.0.20.tar.gz from <a title="Apache Tomcat Download Page" href="http://tomcat.apache.org/download-70.cgi" target="_blank">here</a>. I picked the tar.gz in Binary Distributions / Core section.</li>
<li>Opening/unarchiving the archive will create a folder structure something like this in you Downloads folder: (btw, this <a title=" The Unarchiver" href="http://wakaba.c3.cx/s/apps/unarchiver" target="_blank">Unarchiver app</a> is perfect for all kinds of compressed files and superior to the built-in Archive Utility.app)<br />
~/Downloads/apache-tomcat-7.0.20</li>
<li>Move the unarchived distribution to /usr/local<br />
<code>sudo mkdir /usr/local</code><br />
<code>sudo mv ~/Downloads/apache-tomcat-7.0.20 /usr/local</code></li>
<li>To make it easy to replace this release with future releases, we are going to create a symbolic link that we are going to use when referring to Tomcat:<br />
<code>sudo ln -s /usr/local/apache-tomcat-7.0.20 /Library/Tomcat</code></li>
<li>Change ownership of the /Libaray/Tomcat folder hierarchy:<br />
<code>sudo chown -R &lt;your_username&gt; /Library/Tomcat</code></li>
<li>Make all scripts executable:<br />
<code>sudo chmod +x /Library/Tomcat/bin/*.sh</code></li>
</ol>
<p><a href="http://wolfpaulus.com/wp-content/uploads/2010/06/tomcat2.png" rel="lightbox[760]"><img class="aligncenter size-full wp-image-1424" title="tomcat" src="http://wolfpaulus.com/wp-content/uploads/2010/06/tomcat2.png" alt="" width="550" height="422" /></a></p>
<p>Instead of using the start and stop scripts, like so:<br />
<code>Last login: Sun Aug 14 15:20:38 on ttys000<br />
wpbookpro:~ wolf$ <strong>/Library/Tomcat/bin/startup.sh</strong><br />
Using CATALINA_BASE:   /Library/Tomcat<br />
Using CATALINA_HOME:   /Library/Tomcat<br />
Using CATALINA_TMPDIR: /Library/Tomcat/temp<br />
Using JRE_HOME:        /Library/Java/Home<br />
Using CLASSPATH:       /Library/Tomcat/bin/bootstrap.jar:/Library/Tomcat/bin/tomcat-juli.jar<br />
wpbookpro:~ wolf$ <strong>/Library/Tomcat/bin/shutdown.sh</strong><br />
Using CATALINA_BASE:   /Library/Tomcat<br />
Using CATALINA_HOME:   /Library/Tomcat<br />
Using CATALINA_TMPDIR: /Library/Tomcat/temp<br />
Using JRE_HOME:        /Library/Java/Home<br />
Using CLASSPATH:       /Library/Tomcat/bin/bootstrap.jar:/Library/Tomcat/bin/tomcat-juli.jar<br />
wpbookpro:~ wolf$<br />
</code><br />
you may also want to check out Activata’s <a title="Tomcat Controller" href="http://www.activata.co.uk/tomcatcontroller/" target="_blank">Tomcat Controller</a> a tiny freeware app, providing a UI to quickly start/stop Tomcat.</p>
<p><a href="http://wolfpaulus.com/wp-content/uploads/2010/06/tcontroller.png" rel="lightbox[760]"><img class="aligncenter size-full wp-image-1426" title="Tomcat Controller" src="http://wolfpaulus.com/wp-content/uploads/2010/06/tcontroller.png" alt="" width="500" height="280" /></a></p>
]]></content:encoded>
			<wfw:commentRss>http://wolfpaulus.com/journal/mac/tomcat7/feed</wfw:commentRss>
		<slash:comments>56</slash:comments>
		</item>
		<item>
		<title>16 thousand Color Changes Later</title>
		<link>http://wolfpaulus.com/journal/android-journal/16kcolorchanges?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=16kcolorchanges</link>
		<comments>http://wolfpaulus.com/journal/android-journal/16kcolorchanges#comments</comments>
		<pubDate>Sun, 31 Jul 2011 06:26:01 +0000</pubDate>
		<dc:creator>Wolf Paulus</dc:creator>
				<category><![CDATA[Android]]></category>
		<category><![CDATA[analytics]]></category>
		<category><![CDATA[android]]></category>
		<category><![CDATA[google]]></category>
		<category><![CDATA[print]]></category>

		<guid isPermaLink="false">http://wolfpaulus.com/?p=1398</guid>
		<description><![CDATA[Occasionally, I speak at developer conferences, and recently have talked about how to add printing as feature into Android applications. As an example, I take a pretty simple application with only a few Android Activities and then show how a print intent can be integrated for mime types like image/* or application/pdf. After such a [...]]]></description>
			<content:encoded><![CDATA[<p>Occasionally, I speak at developer conferences, and recently have talked about how to add printing as feature into Android applications. As an example, I take a pretty simple application with only a few Android Activities and then show how a print intent can be integrated for mime types like <em>image/*</em> or <em>application/pdf</em>. After such a talk I have always a couple people come up that seem to be more interested in my demo app than the concept of adding printing to their own apps and they ask me where they can download the app.</p>
<p>Well, about three weeks ago, I decided to make my little demo app available in the Android Market. It&#8217;s a well known simple finger painting application, the display is the canvas and your finger becomes a paint brush.</p>
<p>Simply use your finger to create beautiful paintings. Use the Menu to change color or brush styles and also to print or share your artwork via Email etc. Become an Artist on Android and print some of your best creations. Using HP Advanced Photo Paper has produced stunning results, worth framing.</p>
<p>&nbsp;</p>
<p><a href="http://wolfpaulus.com/wp-content/uploads/2011/07/artistona.png" rel="lightbox[1398]"><img class="alignnone size-medium wp-image-1402" title="artistona" src="http://wolfpaulus.com/wp-content/uploads/2011/07/artistona-300x126.png" alt="" width="300" height="126" /></a></p>
<p><a title="Artist on Android" href="https://market.android.com/details?id=com.techcasita.android.creative" target="_blank">https://market.android.com/details?id=com.techcasita.android.creative</a></p>
<p>&nbsp;</p>
<p><span id="more-1398"></span><br />
I have recently added some code, allowing the artwork to be directly uploaded to a group on Flickr: <a title="Flickr Group Artist on Android" href="http://www.flickr.com/groups/artistonandroid" target="_blank">Artist on Android</a> &#8211; and WOW, check out the results; there really are Artists on Android. I have also integrated Google&#8217;s Analytics library to get a better idea about how the app is being used.</p>
<p>&nbsp;</p>
<p><a href="http://wolfpaulus.com/wp-content/uploads/2011/07/flickrgroup.png" rel="lightbox[1398]"><img class="alignnone size-medium wp-image-1412" title="flickrgroup" src="http://wolfpaulus.com/wp-content/uploads/2011/07/flickrgroup-300x214.png" alt="" width="300" height="214" /></a></p>
<p>&nbsp;</p>
<p><a href="http://code.google.com/mobile/analytics/docs/android/" target="_blank">Google Analytics</a> is really a great tool, every Android app should make use of. There is a lot to learn about usage patterns and while my little app is currently installed on only about 1,200 devices, I still know a lot about how it&#8217;s used.<br />
For instance, I know that 70 times an finger-painted image has been printed and that on average it took 89 seconds to draw the picture before it was printed.</p>
<p>In total, 16,534 times the color of a brush was changed and 12,973 times the canvas was cleared and the artist was starting over.<br />
The brush type (blur, emboss, etc. ) was changed 12,533 times, blur being the most popular 3,403 times.<br />
Users can print via Google Cloud-print or HP iPrint, requiring an HP Printer as well as HP&#8217;s iPrint for Android app being installed. Anyway, if not on the device already, I guide the user through the install process of HP iPrint which can result in some stunning prints. In total, 53 users (4.6%) went that route and installed HP iPrint on their phones.</p>
<p>There are more numbers that need to be interpreted and analyzed. However, just this little adventure into usage tracking with Google Analytics can help you understand your user base so much better, going from &#8220;I think our users want ..&#8221; to &#8220;I know exactly what our users want ..&#8221;.</p>
<p>A very concrete result for this application could be to make brush color changes easier, e.g. pulling it up in the menu tree, or to make choosing the blur brush easier.</p>
<p>Considering how often Android applications are being updated, we might be at the verge of a new area of customer driven User Experience Design, not relying on small groups of test users anymore, but observing real usage behavior and adjusting the user experience almost interactively.</p>
<p>&nbsp;</p>
<div id="attachment_1295" class="wp-caption alignnone" style="width: 310px"><a href="http://wolfpaulus.com/wp-content/uploads/2011/06/tab10paint.jpg" rel="lightbox[1398]"><img class="size-medium wp-image-1295" title="tab10paint" src="http://wolfpaulus.com/wp-content/uploads/2011/06/tab10paint-300x200.jpg" alt="" width="300" height="200" /></a><p class="wp-caption-text">Galaxy 10.1 Tab running a finger paint app that can print</p></div>
]]></content:encoded>
			<wfw:commentRss>http://wolfpaulus.com/journal/android-journal/16kcolorchanges/feed</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Android SDK Source Code for your IDE</title>
		<link>http://wolfpaulus.com/journal/android-journal/androidsdksource?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=androidsdksource</link>
		<comments>http://wolfpaulus.com/journal/android-journal/androidsdksource#comments</comments>
		<pubDate>Fri, 15 Jul 2011 07:00:54 +0000</pubDate>
		<dc:creator>Wolf Paulus</dc:creator>
				<category><![CDATA[Android]]></category>
		<category><![CDATA[android]]></category>

		<guid isPermaLink="false">http://wolfpaulus.com/?p=31</guid>
		<description><![CDATA[&#8220;There was nothing you could have done, Luke, had you been there. You&#8217;d have been killed too, and the droids would now be in the hands of the Empire.&#8221; While the Android SDK comes with a complete set of javadocs, the source code of the SDK is missing in the SDK distribution. This is very unfortunate, [...]]]></description>
			<content:encoded><![CDATA[<blockquote><p>&#8220;There was nothing you could have done, Luke, had you been there. You&#8217;d have been killed too, and the droids would now be in the hands of the Empire.&#8221;</p></blockquote>
<p>While the Android SDK comes with a complete set of <em>javadocs</em>, the source code of the SDK is missing in the SDK distribution. This is very unfortunate, since you cannot easily debug into SDK methods (at least not without running into de-compiled code) nor can you see how things actually work.</p>
<div id="attachment_1001" class="wp-caption aligncenter" style="width: 310px"><a href="http://wolfpaulus.com/wp-content/uploads/2010/01/SourceNotFound.png" rel="lightbox[31]"><img class="size-medium wp-image-1001" title="SourceNotFound" src="http://wolfpaulus.com/wp-content/uploads/2010/01/SourceNotFound-300x133.png" alt="" width="300" height="133" /></a><p class="wp-caption-text">Eclipse - Source Not Found</p></div>
<p>However, there is a quick fix to that problem. I downloaded the complete Android source including the Linux, drivers, libs, etc., like explained here: <a href="http://source.android.com/source/download.html" target="_blank">http://source.android.com/source/download.html</a> and ran small Java program on the source tree. I used to this with a simple bash script but over the last couple of Android Releases, the java source locations got a little more diverse and I started missing a couple files. So instead, this Java program walks the source tree and looks for java source files. All those will then be copied into a new location, considering their package name. Finally, the jar tool gets called to put all the source into a single bundle for easier handling.<span id="more-31"></span></p>
<pre class="brush: java; title: ; notranslate">
package com.techcasita.tools;
import java.io.*;
/**
 * Find all of Android's Java Sources, consider their package names,
 * and place it in a jar file.
 *
 * @author &lt;a href=&quot;mailto:wolf@wolfpaulus.com&quot;&gt;Wolf Paulus&lt;/a&gt;
 */
public class DroidSource {
    private static String TargetPath;

    public static void main(String[] args) {
        if (args.length == 2 &amp;amp;&amp;amp; new File(args[0]).isDirectory()) {
            DroidSource.TargetPath = args[1];
            File t = new File(args[1]);
            if ((t.exists() &amp;amp;&amp;amp; t.isDirectory() &amp;amp;&amp;amp; t.listFiles().length == 0) || t.mkdir()) {
                traverse(new File(args[0]));
                try {
                    Runtime.getRuntime().exec(&quot;jar cf &quot; + t.getParentFile().getAbsolutePath()
+ File.separatorChar + &quot;android-10-src.jar -C &quot; + TargetPath + File.separatorChar + &quot; .&quot;);
                } catch (IOException e) {
                    e.printStackTrace();
                }
            } else {
                System.out.println(&quot;Usage: &quot; + DroidSource.class.getName()
+ &quot;  &quot;);
                System.out.println(&quot; should exist and be empty&quot;);
            }
        } else {
            System.out.println(&quot;Usage: &quot; + DroidSource.class.getName()
+ &quot;  &quot;);
        }
    }

    static void traverse(File file) {
        if (file != null &amp;amp;&amp;amp; !file.isHidden()) {
            if (file.isDirectory()) {
                if (!&quot;out&quot;.equals(file.getName())) {
                    File[] files = file.listFiles();
                    for (File f : files) {
                        traverse(f);
                    }
                }
            } else if (file.getName().endsWith(&quot;java&quot;)) {
                String path = findPackage(file);

                if (path != null) {
                    path = path.replace('.', File.separatorChar);
                    createDirs(path);
                    copyFile(file.getAbsolutePath(), TargetPath + File.separatorChar
+ path + File.separatorChar + file.getName());
                }
            }
        }
    }

    private static String findPackage(File file) {
        final String TAG0 = &quot;package &quot;;
        final String TAG1 = &quot;;&quot;;
        char[] buffer = new char[2048];

        try {
            FileReader r = new FileReader(file);
            r.read(buffer);
            StringBuffer sb = new StringBuffer();
            sb.append(buffer);
            int t0 = sb.indexOf(TAG0);
            if (t0 &amp;lt; 0) return null;
            t0 += TAG0.length();
            int t1 = sb.indexOf(TAG1, t0);
            if (t1 &amp;lt; 0) return null;
            if (t1 &amp;gt; sb.indexOf(&quot; &quot;, t0)) return null;
            return sb.substring(t0, t1);
        } catch (IOException e) {
            return null;
        }
    }

    private static void createDirs(String path) {
        String[] sa = path.split(&quot;&quot; + File.separatorChar);
        String name = TargetPath;
        for (String s : sa) {
            name += File.separatorChar + s;
            File f = new File(name);
            if (!f.exists()) {
                f.mkdir();
            }
        }
    }

    private static void copyFile(String srFile, String dtFile) {
        try {
            File f1 = new File(srFile);
            File f2 = new File(dtFile);
            InputStream in = new FileInputStream(f1);
            OutputStream out = new FileOutputStream(f2);

            byte[] buf = new byte[1024];
            int len;
            while ((len = in.read(buf)) &amp;gt; 0) {
                out.write(buf, 0, len);
            }
            in.close();
            out.close();
        } catch (FileNotFoundException ex) {
            System.out.println(ex.getMessage() + &quot; in the specified directory.&quot;);
            System.exit(0);
        } catch (IOException e) {
            System.out.println(e.getMessage());
        }
    }
}
</pre>
<p>This will generate this <a href="http://wolfpaulus.com/misc/android-10-src.jar">android-10-src.jar</a>, (36 MB) containing all the relevant java sources and after attaching it to the SDK classes in your favorite IDE (IntelliJ in my case) you can easily step into the code, for instance during a debug session&#8230; The jar was generated from the 2-3 tag here <a href="http://android.git.kernel.org" target="_blank">http://android.git.kernel.org</a> on July 1st, 2011.</p>
]]></content:encoded>
			<wfw:commentRss>http://wolfpaulus.com/journal/android-journal/androidsdksource/feed</wfw:commentRss>
		<slash:comments>6</slash:comments>
		</item>
	</channel>
</rss>

