renaud.io

Yet Another Command Line Experiments.

Are you kidding me? This video can’t have been recorded in 1946!

Today, I made some experiment with avconv on videos made with some android devices and found out that the creation_time of a video of mine recorded back in june seems to be crippled since the value returned by ffprobe is 1946-06-26 12:30:42! That can’t be possible…

Googling time

That’s strange but there are only few relevant results returned by Google on that crippled timestamp. After some digging, I found out that FFmpeg issue. In fact, that’s the fourth result of a Google search on android video creation_time but it’s like always, you just need to find the good keywords.

Matter fixed

The ticket’s status is closed defect: fixed. OK but the issue tracker doesn’t contain any reference to the fix.

$ git clone git://source.ffmpeg.org/ffmpeg.git
$ git log --grep=1471
[…]
commit 23eeffcd48a15e73fb2649b712870b6d101c5471
Author: Michael Niedermayer <michaelni@gmx.at>
Date:   Sun Jul 1 21:41:06 2012 +0200

    mov: add workaround for incorrect 0 time point.

    Fixes Ticket1471

    Signed-off-by: Michael Niedermayer <michaelni@gmx.at>
[…]

$ git show 23eeffcd48a15e73fb2649b712870b6d101c5471
commit 23eeffcd48a15e73fb2649b712870b6d101c5471
Author: Michael Niedermayer <michaelni@gmx.at>
Date:   Sun Jul 1 21:41:06 2012 +0200

    mov: add workaround for incorrect 0 time point.

    Fixes Ticket1471

    Signed-off-by: Michael Niedermayer <michaelni@gmx.at>

diff --git a/libavformat/mov.c b/libavformat/mov.c
index af5b126..faa8c65 100644
--- a/libavformat/mov.c
+++ b/libavformat/mov.c
@@ -780,7 +780,8 @@ static void mov_metadata_creation_time(AVDictionary **metadata, time_t time)
     char buffer[32];
     if (time) {
         struct tm *ptm;
-        time -= 2082844800;  /* seconds between 1904-01-01 and Epoch */
+        if(time >= 2082844800)
+            time -= 2082844800;  /* seconds between 1904-01-01 and Epoch */
         ptm = gmtime(&time);
         if (!ptm) return;
         strftime(buffer, sizeof(buffer), "%Y-%m-%d %H:%M:%S", ptm);

Uh!

Compile time

ATTOW, the debian package version of libav-tools is 0.8.xx and a git log --tags --simplify-by-decoration --pretty="format:%ai %d" shows that the 0.9 tag has been bumped in the late 2011… Too bad… I’ll need to compile that myself if I really wanted that creation_date.

Update

The wording of the Compile time is unclear at least, so:

  • libav and FFmpeg has forked.

  • libav is now the default on Debian GNU/Linux.

  • libavformat/mov.c has evolved differently but FFmpeg seems to provide a fix (that must be verified) but that doesn’t mean that the issue has not been fixed in the libav edge, only that I’ve found only clue that the issue has been adressed by the FFmpeg project.

Update 2

I had filed a bug on libav just to found out that the issue had been fixed in Android 4.2.

Matter fixed!

The not so short guide to compile Android Jelly Bean 4.1.1_r4 for the Galaxy Nexus

Introduction

Back to the beginning of this year, I succeded in compiling ICS AOSP 4.0.3_r1 for the Galaxy Nexus. At that time, I didn’t have much time to post a full entry on the whole compilation process (including the different hacks needed to get a fully functional ROM).

With the Jelly Bean AOSP release and my third ROM compilation, I’ve succesfully compiled 4.1 back in July. Now we have 4.1.1_r4 and it’s due time for me to document the process using Debian GNU/Linux.

Without the Android Open Source Project, nothing has been possible and the website clearly describes how to proceed to get the code and compile it for a supported target. I will not rewrite the provided documentation but I’d rather provide a guide on how I have obtained my own ROM. Feel free to review and I will update accordingly.

DISCLAIMER: Please do not do anything described in this post if you have no idea of what you are doing! I will not be held responsible for bricking your phone or invalidate your device warranty! Do not forget to backup your data before doing anything since any issue may involve a factory reset!

My first advices are:

  • Browse the AOSP website
  • Read that whole post a first time before doing anything
  • Unlock your bootloader
  • Flash clockworkmod
  • Backup your ROM

Get the Android Open Source Project code

Follow the instructions given by the “Downloading the Source” section of the AOSP website.

$ mkdir ~/foo/bar/android
$ cd ~/foo/bar/android
$ repo init -u https://android.googlesource.com/platform/manifest -b android-4.1.1_r4
$ repo sync

These commands apply even to an existing Android source tree but you will need to clean up your source tree from a previous build by using:

$ make clobber

That’s the first step to get a JRO03L rom…

Prerequisites for hardware support

To get a fully functional rom, you need to get the hardware support. Unfortunately, we only have access to the binaries distributed for the JRO03H rom Galaxy Nexus (GSM/HSPA+) binaries for Android 4.1.1 (JRO03H):

$ mkdir ~/foo/android_prereq
$ cd ~/foo/android_prereq
$ export BINARIES="broadcom-maguro-jro03h-4cc54d09,imgtec-maguro-jro03h-827bcb4c,invensense-maguro-jro03h-682067a4,samsung-maguro-jro03h-0655880b"
$ curl -# -OOOO https://dl.google.com/dl/android/aosp/{${BINARIES}}.tgz
$ md5sum *
b05a41ed3096c5f19ebc5b172f034e93  broadcom-maguro-jro03h-4cc54d09.tgz
cabedd37a42a9cbe47e1702a122421e0  imgtec-maguro-jro03h-827bcb4c.tgz
c575cc3a712ef61d2ef5eb5e08f054eb  invensense-maguro-jro03h-682067a4.tgz
cda04a52492ee9762196248abb2834d1  samsung-maguro-jro03h-0655880b.tgz
$ unset BINARIES

Retrieve the installation scripts:

$ cd ~/foo/bar/android
$ find ~/foo/android_prereq -maxdepth 1 -type f -exec tar xzvf {} \;

Finally, it is the time to extract the binaries to the source tree:

$ for i in `ls -1 extract-*-maguro.sh`; do ./$i; done;

DISCLAIMER: You must accept the terms of each of the licences before going further!

Initialize the build environment

In the past, I’ve always compiled a userdebug rom. Now, I would like to get a user rom since I’m happy with an unthemed and odexed rom.

$ . build/envsetup.sh
$ lunch full_maguro-user

============================================
PLATFORM_VERSION_CODENAME=REL
PLATFORM_VERSION=4.1.1
TARGET_PRODUCT=full_maguro
TARGET_BUILD_VARIANT=user
TARGET_BUILD_TYPE=release
TARGET_BUILD_APPS=
TARGET_ARCH=arm
TARGET_ARCH_VARIANT=armv7-a-neon
HOST_ARCH=x86
HOST_OS=linux
HOST_OS_EXTRA=Linux-3.2.0-3-amd64-x86_64-with-debian-wheezy-sid
HOST_BUILD_TYPE=release
BUILD_ID=JRO03L
OUT_DIR=out
============================================

If you prefer a userdebug build, just do:

$ . build/envsetup.sh
$ lunch full_maguro-userdebug

============================================
PLATFORM_VERSION_CODENAME=REL
PLATFORM_VERSION=4.1.1
TARGET_PRODUCT=full_maguro
TARGET_BUILD_VARIANT=userdebug
TARGET_BUILD_TYPE=release
TARGET_BUILD_APPS=
TARGET_ARCH=arm
TARGET_ARCH_VARIANT=armv7-a-neon
HOST_ARCH=x86
HOST_OS=linux
HOST_OS_EXTRA=Linux-3.2.0-3-amd64-x86_64-with-debian-wheezy-sid
HOST_BUILD_TYPE=release
BUILD_ID=JRO03L
OUT_DIR=out
============================================

Flash the bootloader and the baseband firmware to the latest versions available

Get the latest factory image “yakju” for Galaxy Nexus “maguro” (GSM/HSPA+) JR003C

$ mkdir ~/foo/android_prereq/jro03c
$ cd ~/foo/android_prereq/jro03c
$ curl -#O https://dl.google.com/dl/android/aosp/yakju-jro03c-factory-3174c1e5.tgz

We will need fastboot to flash images to the device. More over, since it’s the time to compile some host utilities, we take the opportunity to get adb (necessary to interact with the device for debug purposes) and the simg2img (usefull to convert a sparse image file to a raw image file).

$ cd ~/foo/bar/android
$ make fastboot adb simg2img

The resulting host binaries could be found in the ~/foo/bar/android/out/host/linux-x86/bin directory.

If your bootloader is not already unlocked, you will need to boot in fastboot mode:

Press and hold both Volume Up and Volume Down, then press and hold Power.

AOSP Website Booting Into Fastboot Mode Section for the Maguro Device

We define a linux local alias to simplify the use of the brand new fastboot (using the _aosp suffix).

$ alias fastboot_aosp=/home/renaud/git/android_ics/out/host/linux-x86/bin/fastboot

Let’s get and flash the bootloader and the baseband from the JRO03C factory to the actual device:

$ cd ~/foo/android_prereq
$ tar xzvf yakju-jro03c-factory-3174c1e5.tgz
$ tar xzf yakju-jro03c-factory-3174c1e5.tgz
$ cd yakju-jro03c
$ fastboot_aosp flash bootloader bootloader-maguro-primelc03.img
$ fastboot reboot-bootloader
$ fastboot flash radio radio-maguro-i9250xxlf1.img
$ fastboot reboot-bootloader

The bootloader now display:

[…]
BOOTLOADER VERSION - PRIMELC03
BASEBAND VERSION - I9250XXLF1
[…]

Getting root or injecting su and Superuser to the source tree

Make a backup of the original su source:

$ mkdir ~/android_backup
$ cp -rf ~/foo/bar/android/system/extras/su .

Then inject ChainsDD/su-binary using my slightly modified fork:

$ cd ~/foo/bar/android/system/extras
$ git clone https://github.com/nibua-r/su-binary.git su

I want the su binary to be compiled in the very same time of the full AOSP build. As a consequence, I have modified the original Android.mk file by setting LOCAL_MODULE_TAGS := optional and adding the su module to PRODUCT_PACKAGES into the build/target/product/core.mk file to force the inclusion.

I want to have the ChainsDD/Superuser apk included into the build as a system app:

$ cd ~/foo/bar/android/packages/apps
$ git clone https://github.com/nibua-r/Superuser.git

and add Superuser to PRODUCT_PACKAGES into the build/target/product/core.mk.

You will need to generate your own certificate for Superuser using:

$ cd ~/foo/bar/android
$ development/tools/make_key superuser '/C=US/ST=State/L=Location/O=YourOrg/OU=WhateverYouWant/CN=WhateverYouWant/emailAddress=root@example.org'
$ mv superuser.pk8 build/target/product/security/

If superuser.pk8 is not there, the compilation process will fail… Don’t go too far away from your computer since the Superuser compilation will require your password to get the build done.

Time to compile

You’ll need to read the building section of the AOSP website as a prerequisite.

After setting ccache to fit with your resources, just do:

$ cd ~/foo/bar/android
$ make -j4

You normally end up with flashable images located in out/target/product/maguro/. The whole compilation process took a very long time on my machine… so be it and be warned!

Flash the images to the devices

Assuming the fastboot_aosp alias is defined and the battery of your device is decently charged, just flash the whole system with:

$ fastboot_aosp flashall

If you upgrade from a previous Android version, you should wipe your data using the -w switch:

$ fastboot_aosp -w flashall

The whole flashing process took about 1 min. to finish on rebooting the device. Congrats! But wait, this is not the end…

If you have clockworkmod installed on your recovery partition, flashall will overwrite it with the nominal android recovery.

Flash or reflash clockworkmod recovery (for later use)

The clockworkmod recovery enable us to backup and/or restore our ROMs and apply zip update files.

Get the Koushik Dutta’s clockworkmod recovery (direct link ATTOW).

Some hardware parts are not properly supported!

If you test the gps or the camera, you certainly are already grumbling…

Some binaries are distributed by Google, some aren’t and since the famous extract-files.sh have been suppressed from this release, you will have to hunt down some missing libraries. You’ll need to play with find/sort/diff combos to figure out which one are needed.

OK, but on which reference data? Remember the simg2img host compilation? Time to use it…

Some inoffensive linux commands:

$ simg2img system.img system.img.raw
$ mkdir mnt-point
$ sudo mount -t ext4 -o loop system.img.raw mnt-point/

Please read the following section before hunting down the missing files…

Where are my GApps!

The GApps are not-so-useless on Android but they are not Open Source and therefore not distributed by Google AFAIK… Considering the situation, Google seems flexible, up to now, about the various gapps packagings out there…

The short version: Go get a gapps update file on Goo.im/gapps (gapps-jb-20120726-signed.zip ATTOW). Use adb to put that file on the device storage, reboot to clockworkmod and apply…

You should (in not must) locally unzip the file to see what’s into it before flashing and before processing the previous section…

Conclusion

That post is more a personal log than a real guide and although some parts could be automated, the manual toying is a full learning experience!

Have fun!

Integrate google-code-prettify to Enki blog (i.e. a rails 3 application)

This blog is powered by Enki and as technical blog, I’d like to insert some code snippets to the entries. As a rails 3 application, integrating google-code-prettify is relatively simple.

First download the installation bundle (I experienced some difficulties, with perl, to make a distribution package from the svn):

$ curl --progress-bar -o prettify-1-Jun-2011.tar.bz2 \
    http://google-code-prettify.googlecode.com/files/prettify-1-Jun-2011.tar.bz2

Now, copy the .js and the pretiffy.css to your rails application public folder:

$ cd google-code-prettify/distrib/google-code-prettify
$ cp *.js /path/to/my/railsapp/public/javascripts/
$ cp prettify.css public/stylesheets/

You can use one of the other styles by using the corresponding .css file.

Insert <%= stylesheet_link_tag 'prettify' %> and <%= javascript_include_tag 'prettify' %> within the head element of your /path/to/my/railsapp/app/views/layouts/application.html.erb file. Add the onLoad="prettyPrint()" attribute to the body element.

Test and enjoy the efficient syntax highlighting!

ViewPager & the verticality of Fragments

Back to august 2010, Rich Hyndman posts an entry on the android developers’ blog entitled “Horizontal View Swiping with ViewPager”. I already was really enthusiastic about the Android Support Package at that time because of the introduction of the LoaderManager which save us big headaches on the proper cursor management (but that’s another story). In an application of mine, I’ve used an onFling/swipe gesture based on some 2009 codes found on the Web (codeshogun.com and/or ceveni.com for example).

The main drawbacks on my onFling approach and implementation are:

  1. That is based on a threshold velocity
  2. Once initiated, the animation goes on and can’t be reversed
  3. The gesture is the trigger of a startActivity call and each of those activities are… activities!

Surely my onFling implementation was too dumb and since I read Rich’s post, I have the secret personal project to use the ViewPager and one of the underlying adapters to reimplement that ugly onFling part of our application.

Well, the time has come with the Christmas holidays!

The point is that my onFling-based activity is now composed by two fragment and the FragmentPagerAdapter is nice but manage only one Fragment as a ViewPager current page content.

Now, what if a want a vertical stack of several fragments? ⇒ Read and adapt the FragmentPagerAdapter source code from the Support Package (compact version without comments):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
public abstract class VerticalFragmentsPagerAdapter extends PagerAdapter {
    private static final String TAG = "VerticalFragmentsPagerAdapter";
    private static final boolean DEBUG = false;
    private final FragmentManager mFragmentManager;
    private final int mRows;
    private FragmentTransaction mCurTransaction = null;
    private ArrayList<Fragment> mCurrentPrimaryItem = null;

    public VerticalFragmentsPagerAdapter(FragmentManager fm, int rows) {
        mFragmentManager = fm;
        mRows = rows;
        mCurrentPrimaryItem = new ArrayList<Fragment>(mRows);
    }

    /**
     * Return the Fragments associated with a specified position.
     */
    public abstract ArrayList<Fragment> getItem(int position);

    @Override
    public void startUpdate(ViewGroup container) {}

    @Override
    public Object instantiateItem(ViewGroup container, int position) {
        if (mCurTransaction == null) {
            mCurTransaction = mFragmentManager.beginTransaction();
        }

        Context ctx = container.getContext();
        LayoutInflater inflater =
                (LayoutInflater) ctx.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        View layout = inflater.inflate(R.layout.pager, null);
        layout.setId(6109 + position);
        LinearLayout ll = (LinearLayout) layout.findViewById(R.id.pager_linear_layout);
        ll.setId(900913 + position);
        ((ViewPager) container).addView(layout);
        Fragment firstFragment =
                mFragmentManager.findFragmentByTag(
                        makeFragmentName(container.getId(), position, 0));
        ArrayList<Fragment> frags = null;

        if (firstFragment != null) {
            frags = new ArrayList<Fragment>(mRows);

            for (int i = 0; i < mRows; ++i) {
                frags.add(mFragmentManager.findFragmentByTag(
                        makeFragmentName(container.getId(), position, i)));
            }

            for (Fragment f : frags) {
                if (DEBUG) Log.v(TAG, "Attaching item #" + position + ": f=" + f);
                mCurTransaction.attach(f);
            }
        } else {
            frags = getItem(position);
            int currentRow = 0;

            for (Fragment f : frags) {
                if (DEBUG) Log.v(TAG, "instanciateItem / Adding item #" + position + ": f= " + f);
                mCurTransaction.add(ll.getId(), f,
                        makeFragmentName(container.getId(), position, currentRow));
                currentRow++;
            }
        }

        if (frags != mCurrentPrimaryItem) {
            for (Fragment f : frags) {
                f.setMenuVisibility(false);
                f.setUserVisibleHint(false);
            }
        }

        return layout;
    }

    @Override
    public void destroyItem(ViewGroup container, int position, Object object) {
        if (mCurTransaction == null) {
            mCurTransaction = mFragmentManager.beginTransaction();
        }

        for (int i = 0; i < mRows; ++i) {
            if (DEBUG) Log.v(TAG, "destroyItem / Detaching item #" + position + ": row=" + i);
            mCurTransaction.detach(
                    mFragmentManager.findFragmentByTag(
                            makeFragmentName(container.getId(), position, i)));
        }
        ((ViewPager) container).removeView((View)object);
    }

    @Override
    public void setPrimaryItem(ViewGroup container, int position, Object object) {
        ArrayList<Fragment> frags = new ArrayList<Fragment>(mRows);

        for (int i = 0; i < mRows; ++i) {
            frags.add(mFragmentManager.findFragmentByTag(
                    makeFragmentName(container.getId(), position, i)));
        }

        if (!frags.equals(mCurrentPrimaryItem)) {
            if (!mCurrentPrimaryItem.isEmpty() && (mCurrentPrimaryItem.get(0) != null)) {
                for (Fragment f : mCurrentPrimaryItem) {
                    f.setMenuVisibility(false);
                    f.setUserVisibleHint(false);
                }
            }

            if (!frags.isEmpty() && (frags.get(0) != null)) {
                for (Fragment f : frags) {
                    f.setMenuVisibility(true);
                    f.setUserVisibleHint(true);
                }
            }
            mCurrentPrimaryItem = (ArrayList<Fragment>) frags.clone();
            if (DEBUG) Log.v(TAG, "setPrimaryItem #" + position + ": " + mCurrentPrimaryItem);
        }
    }

    @Override
    public void finishUpdate(ViewGroup container) {
        if (mCurTransaction != null) {
            mCurTransaction.commitAllowingStateLoss();
            mCurTransaction = null;
            mFragmentManager.executePendingTransactions();
        }
    }

    @Override
    public boolean isViewFromObject(View view, Object object) { return view == ((View) object); }

    @Override
    public Parcelable saveState() { return null; }

    @Override
    public void restoreState(Parcelable state, ClassLoader loader) {}

    public static String makeFragmentName(int viewId, int index, int subindex) {
        return "android:switcher:" + viewId + ":" + index + ":" + subindex;
    }
}

This implementation uses ArrayList as the fragments’ container. The trick here is that instead of returning a fragment from instanciateItem, I return a containing a LinearLayout.

Be warned: don’t use several instances of the same fragment for a given position! That adapter is not designed to do that…

From the above code, it’s now trivial to figure out how to extends that adapter: “Uh… the getItem should return an ArrayList of Fragments?” − “Yup!”

A trivial example is available on github. Just clone and run ant debug && ant installd… Feel free to fork, improve and return your feedback.

Add Google Analytics to Enki blog (i.e. a rails 3 application)

This blog is powered by Enki and I’d like to monitor the (absence of?) frequentation. As a rails 3 application, integrating Google Analytics is dead simple thanks to the rack-google_analytics ruby gem and the associated documentation

Just add the following to your Gemfile:

1
2
3
group :production do
  gem 'rack-google_analytics', :require => "rack/google_analytics"
end

and this configuration entry to your config/environments/production.rb:

1
2
# Configure Rack::GoogleAnalytics
config.middleware.use("Rack::GoogleAnalytics", :web_property_id => "UA-12345678-1")

with the web_property_id provided by Analytics.

I told you: dead simple!

QR codes − From strings to a full-fledged pdf file

Last days at work, we integrate some QR codes recognition development on android using ZXing. Although we have a special printer, we need some test cases and a standalone solution to generate and print out several QR codes on a standard printer. At first, a colleague of mine has used some online QR generator but it does not scale very well. Fortunately, a C library and a derived command line tool exists in the libqrencode project, thanks to Kentaro Fukuchi. Moreover, that’s packaged on debian…

qrencode is the right tool, the unix way. However, our use case requires that we get an organized output for testing. To be more specific, we need to sequentially recognize several sets of QR codes.

Black box description

The input is a sequence of strings. As for the output, I need to obtain a standalone printed document. As a consequence, I choose to use the pdf format to not be bothered with any printing issue, even if the content is pure raster.

Input data sample

I will not provide there my real data. The sample data are:

  1. Lorem ipsum dolor sit amet duis mattis ullamcorper ultricies aliquam sit amet vestibulum augue
  2. Aenean ut orci eget nisi tincidunt pulvinar
  3. Aenean eleifend tellus ac lacus convallis fermentum

Be prepared…

I have already used ImageMagick in the past to do composition and conversion operations and my feelings at the beginning were that ImageMagick is all I need to get my formatted output.

Obviously, we need to install qrencode and ImageMagick (and the associated verbose documentation):

$ sudo apt-get install qrencode imagemagick imagemagick-doc

But, wait, I need a multipage pdf in the end. I certainly need some pdf merging tool. Go with the installation of pdfsam then:

$ sudo apt-get install pdfsam

QR codes generation with human-readable label

Pretty straightforward. An example on the first dataset:

1
2
3
4
5
6
for i in lorem ipsum dolor sit amet duis mattis ullamcorper
do
    qrencode -l Q -s 20 -m 10 -o $i.png $i
    composite -geometry +0+40 -font Lucida-Sans-Typewriter-Regular -pointsize 40 \
        -gravity north label:"${i}" $i.png $i.png
done

The qrencode call is to generate each png from a single string. The -l Q option is to set the error correction level to Q (25% of codewords can be restored), -s 20 is to specify the size of dot and -m 10 is to set the margin width.

ImageMagick composite is used to add a human-readable label to each png. The Lucida-Sans-Typewriter-Regular font is provided by the sun-java6-fonts debian package. You can use whatever font you like as listed by identify -list font.

Generate a multipage pdf file for each dataset

From the generated png files, we use ImageMagick montage to arrange a multipage pdf (2 rows / 3 columns) with the following command:

$ montage -page A4+20-20 -tile 2x3 -geometry +1+1 \
  -transparent white 'input files' \
  -font Lucida-Sans-Typewriter-Regular -pointsize 40 \
  -title "A title" output.pdf

with input files the list of the png input files. The -transparent white option is really important to enable the correct insertion of the title. Without that option, the title would be overridden by the white pixels of the input files.

Putting it all together and merge the result to a standalone pdf file

Once we have the intermediate pdf files, it is simple to merge them into one using pdfsam-console. The final quick-and-dirty bash script is:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
#!/bin/bash

LABEL=("first" "second" "third")
DATA=("Lorem ipsum dolor sit amet duis mattis ullamcorper ultricies aliquam sit amet vestibulum augue" \
    "Aenean ut orci eget nisi tincidunt pulvinar" \
    "Aene eleifend tellus ac lacus convallis fermentum")

dataLen=${#DATA[@]}

FILE=()

for (( j=0; j<${dataLen}; j++ ));
do
    for i in ${DATA[$j]}
    do
        # Generate the QR codes
        qrencode -l Q -s 5 -m 50 -o $i.png $i
        # Add a human-readable label
        composite -geometry +0+40 -font Lucida-Sans-Typewriter-Regular -pointsize 40 \
            -gravity north label:"${i}" $i.png $i.png
        FILE[$j]="${FILE[$j]} ${i}.png"
    done
    # Assemble them into a pdf
    montage -page A4+20-20 -tile 2x3 -geometry +1+1 -transparent white ${FILE[$j]} \
        -font Lucida-Sans-Typewriter-Regular -pointsize 40 \
        -title "${LABEL[$j]}" ${LABEL[$j]}.pdf
done

# Create the final pdf
if [ -f output.pdf ]; then rm -rf $PWD/output.pdf; fi;
pdfsam-console -o $PWD/output.pdf -d $PWD/ concat

# Delete all the intermediate files
for i in ${DATA[@]}; do rm -rf ${i}.png ;done;
for (( i=0; i<${dataLen}; i++ )); do rm -rf ${LABEL[$i]}.pdf; done;

Check out the resulting pdf file… Et voilà !

Build and setup GNU Emacs 24 from git the alternatives way on Debian GNU/Linux

Get the source from the Savannah git repository

I am just interested in the recent history and would like to save some precious bandwidth, so I just use --depth 1 to get only what is needed:

$ git clone --depth 1 git://git.savannah.gnu.org/emacs.git

Build

An ./autogen.sh is needed to generate the configure script:

$ cd emacs
$ ./autogen.sh

I have some emacs binary packages installed, so I have chosen to install my new emacs to /usr/local. Moreover, I have the gtk3 dev packages. As a consequence, my configure call is:

$ ./configure --prefix=/usr/local --with-x-toolkit=gtk3

At this step, you will have to retrieve the required and wanted libWhatTheF**k-dev dependencies by yourself… For me, the configure step returns with:

Configured for `x86_64-unknown-linux-gnu'.

  Where should the build process find the source code?    /foo/bar/git/emacs
  What operating system and machine description files should Emacs use?
        `s/gnu-linux.h' and `m/amdx86-64.h'
  What compiler should emacs be built with?               gcc -std=gnu99 -g -O2
  Should Emacs use the GNU version of malloc?             yes
      (Using Doug Lea's new malloc from the GNU C Library.)
  Should Emacs use a relocating allocator for buffers?    no
  Should Emacs use mmap(2) for buffer allocation?         no
  What window system should Emacs use?                    x11
  What toolkit should Emacs use?                          GTK
  Where do we find X Windows header files?                Standard dirs
  Where do we find X Windows libraries?                   Standard dirs
  Does Emacs use -lXaw3d?                                 no
  Does Emacs use -lXpm?                                   yes
  Does Emacs use -ljpeg?                                  yes
  Does Emacs use -ltiff?                                  yes
  Does Emacs use a gif library?                           yes -lgif
  Does Emacs use -lpng?                                   yes
  Does Emacs use -lrsvg-2?                                yes
  Does Emacs use imagemagick?                             yes
  Does Emacs use -lgpm?                                   yes
  Does Emacs use -ldbus?                                  yes
  Does Emacs use -lgconf?                                 yes
  Does Emacs use GSettings?                               yes
  Does Emacs use -lselinux?                               yes
  Does Emacs use -lgnutls (2.6.x or higher)?              yes
  Does Emacs use -lxml2?                                  yes
  Does Emacs use -lfreetype?                              yes
  Does Emacs use -lm17n-flt?                              yes
  Does Emacs use -lotf?                                   yes
  Does Emacs use -lxft?                                   yes
  Does Emacs use toolkit scroll bars?                     yes

Now, just compile, install and fix blessmail as required using:

$ make all && make install
$ sudo lib-src/blessmail /usr/local/libexec/emacs/24.0.91/x86_64-unknown-linux-gnu/movemail

Configure Debian GNU/Linux to use your brand new emacs

Rename your new binaries with the following bash script (reusable after each recompilation):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
for i in etags ctags emacsclient ebrowse rcs-checkin grep-changelog emacs
do
    if [ -f /usr/local/bin/$i ]; then
        if [ -f /usr/local/bin/$i.emacs24 ]; then
            rm /usr/local/bin/$i.emacs24
        fi
        mv /usr/local/bin/$i /usr/local/bin/$i.emacs24
    fi
done

if [ -f /usr/local/bin/emacs.emacs24 ]; then
    if [ -f /usr/local/bin/emacs24-x ]; then
        rm /usr/local/bin/emacs24-x
    fi
    ln -s /usr/local/bin/emacs.emacs24 /usr/local/bin/emacs24-x
fi

Install the ad hoc new alternatives (needed just once, even if you recompile and reinstall emacs after a git pull origin):

sudo update-alternatives --install /usr/bin/emacs emacs \
  /usr/local/bin/emacs24-x 30 \
  --slave /usr/share/icons/hicolor/128x128/apps/emacs.png emacs-128x128.png \
  /usr/local/share/icons/hicolor/128x128/apps/emacs.png \
  --slave /usr/share/icons/hicolor/16x16/apps/emacs.png emacs-16x16.png \
  /usr/local/share/icons/hicolor/16x16/apps/emacs.png \
  --slave /usr/share/icons/hicolor/24x24/apps/emacs.png emacs-24x24.png \
  /usr/local/share/icons/hicolor/24x24/apps/emacs.png \
  --slave /usr/share/icons/hicolor/32x32/apps/emacs.png emacs-32x32.png \
  /usr/local/share/icons/hicolor/32x32/apps/emacs.png \
  --slave /usr/share/icons/hicolor/48x48/apps/emacs.png emacs-48x48.png \
  /usr/local/share/icons/hicolor/48x48/apps/emacs.png \
  --slave /usr/share/icons/hicolor/scalable/mimetypes/emacs-document.svg emacs-document.svg \
  /usr/local/share/icons/hicolor/scalable/mimetypes/emacs-document.svg \
  --slave /usr/share/man/man1/emacs.1.gz emacs.1.gz \
  /usr/local/share/man/man1/emacs.1.gz \
  --slave /usr/share/icons/hicolor/scalable/apps/emacs.png emacs.svg \
  /usr/local/share/icons/hicolor/scalable/apps/emacs.svg

sudo update-alternatives --install /usr/bin/etags etags \
  /usr/local/bin/etags.emacs24 35 \
  --slave /usr/share/man/man1/etags.1.gz etags.1.gz \
  /usr/local/share/man/man1/etags.1.gz

sudo update-alternatives --install /usr/bin/ctags ctags \
  /usr/local/bin/ctags.emacs24 35 \
  --slave /usr/share/man/man1/ctags.1.gz ctags.1.gz \
  /usr/local/share/man/man1/ctags.1.gz

sudo update-alternatives --install /usr/bin/emacsclient emacsclient \
  /usr/local/bin/emacsclient.emacs24 30 \
  --slave /usr/share/man/man1/emacsclient.1.gz emacsclient.1.gz \
  /usr/local/share/man/man1/emacsclient.1.gz

sudo update-alternatives --install /usr/bin/ebrowse ebrowse \
  /usr/local/bin/ebrowse.emacs24 30 \
  --slave /usr/share/man/man1/ebrowse.1.gz ebrowse.1.gz \
  /usr/local/share/man/man1/ebrowse.1.gz

sudo update-alternatives --install /usr/bin/rcs-checkin rcs-checkin \
  /usr/local/bin/rcs-checkin.emacs24 30 \
  --slave /usr/share/man/man1/rcs-checkin.1.gz rcs-checkin.1.gz \
  /usr/local/share/man/man1/rcs-checkin.1.gz

sudo update-alternatives --install /usr/bin/grep-changelog \
  grep-changelog /usr/local/bin/grep-changelog.emacs24 30 \
  --slave /usr/share/man/man1/grep-changelog.1.gz grep-changelog.1.gz \
  /usr/local/share/man/man1/grep-changelog.1.gz

The given priorities are higher than the other alternatives. To finish, I set all those alternatives to the automatic mode:

for i in ctags etags emacs emacsclient ebrowse rcs-checkin grep-changelog
do
    sudo update-alternatives --auto $i
done

Now, my new emacs will always be the default choice, even if I upgrade my distro and the emacs binary packages!

Bonus point?

The default editor is ruled by two alternatives on my system: editor and gnome-text-editor. I set them up to use my new emacs by default.

Et voilà !

Android − My life in gray!

After some existencial questions on how the gray level colors should be defined in our android projects, I’ve decided to check the predefined colors on the android platform:

$ find . -type f -name "colors.xml" -print0 | xargs -0 grep grey
$ find . -type f -name "colors.xml" -print0 | xargs -0 grep gray
./platforms/android-4/data/res/values/colors.xml:    <color name="lighter_gray">#ddd</color>
./platforms/android-4/data/res/values/colors.xml:    <color name="darker_gray">#aaa</color>
[…]
./platforms/android-10/data/res/values/colors.xml:    <color name="lighter_gray">#ddd</color>
./platforms/android-10/data/res/values/colors.xml:    <color name="darker_gray">#aaa</color>
[…]

Ok, the first conclusion is that the android platform definetely used a US convention… No surprises there. Two gray level colors are defined in platforms. Now, I should check on the android developers website: here and there and search for gray level colors. According to this documentation, only darker_gray seems to be public, although lighter_gray is defined within data/res/values/colors.xml

Ok, make a sample stupid project:

$ android create project --name Grayish \
 --target android-10 --path /foo/bar/Grayish \
 --package org.summo.grayish --activity MyLifeInGray

Let’s define some colors in the project res/values/colors.xml file:

1
2
3
4
5
6
<?xml version="1.0" encoding="utf-8"?>
<resources>
  <color name="lighter_gray">#ddd</color>
  <color name="darker_gray">#aaa</color>
  <color name="gray_40">#696969</color>
</resources>

Use them (res/layout/main.xml file):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              android:orientation="vertical"
              android:layout_width="fill_parent"
              android:layout_height="fill_parent" >
  <TextView
      android:background="@android:color/darker_gray"
      android:layout_width="fill_parent"
      android:layout_height="wrap_content"
      android:text="@android:color/darker_gray"
      android:textColor="@android:color/black"
      />
  <TextView
      android:background="@color/lighter_gray"
      android:layout_width="fill_parent"
      android:layout_height="wrap_content"
      android:text="@color/lighter_gray"
      android:textColor="@android:color/black"
      />
  <TextView
      android:background="@color/darker_gray"
      android:layout_width="fill_parent"
      android:layout_height="wrap_content"
      android:text="@color/darker_gray"
      android:textColor="@android:color/black"
      />
  <TextView
      android:background="@color/gray_40"
      android:layout_width="fill_parent"
      android:layout_height="wrap_content"
      android:text="@color/gray_40"
      android:textColor="@android:color/black"
      />
</LinearLayout>

Compile and install the project on your device:

$ ant install

A ddms screenshot and a cropping later:

That’s weird but lighter_gray is not public… We have access to only one gray system color! To conclude: although we could use the system colors, and since one and only one color is just useless, we would rather define lighter_gray and darker_gray in our own res/values/colors.xml file and if we need more grayish colors, we would use a gray_xx, xx corresponding to the percentage of white color.

Et voilà !

Handling the GNU Compiler Collection with debian alternatives

The story so far: I have several versions of gcc and g++ on my debian distro and during some external project compilation I went to version-related compilation issues. OK, gcc should be managed by the alternatives debian stuff…

Just after a $ sudo update-alternative --config gcc[tab][tab], I discovered that gcc, g++, gcov, etc. are NOT managed the alternatives way… OK, there are some alternatives defined for c++, cc and cpp, but these alternatives are symbolic links to… symbolic links:

foo@bar:/etc/alternatives$ ls -al c++ cc cpp
lrwxrwxrwx 1 root root 12  2 avril  2011 c++ -> /usr/bin/g++
lrwxrwxrwx 1 root root 12  2 avril  2011 cc -> /usr/bin/gcc
lrwxrwxrwx 1 root root 12  1 août  23:41 cpp -> /usr/bin/cpp
foo@bar:/etc/alternatives$ ls -al /usr/bin/g++ /usr/bin/gcc /usr/bin/cpp
lrwxrwxrwx 1 root root 7  1 août  23:32 /usr/bin/cpp -> cpp-4.6
lrwxrwxrwx 1 root root 7  9 juil. 19:39 /usr/bin/g++ -> g++-4.6
lrwxrwxrwx 1 root root 7  9 juil. 19:39 /usr/bin/gcc -> gcc-4.6

It’s really really strange since c++ and cc are generic but not cpp. Here is my proposal to have a better management for cpp, gcc, g++ and gcov, assuming that you have 4.4 and 4.6 versions on your system:

foo@bar:/etc/alternatives$ sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-4.6 10
foo@bar:/etc/alternatives$ sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-4.4 1
foo@bar:/etc/alternatives$ sudo update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-4.6 10
foo@bar:/etc/alternatives$ sudo update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-4.4 1
foo@bar:/etc/alternatives$ sudo update-alternatives --install /usr/bin/gcov gcov /usr/bin/gcov-4.6 10
foo@bar:/etc/alternatives$ sudo update-alternatives --install /usr/bin/gcov gcov /usr/bin/gcov-4.4 1
foo@bar:/etc/alternatives$ sudo rm /usr/bin/cpp
foo@bar:/etc/alternatives$ sudo update-alternatives --install /usr/bin/cpp cpp /usr/bin/cpp-4.6 10
foo@bar:/etc/alternatives$ sudo update-alternatives --install /usr/bin/cpp cpp /usr/bin/cpp-4.4 1

Obviously, you should adapt the previous commands to your system (i.e. which versions are present).

Now, when you need gcc-4.4 for a specific compilation and if $ export CC=/usr/bin/gcc-4.4 is not sufficient, you just have to do $ sudo update-alternative --config gcc and manually select gcc-4.4… to reset to gcc-4.6 if you nominally need it.

That’s it!

Rails 3: Migrate your data from SQLite to PostgreSQL

Using my brand new enki blog instance with a SQLite database, I wrote my first post… then I told myself that it could be nice to use a PostgreSQL database, just for fun…

Google and a stackoverflow entry gives me the answer, here the step-by-step result for a RAILS_ENV="production":

  • Install PostgreSQL (see my previous post Postgresql installation on debian Wheezy)
  • $ cp config/database.yml config.database.yml.sqlite3
  • Install yaml_db: $ sudo gem install yaml_db and add that gem to your Gemfile
  • Add the PostgreSQL support: $ sudo gem install pg and add that gem to your Gemfile
  • Add a new config/database.yml.pg file:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
# PostgreSQL version
#   gem install pg
development:
  adapter: postgresql
  encoding: unicode
  database: blog_development
  pool: 5
  username: blog
  password: blurp
  host: localhost

# Warning: The database defined as "test" will be erased and
# re-generated from your development database when you run "rake".
# Do not set this db to the same as development or production.
test:
  adapter: postgresql
  encoding: unicode
  database: blog_test
  pool: 5
  username: blog
  password: blurp
  host: localhost

production:
  adapter: postgresql
  encoding: unicode
  database: blog_production
  pool: 5
  username: blog
  password: blurp
  host: localhost
  • Backup your SQLite data with yaml_db: $ rake db:dump RAILS_ENV="production"
  • Configure Rails to use your new PostgreSQL configuration: $ cp config/database.yml.pg config/database.yml
  • Create the database and the associated tables: $ rake db:create RAILS_ENV="production"
  • Load your schema: $ rake db:schema:load RAILS_ENV="production"
  • Load your data with yaml_db:$ rake db:load RAILS_ENV="production"

Et voilà !