Showing posts with label Android. Show all posts
Showing posts with label Android. Show all posts

Friday, 26 October 2012

Generic adapter for all ListView in android

When using a custom list adapter, you have to do the same work: load row xml, get view by id, map logic data to view, then set adapter for ListView. Some thing like this:
public class CommonAdapter extends BaseAdapter {
 private Context context;
 private List lst;
 int layoutID;

 public CommonAdapter(Context context, List lstData, int layoutID) {
  this.context = context;
  this.lst = lstData;
  this.layoutID = layoutID;
 }

 public static class ViewHolder {
  public TextView Text;
  public TextView SubText;
 }

 public View getView(int position, View convertView, ViewGroup parent) {
  View v = convertView;
  ViewHolder viewHolder;
  if (v == null) {
   LayoutInflater inflater = (LayoutInflater) context
           .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
   v = inflater.inflate(layoutID, parent, false);
   viewHolder = new ViewHolder();
   viewHolder.Text = (TextView) v.findViewById(R.id.lstmText);
   viewHolder.SubText = (TextView) v.findViewById(R.id.lstmSub);
   v.setTag(viewHolder);
  } else {
   viewHolder = (ViewHolder) v.getTag();
  }
  viewHolder.Text.setText(lst.get(position).Text);
  viewHolder.SubText.setText(lst.get(position).SubText);

  Static.setiFont(viewHolder.Text);
  Static.setiFont(viewHolder.SubText);
  return v;
 }

 public int getCount() {
  return lst.size();
 }

 public Object getItem(int position) {
  return lst.get(position);
 }

 public long getItemId(int position) {
  return position;
 }
 
 public class CommonAdapterData {
  public int id;
  public String Text;
  public String SubText;
 }

}

For many kinds of list view 's row (difference template), we have to create an suitable adapter.
Now, we have another way which can make your work is easy.
First, we need the interface define the way to build row view.
public interface Adaptable {
 public View buildView(View v, LayoutInflater inflater, ViewGroup parent);
}

Then, we create a Generic adapter to use this interface above. There is no magic.
public class GenericAdapter extends BaseAdapter {
 private LayoutInflater inflater;
 private List items;

 @SuppressWarnings("unchecked")
 public GenericAdapter(List items, Context c) {
  this.items = (List) items;
  inflater = LayoutInflater.from(c);
 }

 @Override
 public int getCount() {  
  return items.size();
 }

 @Override
 public Object getItem(int position) {
  return items.get(position);
 }

 @Override
 public long getItemId(int position) {
  return position;
 }

 @Override
 public View getView(int position, View convertView, ViewGroup parent) {
  return items.get(position).buildView(convertView, inflater, parent); }

}

After that, we create an view holder which holder the control in row view (TextView, ImageView, EditText...) and we will map it with an id by using annotation.
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface InvokeView {
 int viewId();
}
Assume we have an entity:
public class Mobile {
 public String name;
 public int image;
 
 public Mobile(){}
 
 public Mobile(String name, int image){
  this.name = name;
  this.image = image;
 }
}
Now we create an BaseView abstract class, which can be load row xml layout, map view id to viewhoder by using reflect.
public abstract class BaseView implements Adaptable{
 private static final String TAG = "BaseView";
 protected int layoutId;
 protected T viewHolder;
 protected E entity;
 public BaseView(){
  
 }
 
 public BaseView(E entity, int layoutId){
  this.entity = entity;
  this.layoutId = layoutId;
 }
 protected void invokeView(View v){
  try {
   Field fs[] = viewHolder.getClass().getFields();
   for (Field f : fs) {

      InvokeView a = (InvokeView)
                              f.getAnnotation(InvokeView.class);
      int id = a.viewId();
      Log.d(TAG, "field name: " + f.getName());
      Log.d(TAG, "view id: " + id);
      Log.d(TAG, "class: " + f.getClass());
      f.set(viewHolder, v.findViewById(id));

   }
  } catch (Exception ex) {
   ex.printStackTrace();
  }
 }
 
 @SuppressWarnings("unchecked")
 @Override
 public View buildView(View v, LayoutInflater inflater, ViewGroup parent) {
  // load the view
  if (null == v) {
   v = inflater.inflate(layoutId, parent, false);
   // get the view
   invokeView(v);
   v.setTag(viewHolder);
  } else {
   viewHolder = (T) v.getTag();
  }

  // binding logic data to view
  mappingData(viewHolder, entity);

  return v;
 }
 
 protected abstract void mappingData(T viewHolder, E entity);
}
Then we define how to map data from entity to the view through view holder. This is a sub class of BaseView.
public class MobileView extends BaseView {
 public MobileView(Mobile mobile, int layoutId) {
  super(mobile, layoutId);
  this.viewHolder = new MobileViewHolder();
 }

 @Override
 public void mappingData(MobileViewHolder viewHolder, Mobile entity) {
  viewHolder.text.setText(entity.name);
  viewHolder.image.setBackgroundResource(entity.image);
 }

}
Finally, we can use our adapter for list view.
public class ListMobileActivity extends ListActivity {

 private static final int FRUIT_ID = 0;
 @Override
 public void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  List lst = new ArrayList();
  for(int i = 0; i < 10; i++){
   MobileView mv = new MobileView(new Mobile(String.valueOf(i), R.drawable.android_logo), R.layout.list_mobile);
   lst.add(mv);
  }
  setListAdapter(new GenericAdapter(lst, getApplicationContext()));
 }
}
Also, I give an source code, which can be found here

Thursday, 12 July 2012

Android: Trusting SSL certificates - Exchange Web Service



Previous post from crazybob doesn't work for me. The problem is I want to authenticate Exchange Web Service from Android.  And the long story has begun.

You will need the following tools:





Use the following command to create keystore:

keytool -importcert -v -trustcacerts -file "YourCerFile.cer" -al

ias parkgroup_restful -keystore "Output.bks" -provider org.bounc

ycastle.jce.provider.BouncyCastleProvider -providerpath "bcprov-jdk16-145.jar

" -storetype BKS -storepass "YourPass"
 It should show some result, then ask (choose yes):

Trust this certificate? [no]: yes
 Then verify if the certificates were imported correctly into the keystore:

keytool -list -keystore  "Output.bks"  -provi

der org.bouncycastle.jce.provider.BouncyCastleProvider -providerpath "bcprov-jdk16-145.jar" -storetype BKS -storepass "YourPass"
If success, result will be show like this:

Keystore type: BKS

Keystore provider: BC

Your keystore contains 1 entry

parkgroup_restful, Apr 10, 2012, trustedCertEntry,

Certificate fingerprint (MD5): 36:47:88:62:23:1C:F3:52:17:BE:7A:A9:94:56:19:18


Use keystore in your app.

Create custom trust manager

import java.security.GeneralSecurityException;
import java.security.KeyStore;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.List;

import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
import javax.net.ssl.X509TrustManager;

import android.util.Log;

/**
 * Manage trust certificate
 *
 * @author ductran
 *
 */
public class CustomTrustManager implements X509TrustManager
{
   
 private static final String TAG = CustomTrustManager.class.getSimpleName();

 static class LocalStoreX509TrustManager implements X509TrustManager
 {

  private X509TrustManager trustManager;

  LocalStoreX509TrustManager(KeyStore localTrustStore)
  {
   try
   {
    TrustManagerFactory tmf = TrustManagerFactory
      .getInstance(TrustManagerFactory.getDefaultAlgorithm());
    tmf.init(localTrustStore);

    trustManager = findX509TrustManager(tmf);
    if (trustManager == null)
    {
     throw new IllegalStateException("Couldn't find X509TrustManager");
    }
   } catch (GeneralSecurityException e)
   {
    throw new RuntimeException(e);
   }

  }

  @Override
  public void checkClientTrusted(X509Certificate[] chain, String authType)
    throws CertificateException
  {
   trustManager.checkClientTrusted(chain, authType);
  }

  @Override
  public void checkServerTrusted(X509Certificate[] chain, String authType)
    throws CertificateException
  {
   trustManager.checkServerTrusted(chain, authType);
  }

  @Override
  public X509Certificate[] getAcceptedIssuers()
  {
   return trustManager.getAcceptedIssuers();
  }
 }

 static X509TrustManager findX509TrustManager(TrustManagerFactory tmf)
 {
  TrustManager tms[] = tmf.getTrustManagers();
  for (int i = 0; i < tms.length; i++)
  {
   if (tms[i] instanceof X509TrustManager)
   {
    return (X509TrustManager) tms[i];
   }
  }

  return null;
 }

 private X509TrustManager defaultTrustManager;
 private X509TrustManager localTrustManager;

 private X509Certificate[] acceptedIssuers;

 public CustomTrustManager(KeyStore localKeyStore)
 {
  try
  {
   TrustManagerFactory tmf = TrustManagerFactory
     .getInstance(TrustManagerFactory.getDefaultAlgorithm());
   tmf.init((KeyStore) null);

   defaultTrustManager = findX509TrustManager(tmf);
   if (defaultTrustManager == null)
   {
    throw new IllegalStateException("Couldn't find X509TrustManager");
   }

   localTrustManager = new LocalStoreX509TrustManager(localKeyStore);

   List<x509certificate> allIssuers = new ArrayList<x509certificate>();
   for (X509Certificate cert : defaultTrustManager.getAcceptedIssuers())
   {
    allIssuers.add(cert);
   }
   for (X509Certificate cert : localTrustManager.getAcceptedIssuers())
   {
    allIssuers.add(cert);
   }
   acceptedIssuers = allIssuers.toArray(new X509Certificate[allIssuers
     .size()]);
  } catch (GeneralSecurityException e)
  {
   throw new RuntimeException(e);
  }

 }

 public void checkClientTrusted(X509Certificate[] chain, String authType)
   throws CertificateException
 {
  try
  {
   Log.d(TAG, "checkServerTrusted() with default trust manager...");
   defaultTrustManager.checkClientTrusted(chain, authType);
  } catch (CertificateException ce)
  {
   Log.d(TAG, "checkServerTrusted() with local trust manager...");
   localTrustManager.checkClientTrusted(chain, authType);
  }
 }

 public void checkServerTrusted(X509Certificate[] chain, String authType)
   throws CertificateException
 {
  try
  {
   Log.d(TAG, "checkServerTrusted() with default trust manager...");
   defaultTrustManager.checkServerTrusted(chain, authType);
  } catch (CertificateException ce)
  {
   Log.d(TAG, "checkServerTrusted() with local trust manager...");
   localTrustManager.checkServerTrusted(chain, authType);
  }
 }

 public X509Certificate[] getAcceptedIssuers()
 {
  return acceptedIssuers;
 }

}

Create MySSLSocketFactory






import java.io.IOException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.UnknownHostException;

import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory;

import org.apache.http.conn.ConnectTimeoutException;
import org.apache.http.conn.scheme.LayeredSocketFactory;
import org.apache.http.conn.ssl.X509HostnameVerifier;
import org.apache.http.params.HttpConnectionParams;
import org.apache.http.params.HttpParams;

/**
 *  loosely based on org.apache.http.conn.ssl.SSLSocketFactory
 * @author ductran
 *
 */
public class MySSLSocketFactory implements LayeredSocketFactory {

   // private SSLContext sslCtx;
    private SSLSocketFactory socketFactory;
    private X509HostnameVerifier hostnameVerifier;

    public MySSLSocketFactory(SSLContext sslCtx,
            X509HostnameVerifier hostnameVerifier) {
        //this.setSslCtx(sslCtx);
        this.socketFactory = sslCtx.getSocketFactory();
        this.hostnameVerifier = hostnameVerifier;
    }

    @Override
    public Socket connectSocket(Socket sock, String host, int port,
            InetAddress localAddress, int localPort, HttpParams params)
            throws IOException, UnknownHostException, ConnectTimeoutException {
        if (host == null) {
            throw new IllegalArgumentException("Target host may not be null.");
        }
        if (params == null) {
            throw new IllegalArgumentException("Parameters may not be null.");
        }

        SSLSocket sslsock = (SSLSocket) ((sock != null) ? sock : createSocket());

        if ((localAddress != null) || (localPort > 0)) {
            if (localPort < 0)
                localPort = 0;

            InetSocketAddress isa = new InetSocketAddress(localAddress,
                    localPort);
            sslsock.bind(isa);
        }

        int connTimeout = HttpConnectionParams.getConnectionTimeout(params);
        int soTimeout = HttpConnectionParams.getSoTimeout(params);

        InetSocketAddress remoteAddress = new InetSocketAddress(host, port);

        sslsock.connect(remoteAddress, connTimeout);

        sslsock.setSoTimeout(soTimeout);
        try {
            hostnameVerifier.verify(host, sslsock);
        } catch (IOException iox) {
            try {
                sslsock.close();
            } catch (Exception x) {
            }

            throw iox;
        }

        return sslsock;
    }

    @Override
    public Socket createSocket() throws IOException {
        return socketFactory.createSocket();
    }

    @Override
    public boolean isSecure(Socket sock) throws IllegalArgumentException {
        if (sock == null) {
            throw new IllegalArgumentException("Socket may not be null.");
        }

        if (!(sock instanceof SSLSocket)) {
            throw new IllegalArgumentException(
                    "Socket not created by this factory.");
        }

        if (sock.isClosed()) {
            throw new IllegalArgumentException("Socket is closed.");
        }
        return true;

    }
    @Override
    public Socket createSocket(Socket socket, String host, int port,
            boolean autoClose) throws IOException, UnknownHostException {
        SSLSocket sslSocket = (SSLSocket) socketFactory.createSocket(socket,
                host, port, autoClose);
        hostnameVerifier.verify(host, sslSocket);

        return sslSocket;
    }
}
And make request with HttpsClient:


public class HttpsClient
{
    private static final String KEYSTORE_PASSWORD = "1234567";
    private static final int MAX_CONN_PER_ROUTE = 30;
    private static final int MAX_CONNECTIONS = 30;
    private static final int TIMEOUT = 10 * 1000;
    private static DefaultHttpClient theClient = null;
    private HttpResponse  response = null;
    private static DefaultHttpClient getHttpClient() throws GeneralSecurityException{
  if (theClient == null)
  {
   SSLContext sslContext = createSslContext();
   MySSLSocketFactory socketFactory = new MySSLSocketFactory(sslContext,
     new BrowserCompatHostnameVerifier());
   theClient = createDefaultHttpClient(socketFactory);
  }
  return theClient;
 }

  public HttpResponse  getResponse(){
    return response;
 }

  /**
  * Excute the request
  */
  public void excuteRequest(String url, String username, String password, String requestBody){
  DefaultHttpClient client = getHttpClient();
  HttpPost httpPost = new HttpPost(url);
  httpPost.setHeader("Content-type", "text/xml; charset=utf-8");
  httpPost.setHeader("Keep-Alive", "300");
  httpPost.setHeader("Connection", "Keep-Alive");
  StringEntity se = new StringEntity(requestBody, "UTF-8");
  httpPost.setEntity(se);
  se.setContentType("text/xml");
  se.setContentEncoding("gzip,deflate");
  // Authentication
  client.addRequestInterceptor(preemptiveAuth, 0);
  // Set the proxy
  ProxySelectorRoutePlanner routePlanner = new ProxySelectorRoutePlanner(
    client.getConnectionManager().getSchemeRegistry(),
    ProxySelector.getDefault());
  client.setRoutePlanner(routePlanner);

  List<string> authPrefs = new ArrayList<string>();
  authPrefs.add(AuthPolicy.DIGEST);
  authPrefs.add(AuthPolicy.BASIC);
  authPrefs.add(AuthPolicy.NTLM);
  client.getParams().setParameter(HTTP_AUTH_SCHEME_PRIORITY, authPrefs);
             
                // Basic authentication
  httpPost.addHeader(
    "Authorization",
    "Basic "
      + Base64.encodeToString(
        (username() + ":" + password())
          .getBytes(), Base64.NO_WRAP));
  BasicHttpContext localContext = new BasicHttpContext();
  BasicScheme basicAuth = new BasicScheme();
  localContext.setAttribute("preemptive-auth", basicAuth);
  response = client.execute(httpPost, localContext);
           
     }

  /**
  * Authentication with preemptive
  */
 HttpRequestInterceptor preemptiveAuth = new HttpRequestInterceptor()
 {
  public void process(final HttpRequest request, final HttpContext context)
    throws HttpException, IOException
  {
   AuthState authState = (AuthState) context
     .getAttribute(ClientContext.TARGET_AUTH_STATE);
   CredentialsProvider credsProvider = (CredentialsProvider) context
     .getAttribute(ClientContext.CREDS_PROVIDER);
   HttpHost targetHost = (HttpHost) context
     .getAttribute(ExecutionContext.HTTP_TARGET_HOST);

   if (authState.getAuthScheme() == null)
   {
    AuthScope authScope = new AuthScope(targetHost.getHostName(),
      targetHost.getPort());
    Credentials creds = credsProvider.getCredentials(authScope);
    if (creds != null)
    {
     authState.setAuthScheme(new BasicScheme());
     authState.setCredentials(creds);
    }
   }
  }
 };

          private static DefaultHttpClient createDefaultHttpClient(
   SocketFactory socketFactory)
 {
  HttpParams params = new BasicHttpParams();
  HttpProtocolParams.setContentCharset(params, HTTP.DEFAULT_CONTENT_CHARSET);
  // HttpProtocolParams.setContentCharset(params, HTTP.UTF_8);
  HttpProtocolParams.setVersion(params, HttpVersion.HTTP_1_1);
  // Set the parameters for timeout, max connection
  HttpConnectionParams.setConnectionTimeout(params, TIMEOUT);
  ConnPerRoute connPerRoute = new ConnPerRouteBean(MAX_CONN_PER_ROUTE);
  ConnManagerParams.setMaxConnectionsPerRoute(params, connPerRoute);
  ConnManagerParams.setMaxTotalConnections(params, MAX_CONNECTIONS);

  // Register the scheme for Http and Https
  SchemeRegistry schemeRegistry = new SchemeRegistry();
  schemeRegistry.register(new Scheme("http", PlainSocketFactory
    .getSocketFactory(), 80));
  SocketFactory sslSocketFactory = SSLSocketFactory.getSocketFactory();
  if (socketFactory != null)
  {
   sslSocketFactory = socketFactory;
  }
  schemeRegistry.register(new Scheme("https", sslSocketFactory, 443));
  ClientConnectionManager cm = new ThreadSafeClientConnManager(params,
    schemeRegistry);
  return new DefaultHttpClient(cm, params);
 }


        /**
  * Create SSL Context from X509TrustManager
  *
  * @param clientAuth
  * @return
  * @throws GeneralSecurityException
  */
 private static SSLContext createSslContext() throws GeneralSecurityException
 {
  KeyStore trustStore = loadTrustStore();
  KeyStore keyStore = loadKeyStore();

  // CustomTrustManager myTrustManager = new CustomTrustManager(trustStore);
  EwsX509TrustManager myTrustManager = new EwsX509TrustManager(trustStore,
    null);
  TrustManager[] tms = new TrustManager[]
  { myTrustManager };

  KeyManager[] kms = null;
  KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory
    .getDefaultAlgorithm());
  kmf.init(keyStore, KEYSTORE_PASSWORD.toCharArray());
  kms = kmf.getKeyManagers();

  SSLContext context = SSLContext.getInstance("TLS");
  context.init(kms, tms, null);

  return context;
 }
}