Sunday, September 9, 2012

Android Layout. Dot the i's and cross the t's.

Most likely, you met Android Layouts during your first hours of android development. Android Layouts are primary and most important part of UI toolkit and it's very important to understand 'background' details and be aware about pitfalls to use them most efficiently.
I guess everyone know basic theory about layouts and how to use them, so this part will be ignored. The only thing need to noted is that as the basis of all layouts is ViewGroup, which holds other views (children) and defines LayoutParams - information used by views to tell their parents how they want to be laid out.
Now I propose to go directly to certain layout and it's implementation details.


    AbsoluteLayout

The main idea of AbsoluteLayout is to place each control at an absolute position. Here is source code of onLayout method which which performs children positioning:

@Override  
   protected void onLayout(boolean changed, int l, int t,  
       int r, int b) {  
     int count = getChildCount();  
     for (int i = 0; i < count; i++) {  
       View child = getChildAt(i);  
       if (child.getVisibility() != GONE) {  
         AbsoluteLayout.LayoutParams lp =  
             (AbsoluteLayout.LayoutParams) child.getLayoutParams();  
         int childLeft = mPaddingLeft + lp.x;  
         int childTop = mPaddingTop + lp.y;  
         child.layout(childLeft, childTop,  
             childLeft + child.getMeasuredWidth(),  
             childTop + child.getMeasuredHeight());  
       }  
     }  
   }  

It's quite obvious: every child is positioning on specified position (or left top point f layout if coordinates is not specified explicitly) considering position of parent layout and padding inside layout. Absolutely positioning makes your UI inflexible, so in most cases, it's not acceptable due to fragmentation of android devices.
AbsoluteLayout is simplest to use (and to implement), but due to significant restrictions it's very rarely used.

    FrameLayout

FrameLayout is used to display a single item at a time. You can have a few elements within a FrameLayout, but each element will be positioned based on the top left point of the screen. Elements, that overlaped by 'top' element, will be displayed overlapping. Child views are drawn in a stack, with the most recently added child on top, i.e. first view added to the frame layout will display on the bottom of the stack, and the last view added will display on top.
Frame layouts  usually used when you need to overlap views another views of your user interface. For example, you have application, which rendering some graphical data. In our case it will be newspaper. You have preview, which can be loaded immediately, while image with good quality requires some time to load and prepare. While second image is loading you can place spinner at the corner of preview to show, that 'load' operation in progress, something like this:

<?xml version="1.0" encoding="utf-8"?>  
 <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"  
        android:orientation="vertical"  
        android:layout_width="fill_parent"  
        android:layout_height="fill_parent"  
        android:padding="20dp"  
     >  
   <!--  
     UI elements...  
   -->  
   <ProgressBar  
       android:layout_width="wrap_content"  
       android:layout_height="wrap_content"  
       android:layout_gravity="right"  
   />  
 </FrameLayout>  

And how it will looks like:



After image with good quality is ready we can hide progress spinner and replace image in layout:


As we can see, frame layout is perfect choice for such situations. It allows us to overlap 'underlying' layer, but not block it, so user can start to interact with application.
So, now let's take a look on layout algorithm. At first step borders of FrameLayout are calculated:

@Override  
   protected void onLayout(boolean changed, int left, int top, int right, int bottom) {  
     final int count = getChildCount();  
     final int parentLeft = getPaddingLeftWithForeground();  
     final int parentRight = right - left - getPaddingRightWithForeground();  
     final int parentTop = getPaddingTopWithForeground();  
     final int parentBottom = bottom - top - getPaddingBottomWithForeground();  

Next, start loop through layout children to place them. For every non GONE element we will calculate left top point considering element's gravity:

for (int i = 0; i < count; i++) {  
       final View child = getChildAt(i);  
       if (child.getVisibility() != GONE) {  
         final LayoutParams lp = (LayoutParams) child.getLayoutParams();  
         ...

Left point is calculated according to horizontal gravity.

  final int layoutDirection = getResolvedLayoutDirection();  
     final int absoluteGravity = Gravity.getAbsoluteGravity(gravity, layoutDirection);  
     final int verticalGravity = gravity & Gravity.VERTICAL_GRAVITY_MASK;  
     switch (absoluteGravity & Gravity.HORIZONTAL_GRAVITY_MASK) {  
        case Gravity.LEFT:  
           childLeft = parentLeft + lp.leftMargin;  
           break;  
        case Gravity.CENTER_HORIZONTAL:  
           childLeft = parentLeft + (parentRight - parentLeft - width) / 2 +  
           lp.leftMargin - lp.rightMargin;  
           break;  
        case Gravity.RIGHT:  
           childLeft = parentRight - width - lp.rightMargin;  
           break;  
        default:  
           childLeft = parentLeft + lp.leftMargin;  
     }  

As we can see, by default, view is positioned to left corner (of course, margins included). Element width and parent borders are used if element must be positioned to the right corner or centered horizontally. Almost the same picture we have for top point of view (in this case default vertical gravity is TOP):

         switch (verticalGravity) {  
           case Gravity.TOP:  
             childTop = parentTop + lp.topMargin;  
             break;  
           case Gravity.CENTER_VERTICAL:  
             childTop = parentTop + (parentBottom - parentTop - height) / 2 +  
             lp.topMargin - lp.bottomMargin;  
             break;  
           case Gravity.BOTTOM:  
             childTop = parentBottom - height - lp.bottomMargin;  
             break;  
           default:  
             childTop = parentTop + lp.topMargin;  
         }  
         child.layout(childLeft, childTop, childLeft + width, childTop + height);  

and in the end, after we know coordinate of top left point of child view, will will call layout method to assign size and position to a view and all of its descendants.
So, FrameLayout is quite obvious and predictable. It's also really flexible, that's why it's used much often than AbsoluteLayout.

LinearLayout

The linear layout works as its name implies: it places elements in linear or vertical direction. When the layout’s orientation is set to vertical, all child elements within it are organized in a single column; when the layout’s orientation is set to horizontal, all child elements within it are placed in a single row. Due to its obviousness and simplicity LinearLayout is most commonly used layout manager. Unfortunately, linear layouts quite often used in situations, where another layout managers can solve this problem easily. LinearLayout may lead to a more complex and cumbersome layout description (your xml resources), which means creation of useless UI elements. Example will be considered below. 
Moment with size of elements within linear layout should be considered separately. Generally speaking, linear layout doesn't guarantee rubber layout. If elements have precise size (not one of FILL_PARENT(MATCH_PARENT), WRAP_CONTENT) it's possible situation, when their total size is greater than visible space of screen. Such cases breaks 'rubber layout' concept. But with linear layout we can avoid such situations, if we can find proportional coefficient (relative to the other elements) to replace hard coded size. Such technique in linear layout conception called weight. The weight of a child controls 'importance' of this element, i.e. how much  “space” its given within its parent linear layout. By the way, unlike other attributes of linear layout , which are applied to the linear layout view itself, this attribute applies to its child elements. 
The weight values should be numbers: max number for most prior item and min number for the lowest-priority element. You don't have to care about total size of values (I've read a few times, that sum of values must equals 1, but this is not true). You can specify weightSum for parent layout by yourself, but If  you will left it unspecified  - it will be computed at layout/measure time by adding the layout_weight of each child. And simple example:

 <?xml version="1.0" encoding="utf-8"?>  
 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"  
        android:orientation="horizontal"  
        android:layout_width="fill_parent"  
        android:layout_height="fill_parent"  
        android:baselineAligned="false">  
   <LinearLayout android:background="#FF0000"  
          android:layout_height="fill_parent" android:layout_width="wrap_content"  
          android:layout_weight="1.2"/>  
   <LinearLayout android:background="#00FF00"  
          android:layout_height="fill_parent" android:layout_width="wrap_content"  
          android:layout_weight="0.3"/>  
   <LinearLayout android:background="#00FFFF"  
          android:layout_height="fill_parent" android:layout_width="wrap_content"  
          android:layout_weight="0.5"/>  
   <LinearLayout android:background="#FFFF00"  
          android:layout_height="fill_parent" android:layout_width="wrap_content"  
          android:layout_weight="1.5"/>  
 </LinearLayout>  

Due to our layout, yellow part should be the largest (about 43% of total width), red part is a little bit smaller, while cyan and green take together only approximately 23%. And on device it looks like in should be:


Now, lets take a look to ayout algorithm. It's is built around layout orientation: vertical or horizontal:

   @Override  
   protected void onLayout(boolean changed, int l, int t, int r, int b) {  
     if (mOrientation == VERTICAL) {  
       layoutVertical();  
     } else {  
       layoutHorizontal();  
     }  
   }  

Note first that layoutHorizontal and layoutVertical works in the same manner with respect to layout orientation. So, let's considering only one of them, layoutVertical. I won't include code of method here, it can be found here, we only briefly review the main steps. At first step border right coordinate is calculated. Then we get count of child in layout and calculate major and minor gravity:

     final int majorGravity = mGravity & Gravity.VERTICAL_GRAVITY_MASK;  
     final int minorGravity = mGravity & Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK;  

Major gravity will be used on next step to calculate upper point from which items will be placed. Minor gravity will be applied to each child without explicitly specified gravity. In loop through layout children left point (where element should be placed) us calculated with respect to minor gravity (if there is no explicitly specified gravity for current element). And in the end, knowing left and top point where element should be placed and it's dimensions we can draw it. We should note, that some methods within loop have no any effects for linear layout and will be overridden for TableLayout (which based on LinearLayout), which will be considered next.
TableLayout is a grid of of rows and columns, where a cell can display a view control. From a user interface design perspective, a TableLayout is comprised of TableRow controls - one for each row in your table. The contents of a TableRow are simply the view controls that will go in each “cell” of the table grid.
Although table layouts can be used to design entire user interfaces, they usually aren’t the best solution for doing so, because they are derived from LinearLayout and not the most efficient of layout controls. Generally speaking, TableLayout is a little bit more than an organized set of nested LinearLayouts, and nesting layouts too deeply is generally discouraged for performance concerns. However, for data that is already in a format suitable for a table, such as spreadsheet data, table layout may be a reasonable choice.


RelativeLayout

Unlike linear or table layouts relative layout doesn't drive us to the limits of your current row - you can place elements where ever you want relatively to the other elements or even to parent layout. I guess this layout is very familiar to developers and I don't see any reason to focus on it. Just note, that considering capabilities of relative layout, its layout/measurement algorithm is much difficult. 
Let's consider common example of a layout: list item with an icon on the left, a title at the top and an optional description underneath the title:



This layout may be implemented using different layout, for example LinearLayout, RelativeLayout or TableLayout. Having the same result, we can compare layout's complexity by hierarchyviewer. So, here are layouts with different layout managers:

 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"  
        android:layout_width="fill_parent"  
        android:layout_height="72dp"  
        android:padding="6dip">  
   <ImageView  
       android:id="@+id/icon"  
       android:layout_width="wrap_content"  
       android:layout_height="fill_parent"  
       android:layout_marginRight="6dip"  
       android:src="@drawable/ic_launcher" />  
   <LinearLayout  
       android:orientation="vertical"  
       android:layout_width="fill_parent"  
       android:layout_height="fill_parent">  
     <TextView  
         android:layout_width="fill_parent"  
         android:layout_height="0dip"  
         android:layout_weight="1"  
         android:gravity="center_vertical"  
         android:text="Layout Example item" />  
     <TextView  
         android:layout_width="fill_parent"  
         android:layout_height="0dip"  
         android:layout_weight="1"  
         android:singleLine="true"  
         android:ellipsize="marquee"  
         android:text="Simple item that shows how the same..." />  
   </LinearLayout>  
 </LinearLayout>  

 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"  
         android:layout_width="fill_parent"  
         android:layout_height="62dp"  
         android:padding="6dip">  
   <ImageView  
       android:id="@+id/icon"  
       android:layout_width="wrap_content"  
       android:layout_height="fill_parent"  
       android:layout_alignParentTop="true"  
       android:layout_alignParentBottom="true"  
       android:layout_marginRight="6dip"  
       android:src="@drawable/ic_launcher" />  
   <TextView  
       android:id="@+id/secondLine"  
       android:layout_width="fill_parent"  
       android:layout_height="26dip"  
       android:layout_toRightOf="@id/icon"  
       android:layout_alignParentBottom="true"  
       android:layout_alignParentRight="true"  
       android:singleLine="true"  
       android:ellipsize="marquee"  
       android:text="Simple item that shows how the same..." />  
   <TextView  
       android:layout_width="fill_parent"  
       android:layout_height="wrap_content"  
       android:layout_toRightOf="@id/icon"  
       android:layout_alignParentRight="true"  
       android:layout_alignParentTop="true"  
       android:layout_above="@id/secondLine"  
       android:layout_alignWithParentIfMissing="true"  
       android:gravity="center_vertical"  
       android:text="Layout Example item" />  
 </RelativeLayout>  

 <TableLayout xmlns:android="http://schemas.android.com/apk/res/android"  
        android:layout_width="fill_parent"  
        android:layout_height="72dp"  
        android:padding="6dip">  
   <TableRow  
       android:layout_height="wrap_content"  
       android:layout_width="match_parent">  
   <ImageView  
       android:id="@+id/icon"  
       android:layout_width="wrap_content"  
       android:layout_height="fill_parent"  
       android:layout_marginRight="6dip"  
       android:src="@drawable/ic_launcher"/>  
     <LinearLayout  
         android:orientation="vertical"  
         android:layout_width="fill_parent"  
         android:layout_height="fill_parent">  
       <TextView  
           android:layout_width="fill_parent"  
           android:layout_height="0dip"  
           android:layout_weight="1"  
           android:gravity="center_vertical"  
           android:text="Layout Example item"/>  
       <TextView  
           android:layout_width="fill_parent"  
           android:layout_height="0dip"  
           android:layout_weight="1"  
           android:text="Simple item that shows how the same..."/>  
     </LinearLayout>  
   </TableRow>  
 </TableLayout>  

And the difference between implementations becomes obvious when comparing the view hierarchies in HierarchyViewer:
Linear layout

Relative layout

Table layout

So, as we can see, we can create the same layout with different layout managers, but view hierarchies will be much different. For single item it may no sense to optimize view, but it much more important when you use such a layout for every item in a ListView, for instance. So I urge not to focus on single layout manager, but to choose it depending on certain problem.

Friday, August 17, 2012

About cache

Cache is a temporary buffer (with quick access) that stores data so that future requests for this data can be processed faster. If requested data is contained in cache then request can be served by reading the cache, otherwise data has to be computed or fetched from its original storage. Definitely, in first case our application will perform operation quicker.

But we have to pay for this advantage - to store a lot of data cache need to use a huge amount of memory. It's not a big problem when we are talking about cache on local disk (or sdcard) to store, for example, downloaded images. But if we need to store some data in RAM to have the quickest access to this data we will have to limit our cache by some constant value which often much less than all data size. This automatically implies 'preemptive nature' of cache: if there is no free space in cache and we need to put one more 'item' - something (already stored in cache) must be removed.

First and most obvious way to implement own cache would be SoftReference. Let's take a look on simple (not multithreaded) cache based on SoftReference. Let our cache will be called SoftCache and let is be parameterized:

 public class SoftCache<K, V>  

Data will be stored in HashMap:

 private Map<K, SoftReference<V>> mCacheMap = new HashMap<K, SoftReference<V>>();  

And methods to put new item to cache (we must supply our cache records with keys to be able to get them), get item (with appropriate key) from cache and to remove it:

   public V put(K key, V value) {  
     SoftReference<V> softRef = mCacheMap.put(key, new SoftReference<V>(value));  
     if (softRef == null) {  
       return null;  
     }  
     V oldValue = softRef.get();  
     softRef.clear();  
     return oldValue;  
   }
  
   public V get(K key) {  
     SoftReference<V> softRef = mCacheMap.get(key);  
     return softRef == null ? null : softRef.get();  
   }  

   public V remove(K key) {  
     SoftReference<V> softRef = mCacheMap.remove(key);  
     if (softRef == null) {  
       return null;  
     }  
     V oldValue = softRef.get();  
     softRef.clear();  
     return oldValue;  
   }  

As we can see, this approach is easy enough to implement. But it has a few disadvantages:
  1. We can't specify cache size.
  2. SoftReference doesn't give us control over when and how remove records from cache when we need to add new item to already 'full' cache
SoftReference approach may be interested as a general purpose cache. But if we want to optimize our cache to be the most efficient for certain purpose we need somehow define rules for items replacing in cache. Generally speaking, it's very difficult issue of computer science and it's often called Cache Algorithms.
So let's take a look to some of Cache Algorithms without going into details of algorithms' efficiency. Our goal here is to understand algorithms' functions and try to find how we can apply caches based on these algorithms to everyday problems.

     First in First out (FIFO)


Algorithm with least effort for managing the cache entries. It's based on using queue as a storage for cache entries. When cache is full and an entry needs to be replaced (since new entry need to be inserted) - entry at the front of the queue (the oldest entry) will be removed and replaced with the current fetched entry.
It's not easy to find certain case where this algorithm will give max output, probably it should be used as a general purpose cache when we can't find better approaches.

     Least Frequently Used (LFU)


This Algorithm is based on counting how often an entry being requested - it's increments a counter associated with each cache entry. Entry with least frequently used counter is removed first when cache is full and new entry need to be added. This algorithm is not very fast (since it requires brute-force search for element to remove when cache is full). But LFU cache is a good choice when the request frequency does not vary over time (i.e. what was the most frequent yesterday is also the same today). As an very abstract example of Android application which using LFU cache may be some media player which requested audio streams from internet - in most cases users are listening the same music, so it may be cached to provide immediate access. But as I said, it's very abstract example, because in most cases, our cache will be to small to store even a few audio streams. But I believe this example shows certain case where certain algorithm gives real advantage.

     Least Recently Used (LRU)


Main idea of LRU is to remove the least recently used items first when free space in cache is required. Every new item is placed on the top of the cache, while older top items falled down. But whenever an item is accessed, it will be placed at the top again. When the cache exceeds its size limit, items from the bottom will be discarded. This algorithm does not require exhaustive search, so it's faster comparatively to LFU.
LRU will be good choice for some kind of gallery application or applications to render some big pictures which usually broken on small pieces - LRU helps to avoid yank effect when user doing move events around the same area.
B.t.w., since api 12 LRU cache is present in android sdk and compatibility package.

     Most Recently Used (MRU)


The opposite to LRU - adding new items to the top of cache MRU removes items from top when the cache exceeds its size limit. Good choice for common database memory caches, when already fetched data, probably, will not be requested in the foreseeable future.

There are a lot of other algorithms, but we will focus on this and go to implementation.

Let's start from generic base cache element to be used for all cache implementations:
 public class CacheElement<K, V> {  
   
   /**  
    * Cache element value.  
    */  
   private V mValue;  
   
   /**  
    * Cache element key.  
    */  
   private K mKey;  
   
   /**  
    * Counter for LFU algorithm.  
    */  
   private int mHitCount;  
   
   ...
   // mutator methods  
 }  

I guess here all is obvious.
Next, we will define base cache class with some common functionality:
 public abstract class AbstractCache<K, V> {  
   private static final int MAX_ITEMS_IN_CACHE = 3;  

   protected Map<K, CacheElement<K, V>> mTable = new HashMap<K, CacheElement<K, V>>();  

   protected boolean isFull() {  
     return itemsCount() >= MAX_ITEMS_IN_CACHE;  
   }  

   protected CacheElement<K, V> createCacheElement(K key, V value) {  
     CacheElement<K, V> cacheElement = new CacheElement<K, V>();  
     cacheElement.setKey(key);  
     cacheElement.setValue(value);  
     return cacheElement;  
   } 
 
   protected boolean updateCacheElement(CacheElement<K, V> element, K key, V value) {  
     if (element != null) {  
       element.setValue(value);  
       return true;  
     }  
     return false;  
   }  

   public abstract void put(K key, V value);  

   public abstract V get(K key);  

   public abstract void print();  

   protected abstract int itemsCount();  
 }  

Let's look closely to this generic class. MAX_ITEMS_IN_CACHE defined maximum of elements, to be placed in cache. Of course, it would be more convenient to have size limit, but calculation of object size in java is not trivial procedure and since it's only example we can proceed with count limitation. mTable is used for quick search of already stored (cached) items by key. isFull method is quite obvious - it's used to define is cache full and one of the cache items need to be deleted. createCacheElement and updateCacheElement are used to create and update already added to cache element respectively. And 4 abstract methods to be implemented in certain cache implementation. Should be noted, that print is used only for test purposes and it will be implemented using System.out#print to be able to run on desktop.

So, first will be FIFO cache
 public class FIFOCacheImpl<K, V> extends AbstractCache<K, V> {  
   
   protected Queue<CacheElement<K, V>> mCache = new ConcurrentLinkedQueue<CacheElement<K, V>>();  
   
   @Override
   public final synchronized void put(K key, V value) {  
     CacheElement<K, V> element = mTable.get(key);  
     if (updateCacheElement(element, key, value)) {  
       return;  
     }  
     CacheElement<K, V> cacheElement = createCacheElement(key, value);  
     if (isFull()) {  
       CacheElement<K, V> elem = mCache.poll();  
       mTable.remove(elem.getKey());  
     }  
     mCache.offer(cacheElement);  
     mTable.put(key, cacheElement);  
   }  
   
   @Override
   public final synchronized V get(K key) {  
     CacheElement<K, V> element = mTable.get(key);  
     return element != null ? element.getValue() : null;  
   }  
   
   @Override  
   public void print() {  
     System.out.print("{ ");  
     for (CacheElement<K, V> cacheElement : mCache) {  
       System.out.print("[ " + cacheElement.getKey() + " - " + cacheElement.getValue() + " ] ");  
     }  
     System.out.println("}");  
   }  
   
   @Override  
   protected int itemsCount() {  
     return mCache.size();  
   }  
 }  

Queue is used as primary storage for items to provide FIFO behavior. Methods get, print and itemsCount are quite obvious, so let's move to put right away. At first step we are using quick dictionary mTable to find if item already exists in cache and if such item found - update it with new value. If item with key doesn't exists in cache - create it and if cache is not full put it to cache and table (for quick search). But if cache is full - remove item from head (if we are adding new elements to tail) of cache and from search dictionary (mTable).
And simple example:
     AbstractCache<Integer, String> fifoCache = new FIFOCacheImpl<Integer, String>();  
     fifoCache.put(1, "s1");  
     fifoCache.print();  
     fifoCache.put(2, "s2");  
     fifoCache.print();  
     fifoCache.put(3, "s3");  
     fifoCache.print();  
     fifoCache.put(4, "s4");  
     fifoCache.print();  
     fifoCache.put(5, "s5");  
     fifoCache.print();  

and system output:
 { [ 1 - s1 ] }  
 { [ 1 - s1 ] [ 2 - s2 ] }  
 { [ 1 - s1 ] [ 2 - s2 ] [ 3 - s3 ] }  
 { [ 2 - s2 ] [ 3 - s3 ] [ 4 - s4 ] }  
 { [ 3 - s3 ] [ 4 - s4 ] [ 5 - s5 ] }  

So, all as expected: when cache is full - oldest items removed from cache.

Next will be LFU cache
 public class LFUCacheImpl<K, V> extends AbstractCache<K, V> {  
   
   protected List<CacheElement<K, V>> mCache = new LinkedList<CacheElement<K, V>>();  
   
   @Override  
   public void put(K key, V value) {  
     CacheElement<K, V> element = mTable.get(key);  
     if (updateCacheElement(element, key, value)) {  
       return;  
     }  
     CacheElement<K, V> cacheElement = createCacheElement(key, value);  
     if (isFull()) {  
       CacheElement<K, V> elem = removeLfuElement();  
       mTable.remove(elem.getKey());  
     }  
     mCache.add(cacheElement);  
     mTable.put(key, cacheElement);  
   }  
   
   @Override  
   public final synchronized V get(K key) {  
     CacheElement<K, V> element = mTable.get(key);  
     if (element != null) {  
       element.incrementHitCount();  
       return element.getValue();  
     }  
     return null;  
   }  
   
   @Override  
   public void print() {  
     System.out.print("{ ");  
     for (CacheElement<K, V> cacheElement : mCache) {  
       System.out.print("[ " + cacheElement.getKey() + " - " + cacheElement.getValue() + " : " +  
           cacheElement.getHitCount() + " ] ");  
     }  
     System.out.println("}");  
   }  
   
   @Override  
   protected int itemsCount() {  
     return mCache.size();  
   }  
   
   public CacheElement<K, V> removeLfuElement() {  
     CacheElement<K, V> leastElement = leastHit();  
     mCache.remove(leastElement);  
     return leastElement;  
   }  
   
   public CacheElement<K, V> leastHit() {  
     CacheElement<K, V> lowestElement = null;  
     for (CacheElement<K, V> element : mCache) {  
       if (lowestElement == null) {  
         lowestElement = element;  
       } else {  
         if (element.getHitCount() < lowestElement.getHitCount()) {  
           lowestElement = element;  
         }  
       }  
     }  
     return lowestElement;  
   }  
 }  

I won't dwell on the code: the idea is the same as for FIFO cache, only a few moment need to be clarified: when item is requested by get and requested item exists in cache - item's hit count is incremented. leastHit is using mHitCount to find item to remove from cache.
     AbstractCache<Integer, String> lfuCache = new LFUCacheImpl<Integer, String>();  
     lfuCache.put(1, "s1");  
     lfuCache.print();  
     lfuCache.get(1);  
     lfuCache.get(1);  
     lfuCache.print();  
     lfuCache.put(2, "s2");  
     lfuCache.print();  
     lfuCache.get(2);  
     lfuCache.get(2);  
     lfuCache.get(2);  
     lfuCache.print();  
     lfuCache.put(3, "s3");  
     lfuCache.print();  
     lfuCache.get(3);  
     lfuCache.get(3);  
     lfuCache.get(3);  
     lfuCache.get(3);  
     lfuCache.print();  
     lfuCache.put(4, "s4");  
     lfuCache.print();  
     lfuCache.get(4);  
     lfuCache.print();  
     lfuCache.put(5, "s5");  
     lfuCache.print();  

and system output:
 { [ 1 - s1 : 0 ] }  
 { [ 1 - s1 : 2 ] }  
 { [ 1 - s1 : 2 ] [ 2 - s2 : 0 ] }  
 { [ 1 - s1 : 2 ] [ 2 - s2 : 3 ] }  
 { [ 1 - s1 : 2 ] [ 2 - s2 : 3 ] [ 3 - s3 : 0 ] }  
 { [ 1 - s1 : 2 ] [ 2 - s2 : 3 ] [ 3 - s3 : 4 ] }  
 { [ 2 - s2 : 3 ] [ 3 - s3 : 4 ] [ 4 - s4 : 0 ] }  
 { [ 2 - s2 : 3 ] [ 3 - s3 : 4 ] [ 4 - s4 : 1 ] }  
 { [ 2 - s2 : 3 ] [ 3 - s3 : 4 ] [ 5 - s5 : 0 ] }  

So, as we can see, when item is requested from cache - its hit count is incremented. Least demanded items removes from cache when it's full.

And the last will be LRU cache (MLU is almost the same)
 public class LRUCacheImpl<K, V> extends AbstractCache<K, V> {  
   
   protected List<CacheElement<K, V>> mCache = new ArrayList<CacheElement<K, V>>();  
   
   @Override  
   public void put(K key, V value) {  
     CacheElement<K, V> element = mTable.get(key);  
     if (updateCacheElement(element, key, value)) {  
       Collections.swap(mCache, mCache.indexOf(element), 0);  
       return;  
     }  
     CacheElement<K, V> cacheElement = createCacheElement(key, value);  
     if (isFull()) {  
       CacheElement<K, V> elem = mCache.remove(mCache.size() - 1);  
       mTable.remove(elem.getKey());  
     }  
     mCache.add(0, cacheElement);  
     mTable.put(key, cacheElement);  
   }  
   
   @Override  
   public final synchronized V get(K key) {  
     CacheElement<K, V> element = mTable.get(key);  
     if (element != null) {  
       Collections.swap(mCache, mCache.indexOf(element), 0);  
       return element.getValue();  
     }  
     return null;  
   }  
   
   @Override  
   public void print() {  
     System.out.print("{ ");  
     for (CacheElement<K, V> cacheElement : mCache) {  
       System.out.print("[ " + cacheElement.getKey() + " - " + cacheElement.getValue() + " ] ");  
     }  
     System.out.println("}");  
   }  
   
   @Override  
   protected int itemsCount() {  
     return mCache.size();  
   }  
 }  

As we can see, requested (or updated item) is moved to the first position of list, while last element is removed, when cache is full. And here is example:
     AbstractCache<Integer, String> lruCache = new LRUCacheImpl<Integer, String>();  
     lruCache.put(1, "s1");  
     lruCache.print();  
     lruCache.put(2, "s3");  
     lruCache.print();  
     lruCache.put(1, "s1");  
     lruCache.print();  
     lruCache.get(2);  
     lruCache.print();  
     lruCache.put(3, "s1");  
     lruCache.print();  
     lruCache.put(4, "s1");  
     lruCache.print();  
     lruCache.get(4);  
     lruCache.print();  
     lruCache.put(5, "s1");  
     lruCache.print();  
     lruCache.put(6, "s1");  
     lruCache.print();  

and system output:
 { [ 1 - s1 ] }  
 { [ 2 - s3 ] [ 1 - s1 ] }  
 { [ 1 - s1 ] [ 2 - s3 ] }  
 { [ 2 - s3 ] [ 1 - s1 ] }  
 { [ 3 - s1 ] [ 2 - s3 ] [ 1 - s1 ] }  
 { [ 4 - s1 ] [ 3 - s1 ] [ 2 - s3 ] }  
 { [ 4 - s1 ] [ 3 - s1 ] [ 2 - s3 ] }  
 { [ 5 - s1 ] [ 4 - s1 ] [ 3 - s1 ] }  
 { [ 6 - s1 ] [ 5 - s1 ] [ 4 - s1 ] }  

So, all as expected, last requested item is on top, while not requested items are falling to the down. So, it was short introduction to different cache implementations to be used in your Android applications.

Thank you for attention.

Thursday, August 9, 2012

Hello, Kotlin

After a long years month of waiting Jetbrains finally released Kotlin M2 Candidate with official Android support!

A few links: About language, How to run Kotlin on Android and about Build tools.

Being able to provide custom control structures, Operator overloading and many other useful stuff (see Kotlin vs Java) Kotlin may become a very powerful extension for your development tools.

Looking forward to first official release.

Happy coding!

Saturday, July 7, 2012

whoami

Quite often you application must generate unique identifier for current instance of application to create server account linked with current device. Ideally, this identifier should persists on application reinstallation, so even after application being deleted and installed again the same account will be used for application on current device.

The simplest (and, probably, the most correct) approach is to use TelephonyManager#getDeviceId.
 mDeviceId = ((TelephonyManager)context.getSystemService(Context.TELEPHONY_SERVICE)).getDeviceId()  
Unfortunately, deviceId may be null for devices w.o. GSM or CDMA module. Another way is to use Serial Number. This identifier is available via Build#SERIAL from android api 9. So, to cover older os versions let's use reflection:
 Class<?> c = Class.forName("android.os.SystemProperties");  
 Class<?>[] paramTypes= new Class[1];  
 paramTypes[0]= String.class;  
 Method get = c.getMethod("get", paramTypes);  
 Object[] params= new Object[1];  
 params[0]= new String("ro.serialno");  
 mDeviceId = (String) get.invoke(c, params);  
Serial identifier is quite 'unpredictable' and may be absent on Samsung devices, so we have to have more options to cover much more devices. Next step will be Secure#ANDROID_ID - randomly generated 64-bit number on the device's first boot:
 mDeviceId = Secure.getString(context.getContentResolver(), Secure.ANDROID_ID);  
But even this value sometimes is null (unfortunately, I don't remember devices on which this problem can be reproduced). Furthermore, this values will be changed upon factory reset.
It is also possible to use wi-fi mac address as identifier for application instance:
 mDeviceId = ((WifiManager)context.getSystemService(Context.WIFI_SERVICE)).getConnectionInfo().getMacAddress();  
But not all devices have wi-fi. Also, if the wi-fi is not turned on, the hardware may not report the Mac address (null will be returned). As a last resort identifier can be generated using UUID#randomUUID. This is mutable value, but it can be stored somewhere on sdcard to persist it's value after reinstallation. Probably, it's all available options to get unique id. Hopefully, order of presentation provides correct 'brute-force' algorithm of searching identifier.

Sunday, May 27, 2012

Customize your progress bar

ProgressBar is standard visual indicator for progress of some operation. It's often used by developers as a part of ProgressDialog to prevent user's interaction with application (because ProgressDialog is modal) while complex operations are performed in background.

Style of ProgressBar depends on  device vendor. It may looks like

for Samsung devices or like
for HTC devices etc. The same situation for 'indeterminate' progress with infinite round spinner. 

Fortunately, ProgressBar is very flexible and allow us to customize it (for example, when we want to have the same progress line for our application at any device). Let's start from most obvious - style. Default progress bar styles provided by sdk are:
  • Widget.ProgressBar.Horizontal
  • Widget.ProgressBar.Small
  • Widget.ProgressBar.Large
  • Widget.ProgressBar.Inverse
  • Widget.ProgressBar.Small.Inverse
  • Widget.ProgressBar.Large.Inverse
I believe the titles speaks for itself. I prefer Widget.ProgressBar.Horizontal. Further, let's define height of dialog using layout_height attribute:

   <ProgressBar  
       android:id="@+id/progress"  
       style="@android:style/Widget.ProgressBar.Horizontal"  
       android:layout_width="fill_parent"  
       android:layout_height="20dp"/>  

Now progress bar will looks like on pictures below.

To customize progress line we need create LayerDrawable background, something like this:

 <?xml version="1.0" encoding="utf-8"?>  
 <layer-list xmlns:android="http://schemas.android.com/apk/res/android">  
   <item android:id="@+id/my_bg">  
     <shape>  
       <corners android:radius="20dip"/>  
       <gradient android:startColor="#C0C0C0" android:centerColor="#F8F8FF"  
            android:centerY="0.75" android:endColor="#ffffff" android:angle="90"/>  
       <stroke android:width="1dp" android:color="#00aa00"/>  
     </shape>  
   </item>  
   <item android:id="@+id/my_progress">  
     <clip>  
       <shape>  
         <corners android:radius="20dip"/>  
         <gradient android:startColor="#CaCaC0" android:centerColor="#2828FF"  
              android:centerY="0.75" android:endColor="#325423" android:angle="270"/>  
       </shape>  
     </clip>  
   </item>  
 </layer-list>  

Tiny clarifications: since progress line has background and progress state at least 2 layers are required. And important moment: first layer is background while second is progress. Id of layers actually can be arbitrary (as in my example), but if you don't want to clip your layers by yourself you can use system hardcoded names: background for first layer and progress for second. In this case method tileify of ProgressBar class will perform clipping internally:

   private Drawable tileify(Drawable drawable, boolean clip) {  
     if (drawable instanceof LayerDrawable) {  
       LayerDrawable background = (LayerDrawable) drawable;  
       final int N = background.getNumberOfLayers();  
       Drawable[] outDrawables = new Drawable[N];  
       for (int i = 0; i < N; i++) {  
         int id = background.getId(i);  
         outDrawables[i] = tileify(background.getDrawable(i),  
             (id == R.id.progress || id == R.id.secondaryProgress));  
       }  
     ...  
     } else if (drawable instanceof BitmapDrawable) {  
     ...  
       return (clip) ? new ClipDrawable(shapeDrawable, Gravity.LEFT,  
           ClipDrawable.HORIZONTAL) : shapeDrawable;  
     }  
     return drawable;  
   }  

And this is how progress bar will looks like with crazy color in our LayerDrawable:


Not bad, isn't so?

Saturday, May 12, 2012

Deep look at Android Networking. Part 3. Mobile internet.

Mobile internet (GPRS, EDGE, etc) is typically more expensive and slower than internet provided by wlan access points (via wi-fi), but has much greater coverage. Most android devices have mobile internet. It's configured from Settings (Settings->Wireless and network->Mobile Networks):


Since as typical android device has possibility to establish connection to wlan access point (i.e. wi-fi internet connection) and use mobile operator internet (i.e. mobile internet) option to establish 2 internet connection simultaneously theoretical exists. But to dot the i's and cross the t's let's consider typical rules for internet connection, which inherent not just for Android OS, but for most mobile operation systems:
  1. Only one internet connection can be established.
  2. Wi-fi internet has higher priority than mobile internet. 
  3. If wi-fi internet connection is established you will not be able to establish mobile internet connection.
  4. If mobile internet connection is established and you will try establish wi-fi connection, mobile internet connection will be disabled.
This rules are quite logical and make sense. Only one internet connection can be established to save device battery. Wi-fi has higher priority because it's faster and cheaper than mobile internet. And since wi-fi connection has higher priority, points 3 and 4 are quite obvious too. But in case when you need to circumvent these rules you will be faced with serious problems (especially given the fact that the there is no public api in android sdk to manage mobile network connection) and all you remain to do is rely on funny tricks, some of which will be discussed below. But since all this tricks are based on reflection, dirty hacks and hidden api, there is no there is no guarantee, that this will works on your certain device (in my experience, it depends on vendor and device model). Furthermore, I don't recommend you to use this things in your production application, they are (this tricks) interesting only for experiments.

So, let's start from permissions essential for these experiments:
   <uses-permission android:name="android.permission.INTERNET"/>  
   <uses-permission android:name="android.permission.MODIFY_PHONE_STATE"/>  
   <uses-permission android:name="android.permission.WRITE_APN_SETTINGS"/>  
   <uses-permission android:name="android.permission.CHANGE_NETWORK_STATE"/>  
   <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>  
   <uses-permission android:name="android.permission.WRITE_SETTINGS"/>  

Next moment - we need to enable mobile data connection. It should be noted, that this operation a little bit differently performed for old and for new sdk version. For sdk 2.1 and earlier you should do something like this:
   private void setDataConnection(boolean dataConnection) {  
     Method dataConnSwitchMethod;  
     Class telephonyManagerClass;  
     Object ITelephonyStub;  
     Class ITelephonyClass;  
     TelephonyManager telephonyManager = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE);  
     try {  
       telephonyManagerClass = Class.forName(telephonyManager.getClass().getName());  
       Method getITelephonyMethod = telephonyManagerClass.getDeclaredMethod("getITelephony");  
       getITelephonyMethod.setAccessible(true);  
       ITelephonyStub = getITelephonyMethod.invoke(telephonyManager);  
       ITelephonyClass = Class.forName(ITelephonyStub.getClass().getName());  
       String invokeMethodName = dataConnection ? "enableDataConnectivity" : "disableDataConnectivity";  
       Log.d(TAG, invokeMethodName);  
       dataConnSwitchMethod = ITelephonyClass.getDeclaredMethod(invokeMethodName);  
       dataConnSwitchMethod.setAccessible(true);  
       dataConnSwitchMethod.invoke(ITelephonyStub);  
     } catch (Exception e) {  
       Log.e(TAG, e.getMessage());  
     }  
   }  

As you can see, we are getting ITelephony aidl interface to enable or disable mobile data connections:
   /**  
    * Allow mobile data connections.  
    */  
   boolean enableDataConnectivity();  
   /**  
    * Disallow mobile data connections.  
    */  
   boolean disableDataConnectivity();  

Starting from sdk 2.2 you can use the IConnectivityManager#setMobileDataEnabled method. It's hidden in API too, so we have to use reflection again:
   private void setMobileDataEnabled(boolean dataConnection) {  
     try {  
       final ConnectivityManager conman = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);  
       final Class conmanClass = Class.forName(conman.getClass().getName());  
       final Field iConnectivityManagerField = conmanClass.getDeclaredField("mService");  
       iConnectivityManagerField.setAccessible(true);  
       final Object iConnectivityManager = iConnectivityManagerField.get(conman);  
       final Class iConnectivityManagerClass = Class.forName(iConnectivityManager.getClass().getName());  
       final Method setMobileDataEnabledMethod = iConnectivityManagerClass.getDeclaredMethod("setMobileDataEnabled", Boolean.TYPE);  
       setMobileDataEnabledMethod.setAccessible(true);  
       setMobileDataEnabledMethod.invoke(iConnectivityManager, dataConnection);  
     } catch (Exception e) {  
       ...  
     }  
   }  

On some devices this operation requires android.permission.WRITE_SECURE_SETTINGS, which maybe granted only for system applications, so for such cases all next tricks will have no any sense. Only thing you can do - root your device and install app to /system folder (using adb shell push app.apk /system command).

So, after mobile data connection is enabled, we will try add APN we want to use:
   private static final Uri APN_TABLE_URI = Uri.parse("content://telephony/carriers");  
   public static final String OPERATOR_NUMERIC_KEY = "gsm.sim.operator.numeric";  
   
   public int addAPN() {  
     int id = -1;  
     ContentResolver resolver = getContentResolver();  
     ContentValues values = new ContentValues();  
     values.put("name", appName);  
     values.put("apn", accessPointName);  
     values.put("user", userName);  
     values.put("password", password);  
     // read mobile operator numeric info using shell command getprop  
     String numeric = getSystemProperty(OPERATOR_NUMERIC_KEY);  
     String mcc = "";  
     String mnc = "";  
     try {  
       mcc = numeric.substring(0, 3);  
       mnc = numeric.substring(3, 5);  
     } catch (Exception e) {  
       ...  
     }  
     values.put("mcc", mcc);  
     values.put("mnc", mnc);  
     values.put("numeric", numeric);  
     Cursor cursor = null;  
     try {  
       // insert apn  
       Uri newRow = resolver.insert(APN_TABLE_URI, values);  
       if (null != newRow) {  
         cursor = resolver.query(newRow, null, null, null, null);  
         Log.d(TAG, "Newly added APN:");  
         // Obtain the apn id  
         int idIndex = cursor.getColumnIndex("_id");  
         cursor.moveToFirst();  
         id = cursor.getShort(idIndex);  
         Log.d(TAG, "New ID: " + id + ": Inserting new APN succeeded!");  
       }  
     }  
     catch (Exception e) {  
       Log.d(TAG, e.getMessage());  
     }  
     if (null != cursor) {  
       cursor.close();  
     }  
     return id;  
   }  
   
   ...
   public static String getSystemProperty(String key) {
        try {
            String line;
            String formattedKey = "[" + key + ']';
            java.lang.Process p = Runtime.getRuntime().exec("getprop");
            BufferedReader input = new BufferedReader(new InputStreamReader(p.getInputStream()));
            while (null != (line = input.readLine())) {
                String[] property = line.split(":");
                if (formattedKey.equals(property[0].trim())) {
                    return property[1].trim().substring(1, property[1].length() - 2);
                }
            }
            input.close();
        }
        catch (Exception err) {
            err.printStackTrace();
        }

        return null;
    }

I think all is obvious here.  ContentResolver is used to access table of system APNs. The only thing that deserves attention here is 
            
     // read mobile operator numeric info using shell command getprop
     String numeric = getSystemProperty(OPERATOR_NUMERIC_KEY);  

We are extracting system property gsm.sim.operator.numeric and parsing result to get Mobile Network Code (mnc) and Mobile Country Code (mcc) - these values are essential for APN.

Now we need to set our inserted APN to be default. From user interface it's looks like to select appropriate radio button on list of available APNs.


Let's take a look how can we do this programmatically:

   private static final Uri PREFERRED_APN_URI = Uri.parse("content://telephony/carriers/preferapn");  

   public boolean setActiveAPN(int id) {  
     boolean result = false;  
     ContentResolver resolver = getContentResolver();  
     ContentValues values = new ContentValues();  
     values.put("apn_id", id);  
     try {  
       resolver.update(PREFERRED_APN_URI, values, null, null);  
       Cursor cursor = resolver.query(  
           PREFERRED_APN_URI,  
           new String[]{"name", "apn"},  
           "_id=" + id,  
           null,  
           null);  
       if (null != cursor) {  
         result = true;  
         cursor.close();  
       }  
     }  
     catch (Exception e) {  
       Log.d(TAG, e.getMessage());  
     }  
     return result;  
   }  

To setActiveAPN method we need to pass id. We can use identifier of APN, created by addAPN function or any other existed APN.

So, now, after mobile connection is enabled and you add APN and made it default you device will try to establish connection (of course, there should not be wi-fi connection). If you provide correct APN your device will have mobile internet after your credential will be verified.

But there is one more trick I want to show. It allow to raise mobile connection even with wifi connected. ConnectivityManager#startUsingNetworkFeature method is used to request network for you application. You need to specify which network the request pertains to (first param) and the name of the feature to be used (second param). Typical request will looks like:
    connectivityManager.startUsingNetworkFeature(ConnectivityManager.TYPE_MOBILE, "enableHIPRI");

This means, that you want to enable High Priority (HIPRI) Mobile data connection over mobile network. More about first argument you can read in documentation to ConnectivityManager. List of available features you can found in internal interface Phone:
   // "Features" accessible through the connectivity manager  
   static final String FEATURE_ENABLE_MMS = "enableMMS";  
   static final String FEATURE_ENABLE_SUPL = "enableSUPL";  
   static final String FEATURE_ENABLE_DUN = "enableDUN";  
   static final String FEATURE_ENABLE_HIPRI = "enableHIPRI";  
   static final String FEATURE_ENABLE_DUN_ALWAYS = "enableDUNAlways";  
   static final String FEATURE_ENABLE_FOTA = "enableFOTA";  
   static final String FEATURE_ENABLE_IMS = "enableIMS";  
   static final String FEATURE_ENABLE_CBS = "enableCBS";  

But you need to remember, that all requested network features are active only when your application is 'alive' (i.e. not stopped or killed by system). 

Thursday, May 10, 2012

Deep look at Android Networking. Part 2. Wi-fi.

Almost every android device has wi-fi module to exchange data wirelessly and provide quick and cheap internet. This technology is very popular nowadays so I think every internet user is superficially acquainted with it. We just need to remember, that all low-level wi-fi stuff is encapsulated to be accessed through supplicant. In Android SDK Wi-Fi API provides a facet' for applications to communicate with the lower-level wireless stack. With Wi-Fi API your application can get information about available networks (plus connected network speed), add wlan network description to the set of configured networks, terminate and initiate wi-fi connections and some other stuff.
Let's start from simple example, which actually not used wi-fi api: define, is wi-fi network connected:
     public static boolean isWifiAvailable() {  
         ConnectivityManager mgr = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);  
         if(mgr == null) {  
             return false;  
         }  
         NetworkInfo wifi = mgr.getNetworkInfo(ConnectivityManager.TYPE_WIFI);  
         return wifi != null && wifi.isConnected();  
   }  
As you can see, we are using ConnectivityManager to get information about wi-fi network. Quite often (when your application downloads a lot of data from internet) it may be essential to perform network operations (such as downloading content) only through wlan networks. To do that we can simply use utility function to check is wlan network available:
     public static boolean canDownload() {  
         return !mIsWifiRequired || isWifiAvailable();  
     }  
As for wi-fi api - key object here is WifiManager. It is well described in the documentation, so let's not pay much attention to it and focus on real problems.
Android broadcasts a lot of information about changes of network state which may be interesting for our application. WifiManager has a few constants which represent intent actions related to network state events. For example, if we will 'subscribe' on WIFI_STATE_CHANGED_ACTION we will receive notifications when wi-fi has been enabled, disabled, enabling, disabling or unknown. Information about previous wi-fi state will be received too:
     NetworkStateBroadcastReciver networkStateBroadcastReciver = new NetworkStateBroadcastReciver();  
     IntentFilter networkStateFilter = new IntentFilter();  
     networkStateFilter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION);  
     context.registerReceiver(networkStateBroadcastReciver, networkStateFilter);  
     

     class NetworkStateBroadcastReciver extends BroadcastReceiver {  
         public void onReceive(Context c, Intent intent) {  
             int wifiState = intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE, WifiManager.WIFI_STATE_UNKNOWN);    
         
             int wifiPrevState = intent.getIntExtra(WifiManager.EXTRA_PREVIOUS_WIFI_STATE, WifiManager.WIFI_STATE_UNKNOWN);    
             ...  
         }
     }  
You can get much more notifications when 'subscribing' on SUPPLICANT_STATE_CHANGED_ACTION - start scanning for wi-fi networks, start authentication on wlan access point, etc. (for more information take a look on SupplicantState).
'Subscribing' on SCAN_RESULTS_AVAILABLE_ACTION you will get list of available (found by WifiManager.html#startScan method) wlan access points.
Another possible feature, which, I think, is rarely used - manually adding of access points (i.e. not using result of scanning). But those of you who needs to use this option will be disappointed due to serious limitation of this operation - you can only 'configure' access points WEP and WPA-PSK encryption scheme (and points w.o. encryption too), which is a little bit unpleasantly, since from Setting it's possible to add wi-fi networks with EAP authentication (such as EAP-TLS).
Here is example how to configure Open, WEP and WPA-PSK points:
   WifiConfiguration wifiConfiguration = new WifiConfiguration();      
   wifiConfiguration.SSID = ssid;  
   wifiConfiguration.status = WifiConfiguration.Status.ENABLED;  
   wifiConfiguration.priority = 1;  
   
   ...  

   int addOpenNetwork(WifiConfiguration configuration) {  
     configureOpenNetwork(configuration);  
     return wifi.addNetwork(configuration);  
   }  
   
   private void configureOpenNetwork(WifiConfiguration configuration) {  
     configuration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);  
     configuration.allowedProtocols.set(WifiConfiguration.Protocol.RSN);  
     configuration.allowedProtocols.set(WifiConfiguration.Protocol.WPA);  
     configuration.allowedAuthAlgorithms.clear();  
     configuration.allowedPairwiseCiphers.set(WifiConfiguration.PairwiseCipher.CCMP);  
     configuration.allowedPairwiseCiphers.set(WifiConfiguration.PairwiseCipher.TKIP);  
     configuration.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.WEP40);  
     configuration.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.WEP104);  
     configuration.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.CCMP);  
     configuration.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.TKIP);  
   }  
   
   int addWEPNetwork(WifiConfiguration configuration, String password) {  
     configureWEPNetwork(configuration, password);  
     return wifi.addNetwork(configuration);  
   }  
   
   private void configureWEPNetwork(WifiConfiguration configuration, String password) {  
     configuration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);  
     configuration.allowedProtocols.set(WifiConfiguration.Protocol.RSN);  
     configuration.allowedProtocols.set(WifiConfiguration.Protocol.WPA);  
     configuration.allowedAuthAlgorithms.set(WifiConfiguration.AuthAlgorithm.OPEN);  
     configuration.allowedAuthAlgorithms.set(WifiConfiguration.AuthAlgorithm.SHARED);  
     configuration.allowedPairwiseCiphers.set(WifiConfiguration.PairwiseCipher.CCMP);  
     configuration.allowedPairwiseCiphers.set(WifiConfiguration.PairwiseCipher.TKIP);  
     configuration.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.WEP40);  
     configuration.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.WEP104);  
     configuration.wepKeys[0] = "\"".concat(password).concat("\"");  
     configuration.wepTxKeyIndex = 0;  
   }  
   
   int addWPANetwork(WifiConfiguration configuration, String password) {  
     configureWPANetwork(configuration, password);  
     return wifi.addNetwork(configuration);  
   }  
   
   private void configureWPANetwork(WifiConfiguration configuration, String password) {  
     configuration.allowedAuthAlgorithms.set(WifiConfiguration.AuthAlgorithm.OPEN);  
     configuration.allowedProtocols.set(WifiConfiguration.Protocol.RSN);  
     configuration.allowedProtocols.set(WifiConfiguration.Protocol.WPA);  
     configuration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_PSK);  
     configuration.allowedPairwiseCiphers.set(WifiConfiguration.PairwiseCipher.CCMP);  
     configuration.allowedPairwiseCiphers.set(WifiConfiguration.PairwiseCipher.TKIP);  
     configuration.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.CCMP);  
     configuration.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.TKIP);  
     configuration.preSharedKey = "\"".concat(password).concat("\"");  
   }  
After wi-fi networks were configured networks you need to call WifiManager#saveConfiguration. Sometimes this method will return false and in logs (logcat) you will see something like 'wpa supplicant is busy or not responding'. Don't worry, just call this method again after some timeout (you can use WifiManager#pingSupplicant to check supplicant availability). The last step in manual 'wi-fi configuration' is WifiManager#enableNetwork method, which you need to provide with identifier of network you want connect too.

Friday, April 27, 2012

Deep look at Android Networking. Part 1. Introduction.

Most modern applications are actively using internet to interact with outer world. Internet has become an integral part of mobile development. Android OS is based on the Linux kernel and contains mighty set of networking capabilities inherent from it lower layer.
This topic is not about application layer of network stack. Examples about how to get content of some url or download file can be easily found in internet (including android developers). Purpose of the topic is to consider basic points of network stack in context of android and java.
Lets start from Network Interface - point of interconnection between a your device and a private or public network. It's generally a network interface card (NIC), but doesn't have to have a physical form - network interface can be implemented in software layer. For example, every android device (even when wifi is disabled and no mobile network configured) has the loopback interface (127.0.0.1 for IPv4 and ::1 for IPv6) - it's not a physical device but a piece of software which simulating a network interface.
To get list of available interfaces use NetworkInterface#getNetworkInterfaces:
 NetworkInterface.getNetworkInterfaces()  
Width disabled wifi and mobile connection you will typically get only one network interface - loopback interface lo0. NetworkInterface doesn't provide any particular information about network interface card (NIC) which it represents (however, it is worth noting that since sdk 9 a few additional method were added). Only things you can get - local name of interface and assigned ip address (like pdo0 and 46.133.18.123). Considering fact that only 'raised' interface will be found by NetworkInterface#getNetworkInterfaces it's hard to find any practical use of NetworkInterface (it can't be used to figure out connected or interface which performs data transmission or to check metric of interface) - it's interesting only in theoretical context (it's must less functional than NetworkInterface from java SE sdk).
Unfortunately, android shell (adb shell) doen't provide all linux network commands (like ifconfig). But even supported commands, like, netcfg, not always works like expected. For example, with enabled (and connected wifi) we will see:
$ netcfg
lo       UP    127.0.0.1       255.0.0.0       0x00000049
usb0     DOWN  0.0.0.0         0.0.0.0         0x00001002
tunl0    DOWN  0.0.0.0         0.0.0.0         0x00000080
gre0     DOWN  0.0.0.0         0.0.0.0         0x00000080
sit0     DOWN  0.0.0.0         0.0.0.0         0x00000080
svnet0   UP    0.0.0.0         0.0.0.0         0x000000d1
eth0     UP    192.168.100.163 255.255.255.0   0x00001043
while with wifi disabled:
$ netcfg
lo       UP    127.0.0.1       255.0.0.0       0x00000049
usb0     DOWN  0.0.0.0         0.0.0.0         0x00001002
tunl0    DOWN  0.0.0.0         0.0.0.0         0x00000080
gre0     DOWN  0.0.0.0         0.0.0.0         0x00000080
sit0     DOWN  0.0.0.0         0.0.0.0         0x00000080
svnet0   UP    0.0.0.0         0.0.0.0         0x000000d1
So, we can see the same problem that we had for NetworkInterface - disabled interface not found.
Let's try to understand why.
Here is what we can see (in logcat) when we try to enable wifi on device with wifi disabled:
I/WifiHW  ( 3643): [WIFI] Load Driver
I/WifiHW  ( 3643): [WiFi.c] insmod() Start!! 
I/WifiHW  ( 3643): [WiFi.c] start init_module() Start!! 
I/WifiHW  ( 3643): [WIFI] DRIVER OK
I/WifiHW  ( 3643): wifi_start_supplicant: Enter
D/WifiHW  ( 3643): /data/wifi/bcm_supp.conf exits
E/WifiHW  ( 3643): wifi_start_supplicant: pi->serial=117440528, serial=0
I/WifiHW  ( 3643): wifi_start_supplicant: Exit. supp_status=running
...
end when wifi is enabled and connected and we are disabling it:
I/WifiHW  ( 3643): wifi_stop_supplicant: Enter
I/WifiHW  ( 3643): wifi_stop_supplicant: connecting supplicant is done
I/WifiHW  ( 3643): wifi_stop_supplicant: status of  supplicant(connected), count(49) 
I/wpa_supplicant( 5070): CTRL-EVENT-TERMINATING - signal 15 received
I/wpa_supplicant( 5070): CTRL-EVENT-TERMINATING - signal 0 received
I/WifiHW  ( 3643): wifi_stop_supplicant: exit normally
D/WifiStateTracker( 3643): Reset connections and stopping DHCP
I/WifiHW  ( 3643): wifi_close_supplicant_connection: enter
I/WifiHW  ( 3643): wifi_close_supplicant_connection: exit
...
I/WifiHW  ( 3643): wifi_unload_driver: enter
...
E/WifiHW  ( 3643): [WIFI] Unload Driver
I/WifiHW  ( 3643): wifi_unload_driver: exit
What we have here? Assuming that WifiHW tag is used to mark operations related to wifi stuff (quite logical assumption) we can see, that in wifi disabled state not just supplicant is 'stopped', but even wifi driver is unloaded (insmod is used to load it). Otherwise, supplicant received signal 15 (termination signal) and then wifi driver is unloaded. Please note once again: all this assumptions are based on logs of devices.
What does this tell us? This tells us, that Android is quite smart and economical (at least battery) system ... and that most like we not able to write application to monitor network interfaces (at least list all of them) of our device - when NIC is not used it's 'unloaded' from system. As far as you know, it's not possible to keep 2 network connections (wifi and mobile) raised in simultaneously (w.o. some funny tricks i'm going to show later in next topics) - system will disable mobile connection when wifi is connected. Obviously, this is correct behavior - save your battery and save your money (mobile internet sometimes very expensive). So that's why, apparently, we are not able to get all out network interfaces using sdk api.
Also we are not able to manage our network interfaces with this basic api. Fortunately Android sdk provides WifiManager to help us with wifi stuff. A bit more complicated to work with mobile networks (but possible). And finally vpn api is introduced in sdk 14. I'm going to talk about these things in the next few chapters.

Friday, April 20, 2012

Dialog box by PopupWindow

While screen size is increasing dialog boxes become essential part of most applications. Once in early February 2011 first Motorolla Xoom appeared on the shelves this problem became actual for most android developers. Of course, simple dialogs (to confirm some stuff deletion or agree with license agreement) have been used previously to smartphone applications. But the key points of the user interface were spread to the individual pages (Activities). Pages which looked good on small screens of smartphones became very ugly (because a lot of wasted space) on large screens of tablets.
As a quick solution for our problem Android SDK 11 offers us set of holographic themes (more commonly known as themes) among which there are Theme_Holo_Dialog and Theme_Holo_DialogWhenLarge (as well as few derived from them themes stylized for light colors and w.o. action bar). We won't deepen in these topics - I think every one faced with these stuff (besides, internal implementation of these themes deserves own investigation). Just note that using these themes we got quite nice dialogs stylized for holographic themes.
This dialog boxes were quite limited in styling - to perform even small changes in dialog positioning or appearance sometimes developer had to engaged in a tedious search for correct property in appropriate style. Obviously, we can't have 2 activity in foreground, so the question about the 2 open dialogs at the same time no longer relevant. Also do not forget the 'heaviness' of Activities. But support of activity may be a partial compensation for previous inconvenience.

Let's look at alternative approach (in my opinion unjustly deprived of attention) - PopupWindow. Been present from very first sdk, PopupWindow wasn't very popular (at least, I could not find more information than simple example how to create simple popup window). In my opinion, this is very configurable container, which gives us much more advantages than activity-dialog, so let's look on it closer.
First of all, there are 2 views to hold content:
   private View mContentView;  
   private View mPopupView;  

Depending on whether was mBackground provided or not (using PopupWindow#setBackgroundDrawable) mPopupView will be assigned whole mContentView or even composite PopupViewContainer (FrameLayout actually):
   private void preparePopup(WindowManager.LayoutParams p) {  
     ...  
     if (mBackground != null) {  
       final ViewGroup.LayoutParams layoutParams = mContentView.getLayoutParams();  
       int height = ViewGroup.LayoutParams.MATCH_PARENT;  
       if (layoutParams != null &&  
           layoutParams.height == ViewGroup.LayoutParams.WRAP_CONTENT) {  
         height = ViewGroup.LayoutParams.WRAP_CONTENT;  
       }  
       // when a background is available, we embed the content view  
       // within another view that owns the background drawable  
       PopupViewContainer popupViewContainer = new PopupViewContainer(mContext);  
       PopupViewContainer.LayoutParams listParams = new PopupViewContainer.LayoutParams(  
           ViewGroup.LayoutParams.MATCH_PARENT, height  
       );  
       popupViewContainer.setBackgroundDrawable(mBackground);  
       popupViewContainer.addView(mContentView, listParams);  
       mPopupView = popupViewContainer;  
     } else {  
       mPopupView = mContentView;  
     }  
     ...  
   }  
PopupViewContainer is a key point to answer on frequently asked question 'why do we need set setBackgroundDrawable to close our popup window when outside area is touched?' - PopupViewContainer is listening to the touch event and dismisses popup window:
     @Override  
     public boolean onTouchEvent(MotionEvent event) {  
       final int x = (int) event.getX();  
       final int y = (int) event.getY();  
       if ((event.getAction() == MotionEvent.ACTION_DOWN)  
           && ((x < 0) || (x >= getWidth()) || (y < 0) || (y >= getHeight()))) {  
         dismiss();  
         return true;  
       } else if (event.getAction() == MotionEvent.ACTION_OUTSIDE) {  
         dismiss();  
         return true;  
       } else {  
         return super.onTouchEvent(event);  
       }  
     }  
Content of PopupWindow is added to Activity by WindowManager (ViewManager#addView method is used). We are not limited in number of PopupWindows that can be added to page. But since ViewManager#addView creates new 'layer' for every PopupWindow they will be closed (in case of touch at outside area) in reverse order about how they were added.
From the fact that content of PopupWindow is View follows that we can apply animations to dialog box (using, for example, View#startAnimation). It may be useful when we want to show/hide dialog box with move effect or something like this (this often happens when we copy the behavior of ios apps :))
Finally, very often problem when you tried to create to show PopupWindow from activity lifecycle method:
 E/AndroidRuntime( 524): java.lang.RuntimeException: Unable to resume activity {packagename/packagename.ActivityName}: android.view.WindowManager$BadTokenException: Unable to add window -- token null is not valid; is your activity running?  

For those who haven't faced this problem here is solution: add your popup window showing to the message queue (using post method) to be run after all the lifecycle methods finish:
     viewRoot.post(new Runnable() {  
       public void run() {  
         popupWindow.showAtLocation(viewRoot, Gravity.TOP | Gravity.LEFT, 0, 0);  
       }  
     });  
Looks like this is essential requirement. I couldn't find explanation why this happens, looks like we have to wait until everything in Activity is set up.
Summing up, I want to say, that PopupWindow is a great choice when you need lightweight (and configurable) mechanism to communicate with user.