Category Archives: Android

Using MonkeyTalk in AndroidStudio


Let me first introduce what MonkeyTalk is?
Monkey talk is a mobile app testing tool. MonkeyTalk automates real, functional interactive tests for iOS and Android apps – everything from simple “smoke tests” to sophisticated data-driven test suites. Native, mobile, and hybrid app, real devices or simulators. Moreover, automation saves time, it is re-usable, repeatable and so on. Monkey talk is an open source automation tool. It supports both Android and iPhone. It is very easy to learn and its a powerful functional testing tool.

Everything is in the web. So, why am I writing this blog?
Its because recently I have switched to android studio for android development. There are loads of tutorial based in Eclipse but very few based in Android Studio. So, I thought maybe this will be useful for everyone out there who is struggling to use MonkeyTalk in Android Studio.

Requirements for MonkeyTalk are….
1. Android Studio
2. MonkeyTalk IDE which you can download from this link

Procedure :
Before you proceed with this blog, I presume you know how to use android studio and gradle build system.
So, here we start.

There are two important steps to be followed:

Step 1: We build an instrumented apk using Android Studio

Step 2: We perform automation on instrumented apk using MonkeyTalk IDE

How to make an instrumented apk ?

      1. I assume you have a ready project in Android Studio which can be compiled successfully using Android Studio. You can get a test project from this github link.
      2. Now, in your app folder create a new folder named monkey-libs folder. Inside this folder place Monkey Talk Agent as shown in the github.
      3. Then, in your build.gradle file located inside app folder, add the aspectj dependency inside buildscript
         buildscript {  
           repositories {  
             mavenCentral()  
           }  
           dependencies {  
             classpath 'com.uphyca.gradle:gradle-android-aspectj-plugin:0.9.5'  
           }  
         }  
        
      4. Add aspectj plugin in dependencies of buildscript
      5. Then to activate the aspectj plugin, write the given code in build.gradle file
         apply plugin: 'android-aspectj'  
        
      6. Then compile the Java file with AspectJ plugin by writing the following code in build.gradle file
         dependencies {  
         compile fileTree(dir: 'libs', include: ['*.jar'])  
         monkeytalkCompile fileTree(dir: 'monkey-libs', include: ['*.jar'])  
         }  
        
      7. Thats all you need to do in build.gradle file.
      8. Now we need to create an instrumented apk. To get instrumented apk you need to create a folder name monkeytalk inside src folder. This monkey talk folder must contain AndroidManifest file. Just one AndroidManifest file is required. You can see the sample here .
      9. AndroidManifest file located inside monkeytalk must consist of the following permissions:
         <?xml version="1.0" encoding="utf-8"?>  
         <manifest xmlns:android="http://schemas.android.com/apk/res/android"  
           package="com.georgepapas.monkeytalkdemo" >  
           <uses-permission android:name="android.permission.GET_TASKS"/>  
           <uses-permission android:name="android.permission.INTERNET"/>  
         </manifest>  
        
      10. and thats all. A build instrumented with monkey talk aspects will be generated and installed in the emulator or your device by running the given code:
         ./gradlew clean installMonkeytalk  
        

Hence, you can find an instrumented apk in your device or emulator.

Now, how to run the automation ?

      1.  Start your monkey talk ide.
      2. Go to File > New > MonkeyTalk Project > project name (say TestMonkeyTalk)
      3. Right click on project TestMonkeyTalk > New > Script > ScriptName (say testScript)
      4. Open your device which has debugging mode enabled and start your app.
      5. Press Connect > Android Device.
      6. Then press the Record Button and start using your app. As you finish visiting all the pages of the app, press stop button.
      7. Hence, now you have a Script for automation.
      8. Now, to start the automation, press the play button.

Hence, you have successfully automated your testing procedure.

Good luck with monkey talk.

See you later in the next blog.

Until then Happy Coding 🙂

Advertisements

Uploading Image from Android App to Server ~ Programmatically :)


Its been such a long time ever since I have posted anything into the blog. Well not posting into it does not mean I have not learned anything new in the mean time. Well, I have learned so many different things in this month that I really have not been able to keep track of it. But, I will eventually post about all of them. Today, I am posting about the way we perform image uploading from android app to server. 

I thought uploading image to server was really hard. I researched for whole day. Different blogs were giving different types of suggestions. I was literally going through paralysis analysis. Then,after a day I finally uploaded image to the server. So, I am feeling great and happy right now.

To perform uploading, you will have to first understand the following things:

HTTP is a protocol for exchanging messages between a client and a server. An HTTP client opens a connection and sends a request message to an HTTP server; the server then returns a response message, usually containing the resource that was requested. After delivering the response, the server closes the connection. Request has three parts : a method name – GET or POST method , resources and the version of HTTP being used. Response also has three parts: the HTTP version, a response status code that gives the result of the request, and status code.

GET method : Requests data from a specified resource.

Query strings (name/value pairs) is send in the URL of a GET request. It should only be used to retrieve data

POST method : Submits data to be processed to a specified resource

Query strings (name/value pairs) is sent in the HTTP message body of a POST request.

Requests made through these methods return an instance of HTTPResponse. This instance of HTTPResponse has methods for accessing the response headers ( getHeader()), response info (getStatusCode()), response data (getData(), getInputStream()) and any trailers that might have been sent.

Although the java.net packages provides basic functionality for accessing resources via HTTP, it doesn’t provide the full flexibility or functionality needed by many applications.

So we need following jar file which can be downloaded from here to get an efficient, up-to-date, and feature-rich package implementing the client side of the most recent HTTP standards and recommendations-

  • httpcore-4.3.2.jar
  • httpmime-4.3.1.jar

Hence, we have understood how Client-Server communication is handled. Now through the following code snippet, we can upload image to the server:

private class ImageUploader extends AsyncTask<Void, Void, String> {

		@Override
		protected String doInBackground(Void... params) {
			// TODO Auto-generated method stub
			String result = "";

			// Client-side HTTP transport library
			HttpClient httpClient = new DefaultHttpClient();

			// using POST method
			HttpPost httpPostRequest = new HttpPost(imagePostUrl);
			try {

				// creating a file body consisting of the file that we want to
				// send to the server
				FileBody bin = new FileBody(imageFile);

				/**
				 * An HTTP entity is the majority of an HTTP request or
				 * response, consisting of some of the headers and the body, if
				 * present. It seems to be the entire request or response
				 * without the request or status line (although only certain
				 * header fields are considered part of the entity).
				 * 
				 * */
				MultipartEntityBuilder multiPartEntityBuilder = MultipartEntityBuilder.create();
				multiPartEntityBuilder.addPart("images[1]", bin);
				httpPostRequest.setEntity(multiPartEntityBuilder.build());

				// Execute POST request to the given URL
				HttpResponse httpResponse = null;
				httpResponse = httpClient.execute(httpPostRequest);

				// receive response as inputStream
				InputStream inputStream = null;
				inputStream = httpResponse.getEntity().getContent();

				if (inputStream != null)
					result = convertInputStreamToString(inputStream);
				else
					result = "Did not work!";
				return result;
			} catch (Exception e) {

				return null;
			}

			// return result;
		}

		@Override
		protected void onPreExecute() {
			// TODO Auto-generated method stub
			super.onPreExecute();
			uploadStatus.setText("Uploading image to server");
		}

		@Override
		protected void onPostExecute(String result) {
			// TODO Auto-generated method stub
			super.onPostExecute(result);
			uploadStatus.setText(result);
		}

	}

	private static String convertInputStreamToString(InputStream inputStream)
			throws IOException {
		BufferedReader bufferedReader = new BufferedReader(
				new InputStreamReader(inputStream));
		String line = "";
		String result = "";
		while ((line = bufferedReader.readLine()) != null)
			result += line;

		inputStream.close();
		return result;

	}

References:

http://www.jmarshall.com/easy/http/

http://www.innovation.ch/java/HTTPClient/getting_started.html

http://hc.apache.org/downloads.cgi

http://stackoverflow.com/questions/20284542/upload-photo-using-httppost-multipartentitybuilder

http://stackoverflow.com/questions/18964288/upload-a-file-through-an-http-form-via-multipartentitybuilder-with-a-progress

Relationship of Android with Thread


A thread is an execution context, which is all the information a CPU needs to execute a stream of instructions. It controls what executes in what order.

When using threads, CPU gives you the illusion that it’s doing multiple computations at the same time. It does that by spending a bit of time on each computation.

Threads are like two people using the same computer, who don’t have to share data explicitly but must carefully take turns. Conceptually, threads are just multiple worker bees buzzing around in the same address sapce. Each thread has its own stack, its own instructor pointer(aka program counter), etc. , but all threads in a process share the same memory.

You can kill one thread and others will still be running.

Android’s user interface thread

1.1 Main Thread
Android modifies the user interface and handles input events from one single user interface thread. This thread is also called the main thread.

Android collects all events in a queue and process an instance of the looper class.

xlooper_messagequeue10.png.pagespeed.ic.bCJly9aOKA

Is Main Thread enough for processing large data in Android ?


No, it’s not. If only main thread is used then all code of an Android application runs in the main thread and every statement is executed after each other.
If your perform the long lasting operation, for example accessing data from the internet, the application blocks until the corresponding operation has finished.

Then, what is the solution ?
To provide a good user experience, all potentially slow running operation in an Android application should run asynchronously, e.g. via some way of concurrency constructs of the Android framework. This includes all potential slow operations, like network, file and database access and complex calculations.

Android support the usage of the Thread class to perform asynchronous processing.

How to use threads in Android to obtain concurrency ?
Android provides android.os.Handler class or the AsyncTasks classes to handle concurrency.

1. android.os.Handler:
Handler class is used to register to a thread and provide a simple channel to send data to this thread. A Handler object registers itself with the thread in which it is created.
To use a handler, you have to subclass it and override the handleMessage() method to process messages.
Your thread can post messages via the sendMessage(Message) method or via the sendEmptyMessage() method to the Handler object.

For example:
The following code demonstrates the usage of a Handler via a View.
Assume your activity uses the following layout.

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >

    <ProgressBar
        android:id="@+id/progressBar1"
        style="?android:attr/progressBarStyleHorizontal"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:indeterminate="false"
        android:max="10"
        android:padding="4dip" >
    </ProgressBar>

   <TextView
        android:id="@+id/textView1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="" >
      </TextView>
    <Button
        android:id="@+id/button1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:onClick="startProgress"
        android:text="Start Progress" >
    </Button>

</LinearLayout> 

With the following the ProgressBar get updated once the users presses the Button.

public class ProgressTestActivity extends Activity {
  private ProgressBar progress;
  private TextView text;

  @Override
  public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);
    progress = (ProgressBar) findViewById(R.id.progressBar1);
    text = (TextView) findViewById(R.id.textView1);

  }

  public void startProgress(View view) {
    // Do something long
    Runnable runnable = new Runnable() {
      @Override
      public void run() {
        for (int i = 0; i <= 10; i++) {
          final int value = i;
           doFakeWork();
          progress.post(new Runnable() {
            @Override
            public void run() {
              text.setText("Updating");
              progress.setProgress(value);
            }
          });
        }
      }
    };
    new Thread(runnable).start();
  }

  // Simulating something timeconsuming
  private void doFakeWork() {
    try {
      Thread.sleep(2000);
    } catch (InterruptedException e) {
      e.printStackTrace();
    }
  }

} 

2. AsyncTask:
The AsyncTask class encapsulates the creation of background process and the synchronization with the main thread. It also supports reporting progress of the running tasks.

How to use AsyncTask ?

To use AysncTask, you must subclass it. AsyncTask uses generics and varargs. The parameters are the following

 AsyncTask <TypeOfVarArgParams , ProgressValue , ResultValue> 

An AsyncTask is started via the

execute()

method. This method calls the

doInBackground()

and the

onPostExecute()

method.
TypeOfVarArgParams is passed into the doInBackground() method as input, ProgressValue is used for progress information and and ResultValue must be returned from doInBackground() method and is passed to onPostExecute() as a parameter.

The doInBackground() method contains the coding instruction which should be performed in a background thread. This method runs automatically in a separate thread.

The onPostExecute() method synchronizes itself again with the user interface thread and allows it to be updated. This method is called by the framework once the doInBackground() method finishes.

The following code demonstrates how to use the AsyncTask class to download the content of a webpage.

Create the following layout.

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >

    <Button
        android:id="@+id/readWebpage"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:onClick="onClick"
        android:text="Load Webpage" >
    </Button>

    <TextView
        android:id="@+id/TextView01"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:text="Placeholder" >
    </TextView>

</LinearLayout> 

Add the android.permission.INTERNET permission to your AndroidManifest.xml file.

Change your activity to the following:

public class ReadWebpageAsyncTask extends Activity {
  private TextView textView;

  @Override
  public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);
    textView = (TextView) findViewById(R.id.TextView01);
  }

  private class DownloadWebPageTask extends AsyncTask<String, Void, String> {
    @Override
    protected String doInBackground(String... urls) {
      String response = "";
      for (String url : urls) {
        DefaultHttpClient client = new DefaultHttpClient();
        HttpGet httpGet = new HttpGet(url);
        try {
          HttpResponse execute = client.execute(httpGet);
          InputStream content = execute.getEntity().getContent();

          BufferedReader buffer = new BufferedReader(new InputStreamReader(content));
          String s = "";
          while ((s = buffer.readLine()) != null) {
            response += s;
          }

        } catch (Exception e) {
          e.printStackTrace();
        }
      }
      return response;
    }

    @Override
    protected void onPostExecute(String result) {
      textView.setText(result);
    }
  }

  public void onClick(View view) {
    DownloadWebPageTask task = new DownloadWebPageTask();
    task.execute(new String[] { "http://www.vogella.com" });

  }
} 

If you run your application and press your button then the content of the defined webpage is read in the background. Once this process is done your TextView is updated.

Handler Vs AsyncTask

handler vs AsyncTask
handler vs AsyncTask

WebP – enabling faster, smaller and more beautiful images


For an average app, image account for 69% of transferred bytes for mobile sites. The size of the image is growing fast because its a HiDPI world.

Hence, use WebP image format in your app.

Why?

WebP image format uses improved data compression. It uses both lossy and lossless modes.

Never heard of webp??

– WebM video format uses VP8 video codec.

-WebP is derived from VP8, essentially a key frame..

-WeP{P,M} are open-source, royalty free formats , open-sourced by Google in 2010. It has BSD-style license.

History of WebP….

– Initial release in 2010

Lossy compression of true-color graphics

-August, 2012

Lossless compression support

Transparency(alpha channel) support

-April, 2013

Color profile, animation , metadata

Now, ready for general purpose use, on the web and elsewhere!

Why should I use webp image format in my app?

In the age of Expensive Data Plans, it saves your bandwidth. Your app will have to download fewer bytes, it completes loading much earlier compared to the JPEG page.

How do I create a WebP file?

● Download WebP converter (Linux, OSX, Windows)
○ cwebp -q 80 image.png -o image.webp
○ dwebp image.webp -o image.png
● Download WebP Codec for Windows (Photo Viewer, Explorer, Office 2010+, …)
● Download Photoshop plugin (by Telegraphics)
● Download GIMP plugin
● ImageMagick, Pixelmator, XnView, IrfanView, GDAL, JPEGView have native support for
WebP
● Java, .NET, Flash, Python, Ruby, PHP bindings available to libwebp…
● img2webp.net online tool

WebP on Android !

Google+ on Android

● Photos and images comprise the vast majority of bytes
● On average, got 50% byte savings with WebP!
● Saving many terabytes of bandwidth per day…
● Saving our users money each time they use the app!

Android

Native library — all versions of Android

static {
System.loadLibrary("webp");
}
private Bitmap webpToBitmap(byte[] encoded) {
int[] width = new int[] { 0 };
int[] height = new int[] { 0 };
byte[] decoded = libwebp.WebPDecodeARGB(encoded, encoded.length, width, height);
int[] pixels = new int[decoded.length / 4];
ByteBuffer.wrap(decoded).asIntBuffer().get(pixels);
return Bitmap.createBitmap(pixels, width[0], height[0], Bitmap.Config.ARGB_8888);
}

webp-android-backport  for Android <4.0

For Novice Android Developer: Display Google Maps in Android Devices


Google Map is one of the search website of Google that allows us

  • to map out public transit directions,
  • get the map and landmark of the place that we wish to visit by creating our own maps
  • add lines and markers for more comprehensive and detailed in the Google Map box
  • get real time traffic information
  • view streets and identify locations by navigating through Google getting a street leve image of any city around the world

All we have to do is to type the places that we wish to go and visit, and the whole search will come out making it easier for us to view our search result.

There are so many advantages of embedding Google Maps into our Android Application. Google provides an easy way to integrate Google Maps into our Android Applications. To integrate Google Maps into our Android Applications, we need to work with Google Maps Android API v2

  • Download and configure the Google Play services SDK. The Google Maps Android API is distributed as part of this SDK.
  • Obtain an API key. To do this, you will need to register a project in the Google APIs Console, and get a signing certificate for your app.
  • Specify settings in the Application Manifest.
  • Add a map to a new or existing Android project.

If you dont know how the above steps need to be carried out, then this tutorial is just for you:

Step 1:

First of all, Download and configure the Google Play services SDK, we have to follow the given steps:

Install Google Play Services From : Android SDK Manager > extras > Google Play Services

Google Play services SDK_02

Step 2:

Import the library file google-play-services_lib by following the given step:
a. File > Other > Android > Android Project From Existing Code…
b. path to sdk/extras/google/google_play_services/libproject/google-play-services_lib
c. click finish

Import-Projects-in-Eclipse

Step 3:

Create a New Android Application Project  and add the google play services as library in our project:

location_in_google_map_v2_link_library

Step 4:

To access the Google Maps servers with the Maps API, we have to add a Maps API key to our application.
The key is free, we can use it with any of our applications that call the Maps API, and it supports an unlimited number of users.We can obtain a Maps API key from the Google APIs Console by providing your application’s signing certificate and its package name.

Obtaining a key for your application requires several steps. These steps are outlined here, and described in detail in the following sections.

Step 1
Retrieve information about your application’s certificate.This can be done by running the following command in terminal….

keytool -list -v -keystore ~/.android/debug.keystore -alias androiddebugkey 
-storepass android -keypass android

After running this code, you will get following data in your terminal:

Alias name: androiddebugkey
Creation date: Jun 12, 2013
Entry type: PrivateKeyEntry
Certificate chain length: 1
Certificate[1]:
Owner: CN=Android Debug, O=Android, C=US
Issuer: CN=Android Debug, O=Android, C=US
Serial number: 51b8444f
Valid from: Wed Jun 12 15:35:07 NPT 2013 until: Fri Jun 05 15:35:07 NPT 2043
Certificate fingerprints:
     MD5:  C5:47:79:D0:8A:F7:8E:BB:A1:72:4A:8E:35:89:EB:D9
     SHA1: 8B:6F:B4:2D:0E:56:51:53:35:C3:DB:82:3C:9C:87:B9:04:33:6D:38
     Signature algorithm name: SHA1withRSA
     Version: 3

SHA-1 fingerprint: The fingerprint is a unique text string generated from the commonly-used SHA-1 hashing algorithm. Because the fingerprint is itself unique, Google Maps uses it as a way to identify your application.

SHA1: 8B:6F:B4:2D:0E:56:51:53:35:C3:DB:82:3C:9C:87:B9:04:33:6D:38

Step 2:

Create a new project in the Google APIs Console.

To create a new project for Google Maps Android API,select the project name in the upper left hand corner and then click Create.

api1

Step 3:

Then, select Services from the left navigation bar and add the Google Maps Android API v2 as a service for the project by turning it on.

maps_on

This displays the Google Maps Android API Terms of Service. If you agree to the terms of service, click the checkbox below the terms of service, then click Accept. This returns you to the list of APIs and services.

Step 4:

You’re now ready to get a Maps API key.

To get the key:

  • Navigate to your project in the Google APIs Console.
  • In the left navigation bar, click API Access.
  • In the resulting page, click Create New Android Key….
  • In the resulting dialog, enter the SHA-1 fingerprint , that we obtained from Step 1 and after semicolon ( ; ) add your package name

sha-1
The Google APIs Console responds by displaying Key for Android apps (with certificates) followed by a forty-character API key, for example: AIzaSyBdVl-cTICSwYKrZ95SuvNw7dbMuDt1KG0
Copy this key value. You will use it in the next step.

Step 6:

Finally, you can add your key to your application and begin development.

The easiest way to test that your application is configured correctly is to add a simple map. We will have to make changes in two files: main.xml,manifest.xml and MainActivity.java. Please note that the code below is only useful for testing our settings in an application targeting Android API 12 or later, This code should not be used in a production application.

To add the key to your application, your manifest should consist of the following fields:

<?xml version="1.0" encoding="utf-8"?>

<manifest xmlns:android="http://schemas.android.com/apk/res/android"

   package="com.example.demomapproject"

   android:versionCode="1"

   android:versionName="1.0" >

    <uses-sdk

        android:minSdkVersion="8"

        android:targetSdkVersion="16" />

    <!-- Because version 2 of the Google Maps Android API requires OpenGL ES version 2
     External services can detect this notification and act accordingly.
     So, we need to add this in our manifest -->

    <uses-feature

        android:glEsVersion="0x00020000"

        android:required="true"/>

    <!--Permissions that give the application access to
         Android system features and to the Google Maps servers.-->

    <permission

        android:name="com.example.demomapproject.permission.MAPS_RECEIVE"

        android:protectionLevel="signature" />

    <uses-permission android:name="com.example.demomapproject.permission.MAPS_RECEIVE" />

    <uses-permission android:name="android.permission.INTERNET" />

    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />

    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

    <uses-permission android:name="com.google.android.providers.gsf.permission.READ_GSERVICES" />

    <!--
     The following two permissions are not required to use

     Google Maps Android API v2, but are recommended.
    -->

    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />

    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />

     <application

        android:allowBackup="true"

        android:icon="@drawable/ic_launcher"

        android:label="@string/app_name"

        android:theme="@style/AppTheme" >

     <!--
    From here, the Maps API reads the key value and passes it to the Google Maps server,
         which then confirms that you have access to Google Maps data.

        This element sets the key com.google.android.maps.v2.API_KEY to the value API_KEY
         and makes the API key visible to any MapFragment in your application. 
    -->    

        <meta-data

            android:name="com.google.android.maps.v2.API_KEY"

            android:value="AIzaSyA5idXLlKzifjzXIO_j9tQ7CAVYEZuH-10" />

        <activity

            android:name="com.example.demomapproject.MainActivity"

            android:label="@string/app_name" >

            <intent-filter>

                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />

            </intent-filter>

        </activity>

    </application>

</manifest>

Your main.xml should consist of something like this

<?xml version="1.0" encoding="utf-8"?>
<fragment xmlns:android="http://schemas.android.com/apk/res/android"
          android:id="@+id/map"
          android:layout_width="match_parent"
          android:layout_height="match_parent"
          android:name="com.google.android.gms.maps.MapFragment"/>

Your MainActivity.Java should consist of something like this

package com.example.mapdemo;

import android.app.Activity;
import android.os.Bundle;

public class MainActivity extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
    }
}

Build and run your application. You should see a map. If you don’t see a map, confirm that you’ve completed all of the steps appearing earlier in this document.

Happy Coding

🙂

TextView HyperLink – Change Color and Remove Underline


Sometimes, we need to display hyperlink in textview.

This hyperlink can of following format:

<strong><a href=”\&quot;index.php?option=com_socialapp&amp;task=profile.viewprofile&amp;userid=42\&quot;”>admin</a> is attending <a href=”_QQQ_/jsocial_app/index.php?option=com_socialapp&amp;view=events&amp;task=viewevent&amp;eventid=137&amp;Itemid=238_QQQ_”>Alchemist Rocks</a>.</strong>

Which will be seen in the web view as displayed below

Screenshot-Mozilla Firefox (Private Browsing)

How do we create such text in android textview ?

Follow the given steps and you will successfully create a textview whose links will be displayed as shown in the webview:

1. Create a textview in xml

  <TextView  
       android:id="@+id/profileFeedTitle"  
       android:layout_width="wrap_content"  
       android:layout_height="wrap_content"  
       android:layout_alignParentTop="true"  
       android:autoLink="web" //link the content of web  
       android:textColorLink="#576586" //change the color of the link  
       android:textColor="#555555" />  

2. In your activity, initialize the String and textview as given in the following code snippet

 String webLinkText = <a href="https://prativas.files.wordpress.com/2013/05/screenshot-mozilla-firefox-private-browsing.png"><img src="https://prativas.files.wordpress.com/2013/05/screenshot-mozilla-firefox-private-browsing.png" alt="Screenshot-Mozilla Firefox (Private Browsing)" width="293" height="254" class="alignnone size-full wp-image-291" /></a>  
 TextView linkTextView = (TextView) findViewById(R.id.profileFeedTitle);  
 holder.profileFeedTitle.setText(Html.fromHtml(webLinkText)));  

3. Run your project.

When you run your project, you will see a textview similar to the give figure:

tt_1

Here, in the given figure, you will see the user names in blue color, which is just as we want……..
But, we dont need the underline. To remove underline

i. Create a new class named as URLSpanNoUnderline as shown in the given code snippet:

 package com.example;  
 import android.text.TextPaint;  
 import android.text.style.URLSpan;  
 public class URLSpanNoUnderline extends URLSpan {  
      public URLSpanNoUnderline(String p_Url) {  
           super(p_Url);  
      }  
      public void updateDrawState(TextPaint p_DrawState) {  
           super.updateDrawState(p_DrawState);  
           p_DrawState.setUnderlineText(false);  
      }  
 }  

ii. Go to your activity and replace the code

 String webLinkText = <a href="https://prativas.files.wordpress.com/2013/05/screenshot-mozilla-firefox-private-browsing.png"><img src="https://prativas.files.wordpress.com/2013/05/screenshot-mozilla-firefox-private-browsing.png" alt="Screenshot-Mozilla Firefox (Private Browsing)" width="293" height="254" class="alignnone size-full wp-image-291" /></a>  
 TextView linkTextView = (TextView) findViewById(R.id.profileFeedTitle);  
 holder.profileFeedTitle.setText(Html.fromHtml(webLinkText)));  

by the following code snippet:

 String webLinkText = <a href="https://prativas.files.wordpress.com/2013/05/screenshot-mozilla-firefox-private-browsing.png"><img src="https://prativas.files.wordpress.com/2013/05/screenshot-mozilla-firefox-private-browsing.png" alt="Screenshot-Mozilla Firefox (Private Browsing)" width="293" height="254" class="alignnone size-full wp-image-291" /></a>  
 TextView linkTextView = (TextView) findViewById(R.id.profileFeedTitle);  
 //set user name in blue color and remove underline from the textview 
Spannable spannedText = Spannable.Factory.getInstance().newSpannable(
				Html.fromHtml(webLinkText));
Spannable processedText = removeUnderlines(spannedText);
if (holder.profileFeedTitle != null) {
	holder.profileFeedTitle.setText(processedText);
}  

iii. then, add the given function in your activity:



/**
 * Removes URL underlines in a string by replacing URLSpan occurrences by
 * URLSpanNoUnderline objects.
 *
 * @param p_Text A Spannable object. For example, a TextView casted as
 *               Spannable.
 */

 public static Spannable removeUnderlines(Spannable p_Text) {  
           URLSpan[] spans = p_Text.getSpans(0, p_Text.length(), URLSpan.class);  
           for (URLSpan span : spans) {  
                int start = p_Text.getSpanStart(span);  
                int end = p_Text.getSpanEnd(span);  
                p_Text.removeSpan(span);  
                span = new URLSpanNoUnderline(span.getURL());  
                p_Text.setSpan(span, start, end, 0);  
           }  
           return p_Text;  
      }  

Then, run your code, you will get the perfect textview as shown in the given screenshot:

tt2

How did the underline disappeared ? What did the given class and function do ?

Heres the explanation :

As you may already know, the TextView object has a property named android:autoLink that creates HTML or email links automatically. You also got properties to change link colors such as android:textColorLink and android:textColorHighlight. The only missing option many of us would like to have is a way to remove the underline under the link itself.

Sadly, removing the underline seems to be somewhat “hard” to achieve. So i had added the given a news class and a new function.

Custom Layout in ActionBarSherlock


Today, we are going to create a IcsSpinner withing the ActionBarSherlock in Android. The snapshot is given below:
device-2013-02-05-141413

First of all, in res > menu > create menu_configuration_activity with following codes

1:  <?xml version="1.0" encoding="utf-8"?>  
2:  <menu xmlns:android="http://schemas.android.com/apk/res/android" >  
3:    <item  
4:      android:id="@+id/menuConfigLanguageSelector"  
5:      android:actionLayout="@layout/layout_config_menu"  
6:      android:icon="@drawable/ic_setting"  
7:      android:showAsAction="always"  
8:      android:title="language"/>  
9:    <item  
10:      android:id="@+id/menuConfigDone"  
11:      android:icon="@drawable/ic_done"  
12:      android:showAsAction="always"  
13:      android:title="OK"/>  
14:  </menu>  

Here, in line number 5, we have assigned a custom layout. This custom layout is created in res > layout > layout_config_menu with following contents:

1:  <?xml version="1.0" encoding="utf-8"?>  
2:  <com.actionbarsherlock.internal.widget.IcsSpinner xmlns:android="http://schemas.android.com/apk/res/android"  
3:    android:id="@+id/languageSelectSpinner"  
4:    android:layout_width="wrap_content"  
5:    android:layout_height="wrap_content" />  

Then in the activity, where you want to place this actionbar, do as given below:

1:  @Override  
2:       public boolean onCreateOptionsMenu(com.actionbarsherlock.view.Menu menu) {  
3:            getSupportMenuInflater().inflate(R.menu.menu_configuration_activity,  
4:                      menu);  
5:            MenuItem itemLanguageSelector = menu  
6:                      .findItem(R.id.menuConfigLanguageSelector);  
7:            IcsSpinner icsSpinner = (IcsSpinner) itemLanguageSelector  
8:                      .getActionView();  
9:          Context subContext = getSupportActionBar().getThemedContext();  
10:            ArrayAdapter<CharSequence> list = ArrayAdapter.createFromResource(  
11:                      subContext, R.array.language_selection,  
12:                      com.actionbarsherlock.R.layout.sherlock_spinner_item);  
13:            list.setDropDownViewResource(R.layout.sherlock_spinner_dropdown_item);  
14:            icsSpinner.setAdapter(list);  
15:            icsSpinner.setOnItemSelectedListener(new OnItemSelectedListener() {  
16:                 @Override  
17:                 public void onItemSelected(IcsAdapterView<?> parent, View view,  
18:                           int position, long id) {  
19:                      //do something with the selected item  
20:                 }  
21:                 @Override  
22:                 public void onNothingSelected(IcsAdapterView<?> parent) {  
23:                      Log.i("Test click", "Inside on nothing selected");  
24:                 }  
25:            });  
26:            return super.onCreateOptionsMenu(menu);  
27:       };  
28:       @Override  
29:       public boolean onOptionsItemSelected(MenuItem item) {  
30:            if (item.getTitle().toString().equals("OK")) {  
31:                 if (selectedCategory != null && sourceAndLanguage != null) {  
32:                      try {  
33:                           // initAppWidget();  
34:                           saveTheUserValueInPref(selectedCategory, sourceAndLanguage);  
35:                           showAppWidget();  
36:                      } catch (Exception e) { // TODO: handle exception }  
37:                      }  
38:                 }  
39:            }  
40:            return super.onOptionsItemSelected(item);  
41:       }  

Dont forget to extend sherlockactivity in your activity to implement actionbarsherlock
Now, your IcsSpinner is ready to be used in ActionBarSherlock 😀
Happy Coding 😀

Create Amazing RadioGroup in Android


The main aim of this tutoral is to help you create a RadioGroup as shown in the given snapshot
radiogroupwithsectionheader

Download the source code from HERE .
In this project, I have basically done the following things:
1. I created a custom title view for the project that display the message “Select a source”
2. I created a MainActivity. Its layout consists of only one ScrollView. The ScrollView acts as a container for radio groups in our project.
3. I created one UI class named as RadioGroupHeaderUI.
4. While initializing the RadioGroupHeaderUI class, pass the context and the scroll view layout from the activity to the class.
Now, our project is ready to use.
Test it, and message me if any issues arrive
Happy Coding 😀

Dynamic Views in Android


This tutorial is about creating dynamic views in android based on json data. These json data can be a server response or may be not. For everybodies sake, I have stored my json data in the asset folder itself. But, we can use this tutorial by fetching json data from whereever you want. This may help you to fetch your json data. Ok, now lets get started.
What are dynamic views?
Its the process of using one activity as a container and drawing multiple view in the same activity. These views are not declare in XML. We are generating our widgets dynamically.

Our Scenario:
We will create a Profile Activity. The MainActivity is the ViewProfileActivity. EditProfileActivity creates the Dynamic Views and stores user inputs. We have three snapshots of our project.
Dynamic View Project

ViewProfileActivity-SS(1) is the screenshot of the Profile Page of the user. The Edit Profile button located in the top of the ViewProfileActivity starts the EditProfileActivity represented by EditProfileActivity-SS(2). EditProfileActivity displays the dynamic views. It allows user to provide their inputs. At the end of the layout, there is a save button through which the user can store their inputs and finish the EditProfileActivity. After the user presses the Save button, they are redirected to the ViewProfileActivity which displays all their inputs stored in the dynamic widgets.

ViewProfileActivity just displays the data.
The layout for ViewProfileActivity is as follows:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >
    <RelativeLayout
        android:id="@+id/btnListBar_profile"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:background="@color/repeating_bgcolor"
            android:paddingBottom="4dp"
        android:paddingTop="4dp" >
        <LinearLayout
            android:layout_width="fill_parent"
            android:layout_height="33dp"
            android:layout_centerInParent="true"
            android:layout_margin="5dp"
            android:background="@color/border_button"
            android:gravity="center"
            android:orientation="horizontal"
            android:padding="1dp"
            android:weightSum="3" >
            <Button
                android:id="@+id/btnEditProfile"
                style="@style/CustomProfileButtonStyle"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:background="@android:color/transparent"
                android:text="Edit Profile"
                android:textColor="@color/text_saved"
                android:textSize="14sp" />
        </LinearLayout>
    </RelativeLayout>
    <TextView
        android:id="@+id/field"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:editable="false"
        android:padding="5dp"
        android:shadowColor="#FFFFFF"
        android:shadowDy="1"
        android:shadowRadius="0.01"
        android:text="Details of user profile"
        android:textColor="#000000"
        android:textSize="12sp"
        android:textStyle="bold" />

    <TextView
        android:id="@+id/detailUserData"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:editable="false"
        android:padding="5dp"
        android:shadowColor="#FFFFFF"
        android:shadowDy="1"
        android:shadowRadius="0.01"
        android:text="Click Edit Profile To Fill This Data"
        android:textColor="#000000"
        android:textSize="12sp"
        android:textStyle="bold" />

</LinearLayout>

The content of ViewProfileActivity is as follows:

public class ViewProfileActivity extends Activity {
    Button editProfile;
    ProfileInfoHolder profileInfoHolder;
    ImageView userProfileImage;
    public static final int EDIT_PROFILE_DATA = 1212;
    public static final int CAMERA_UPLOAD = 1313;
    public static final int MEDIA_UPLOAD = 1888;
    TextView detailUserData;
    protected void onCreate(android.os.Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.profile_page_display);
        initViews();

    };

    public void initViews() {
        profileInfoHolder = new ProfileInfoHolder();
        editProfile = (Button) findViewById(R.id.btnEditProfile);

        editProfile.setOnClickListener(new OnClickListener() {

            @Override
            public void onClick(View v) {

                Intent editDataIntent = new Intent(ViewProfileActivity.this,
                        EditProfileActivity.class);
                startActivityForResult(editDataIntent, EDIT_PROFILE_DATA);

            }
        });
        detailUserData = (TextView) findViewById(R.id.detailUserData);

    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        // TODO Auto-generated method stub
        super.onActivityResult(requestCode, resultCode, data);
        Log.i("Result Code", resultCode + "");
        if (requestCode == EDIT_PROFILE_DATA) {

            String detailInformation = "";
            LinkedHashMap<String, String> userFields = new LinkedHashMap<String, String>();
            userFields = UtilityClass.EDIT_PROFILE_WITH_FIELD_NAME;

            // testing testing
            String fieldValueAndPrivacy = null;// = new
                                                // ArrayList<String>();
            LinkedHashMap<String, ArrayList<String>> editProfileHashValues = new LinkedHashMap<String, ArrayList<String>>();

            Iterator fieldIterator = userFields.entrySet().iterator();
            while (fieldIterator.hasNext()) {
                Map.Entry fieldPairs = (Map.Entry) fieldIterator.next();
                String fieldName = (String) fieldPairs.getKey();
                String fieldRunTimeValue = (String) fieldPairs.getValue();
                detailInformation += fieldName + " => " + fieldRunTimeValue
                        + "\n";

            }
            detailUserData.setText(detailInformation);

        }
    }
}

The main work is done by EditProfileActivity. The functionalities of EditProfileActivity are listed below:
1.It fetches the json data from the asset folder named “jsonresponse.txt” and then parses the json data to the java objects. These java objects are created from helper classes. Helper classes are specifically create to hold the parsed json data. We are using two helper classes: Field and ProfileInfoHolder.There is a demo of Parser class:
The contents of helper class Field are as follows:


public class Field implements Serializable {
    public String editProfileFieldId;
    public String editProfileFieldType = null;
    public String editProfileFieldGroup;
    public int ePFieldIsPublished;
    public int ePFieldIsSearchable;
    public int ePFieldIsVisible;
    public int ePFieldIsRequired;
    public int ePFieldIsRegistered;
    public int ePFieldMinimumChars;
    public int ePFieldMaximumChars;
    public int ePFieldOrdering;
    public String editProfileFieldName = null;
    public String editProfileFieldTips = null;
    public String editProfileFieldCode = null;
    public String editProfileFieldValue = null;
    public String editProfileFieldPrivacyAccess = null;
}

The contents of helper class ProfileInfoHolder are as follows:


public class ProfileInfoHolder {
    public static ArrayList<Field> listFieldType = new ArrayList<Field>();
    public static ArrayList<String> nameOfFieldHeader = new ArrayList<String>();
    public static LinkedHashMap<String, ArrayList<Field>> fieldHashValues = new LinkedHashMap<String, ArrayList<Field>>();
    public String userId;
    public String userName;
    public String userEmail;
}

The parser class parses the json data into java objects in the following way:


public class JsonParser {
    public static LinkedHashMap<String, ArrayList<Field>> parseUserProfileDetails(
            String jsonString) throws JSONException {
        LinkedHashMap<String, ArrayList<Field>> hashValuesOfJsonData = new LinkedHashMap<String, ArrayList<Field>>();
        ArrayList<Field> listFieldArrays = new ArrayList<Field>();
        String fieldHeaderName = "nothing";
        ProfileInfoHolder profileDetails = new ProfileInfoHolder();
        JSONObject profileDetailsObj = new JSONObject(jsonString);
        profileDetails.userId = profileDetailsObj.getString("id");
        Log.i("profile uid", profileDetails.userId);
        profileDetails.userName = profileDetailsObj.getString("name");
        Log.i("profile user name", profileDetails.userName);
        profileDetails.userEmail = profileDetailsObj.getString("email");
        Log.i("Profile email", profileDetails.userEmail);
        JSONObject fieldsObject = profileDetailsObj.getJSONObject("fields");
        JSONArray fieldNamesArray = fieldsObject.names();
        // method to parse fields of a category
        for (int i = 0; i < fieldNamesArray.length(); i++) {

            JSONArray fieldsArray = fieldsObject.getJSONArray(fieldNamesArray
                    .get(i).toString());
            fieldHeaderName = fieldNamesArray.get(i).toString();

            listFieldArrays = parseCategoryField(fieldsArray);
            Field field = new Field();
            field.editProfileFieldGroup = fieldHeaderName;
            hashValuesOfJsonData.put(fieldHeaderName, listFieldArrays);
        }
        return hashValuesOfJsonData;
    }
    public static ArrayList<Field> parseCategoryField(JSONArray fieldArray)
            throws JSONException {
        ArrayList<Field> fieldArrayList = new ArrayList<Field>();
        int arrLength = fieldArray.length();
        for (int i = 0; i < arrLength; i++) {
            Field field = new Field();
            JSONObject fieldObj = fieldArray.getJSONObject(i);
            field.editProfileFieldId = fieldObj.getString("id");
            field.editProfileFieldType = fieldObj.getString("type");
            // for int
            field.ePFieldIsVisible = fieldObj.getInt("visible");
            field.ePFieldIsRequired = fieldObj.getInt("required");
            field.ePFieldIsSearchable = fieldObj.getInt("searchable");
            field.ePFieldIsRegistered = fieldObj.getInt("registration");
            // for int
            field.ePFieldOrdering = fieldObj.getInt("ordering");
            field.ePFieldMinimumChars = fieldObj.getInt("min");
            field.ePFieldMaximumChars = fieldObj.getInt("max");
            // for strings
            field.editProfileFieldName = fieldObj.getString("name");
            field.editProfileFieldTips = fieldObj.getString("tips");
            field.editProfileFieldValue = fieldObj.getString("value");
            field.editProfileFieldPrivacyAccess = fieldObj.getString("access");
             fieldArrayList.add(field);
        }
        return fieldArrayList;
    }

2.It instantiates the DynamicFormCreation class. Dynamic form creation class creates dynamic widgets. While instantiating the class, we pass the profile field objects and ids. We pass ids because we can manually assign ids when we create dynamic widgets. Passing ids is important because it makes it easy to store user inputs from these widgets.The content of DynamicFormCreation class is given below:

public class DynamicFormCreation {
    public int extraFieldId;
    private Context context;
    int intToDpi1, intToDpi2, intToDpi3, intToDpi4, intToDpi5, intToDpi45,
            intToDpi0, intToDpi175, intToDpi125, intToDpi20, intToDpi80;
    public ImageView imageViewToUpload;
    private LinearLayout.LayoutParams layoutParamsBasic;
    public DynamicFormCreation(Context context) {
        this.context = context;
        initDpiData();
    }
    public void initDpiData() {
        intToDpi1 = UtilityClass.intToDpi(1, context);
        intToDpi2 = UtilityClass.intToDpi(2, context);
        intToDpi3 = UtilityClass.intToDpi(3, context);
        intToDpi4 = UtilityClass.intToDpi(4, context);
        intToDpi5 = UtilityClass.intToDpi(5, context);
        }
    public View getRuntimeView(Field formFields, int lastFieldId) {
        View mView = null;
        mView = getTextFields(formFields, lastFieldId);
        return mView;
    }

    public View getTextFields(Field formFields, int parentId) {
        LinearLayout rel = UtilityClass.getLinearLayoutHorizontal(context);
        TextView titleTextView = createTextView(context, formFields, parentId);
        rel.addView(titleTextView);
        parentId++;
        EditText editTextField = createEditText(
                formFields.editProfileFieldTips, context, parentId);
        if (formFields.editProfileFieldType.equals("url")) {
            Log.i("Enters here", "tag is set");
            editTextField.setTag("urlTextfield");
        }
        if (formFields.editProfileFieldType.equals("email")) {
            editTextField.setInputType(InputType.TYPE_CLASS_TEXT
                    | InputType.TYPE_TEXT_VARIATION_EMAIL_ADDRESS);
        } else if (formFields.editProfileFieldType.equals("url")) {
            editTextField.setInputType(InputType.TYPE_CLASS_TEXT
                    | InputType.TYPE_TEXT_VARIATION_URI);
        }
        parentId++;
        editTextField.setTag("editText");
        if (formFields.editProfileFieldType.equals("label")) {
            editTextField.setTag("labelEditText");
            editTextField.setEnabled(false);
        }
        rel.addView(editTextField);
        if (formFields.ePFieldIsRequired == 1) {
            editTextField.addTextChangedListener(new CustomTextChangedListener(
                    context, editTextField, titleTextView,
                    formFields.editProfileFieldType));
        }
        extraFieldId = parentId++;
        rel.setId(extraFieldId);
        return rel;
    }

    public TextView createTextView(Context context, Field formField,
            int parentId) {
        TextView textView = new TextView(context);
        LinearLayout.LayoutParams layoutParamsBasic = UtilityClass.getLayoutParams();
        layoutParamsBasic.width = intToDpi80;
        textView.setLayoutParams(layoutParamsBasic);
        String text = formField.editProfileFieldName;
        textView.setText(text);
        if (formField.ePFieldIsRequired == 1) {
            textView.setText(text + "*");
        } else {
            textView.setText(text);
        }
        extraFieldId = parentId++;
        textView.setPadding(intToDpi1, intToDpi1, intToDpi1, intToDpi1);
        textView.setShadowLayer(1, 1, 1, Color.WHITE);
        textView.setId(extraFieldId);
        return textView;
    }
    public EditText createEditText(String hint, Context context, int parentId) {
        EditText editText = new EditText(context);
        editText.setTextSize(10);
        editText.setPadding(intToDpi2, intToDpi2, intToDpi2, intToDpi2);
        editText.setWidth(UtilityClass.intToDpi(175, context));
        editText.setGravity(Gravity.CENTER);
        LinearLayout.LayoutParams layoutParamsBasic = UtilityClass.getLayoutParams();
        editText.setLayoutParams(layoutParamsBasic);
        extraFieldId = parentId++;
        editText.setId(extraFieldId);
        if (!hint.equals(null))editText.setHint(hint);
        return editText;
    }

}

3.It instantiates StoreDynamicFormValues class to store the user inputs from the dynamic widgets. We have to pass the container layout where we have created our dynamic views and arraylist where we have stored the profile fields and ids for dynamic views. The content of StoreDynamicFormValues is given below:

public class StoreDynamicFormValues {
    public ArrayList<TextView> inCorrectFieldsList;
    public LinkedHashMap<String, String> postFieldsMap;
    public LinkedHashMap<String, String> displayFieldsMap;
    public LinkedHashMap<String, String> privacyFieldsMap;
    private TextView textTitle;
    private EditText editTextRunTime;
    private Pattern pattern;
    String hypertextSelected;
    private Matcher matcher;

    public void getRegistrationFormFields(LinearLayout editPageLayout,
            HashMap<String, Field> displayedExtraFieldsMap) {
        inCorrectFieldsList = new ArrayList<TextView>();
        privacyFieldsMap = new LinkedHashMap<String, String>();
        postFieldsMap = new LinkedHashMap<String, String>();
        displayFieldsMap = new LinkedHashMap<String, String>();
        getFormFields(editPageLayout,(LinkedHashMap<String, Field>) displayedExtraFieldsMap);
    }
    private void getFormFields(LinearLayout layoutCoreFields,
            LinkedHashMap<String, Field> coreFieldsMap) {
        Field runTimeFormFields = null;
        for (int i = 0; i < layoutCoreFields.getChildCount(); i++) {
            View view = layoutCoreFields.getChildAt(i);
            if (view instanceof LinearLayout) {
                LinearLayout rel = (LinearLayout) view;
                for (int j = 0; j < rel.getChildCount(); j++) {
                    View mView = (View) rel.getChildAt(j);
                    if (mView instanceof LinearLayout) {
                        LinearLayout runTimeUserView = (LinearLayout) mView;
                        textTitle = (TextView) runTimeUserView.getChildAt(0);
                        runTimeFormFields = coreFieldsMap.get(String.valueOf(runTimeUserView.getId()));                 
                        for (int p = 0; p < runTimeUserView.getChildCount(); p++) {
                            View runTimeView = runTimeUserView.getChildAt(p);
                            if (runTimeView instanceof EditText) {
                                editTextRunTime = (EditText) runTimeView;
                                String text = editTextRunTime.getText()
                                        .toString();
                                if ((text.equals("") && editTextRunTime
                                        .isEnabled())) {
                                    textTitle.setTextColor(Color.RED);
                                    inCorrectFieldsList.add(textTitle);
                                }
                                runTimeFormFields.editProfileFieldValue = text;
                            }
                            postFieldsMap.put(runTimeFormFields.editProfileFieldId, runTimeFormFields.editProfileFieldValue);
                            displayFieldsMap.put(runTimeFormFields.editProfileFieldName, runTimeFormFields.editProfileFieldValue);
                        }
                    }
                }
            }
        }
    }
}

Hence, its time to summarise the content of EditProfileActivity. The layout for EditProfileActivity is as follows:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >

    <ScrollView
        android:layout_width="fill_parent"
        android:layout_height="fill_parent" >

        <LinearLayout
            android:id="@+id/linearLayoutForEdit"
            android:layout_width="fill_parent"
            android:layout_height="fill_parent"
            android:orientation="vertical" />
    </ScrollView>

</LinearLayout>

The complete source code for EditProfileActivity is given below

public class EditProfileActivity extends Activity {
    TextView textView;

    private ActionMode actionMode;

    private String JSON_FILENAME = "jsonresponse.txt";
    DynamicFormCreation runTimeUiLibs;
    LinearLayout editPageLayout;
    LinearLayout.LayoutParams layoutParamsBasic;
    Button saveBtn;
    ArrayList<Field> fieldValuesArray;
    ProfileInfoHolder profileDetails;
    
    int intToDpi10;
    int count = 0;
    private LinkedHashMap<String, Field> fieldMapList;
    private StoreDynamicFormValues storeDynamicFormValues = new StoreDynamicFormValues();

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_edit_profile);
        initViews();
        try {
            fetchJsonDataFromAssets();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public void initViews() {
        initDpiData();
        profileDetails = new ProfileInfoHolder();
        runTimeUiLibs = new DynamicFormCreation(EditProfileActivity.this);
        editPageLayout = (LinearLayout) findViewById(R.id.linearLayoutForEdit);
        layoutParamsBasic = UtilityClass.getLayoutParams();

    }

    public void initDpiData() {
        intToDpi10 = UtilityClass.intToDpi(10, EditProfileActivity.this);
    }

    public void fetchJsonDataFromAssets() throws IOException {

        String jsonLocation = convertJsonToStringFromAssetFolder(JSON_FILENAME);
        Log.i("Json Data Are", jsonLocation);
        parseJsonFileToJavaObjects(jsonLocation);// this function parses the
                                                    // json data to java objects
    }

    public String convertJsonToStringFromAssetFolder(String fileName)
            throws IOException {
        AssetManager manager = this.getAssets();
        InputStream file = manager.open(fileName);

        byte[] data = new byte[file.available()];
        file.read(data);
        file.close();
        return new String(data);
    }

    public void parseJsonFileToJavaObjects(String json) {

        if (json != null) {
            try {
                ProfileInfoHolder.fieldHashValues = JsonParser
                        .parseUserProfileDetails(json);
            } catch (JSONException e) {
                e.printStackTrace();
            }
            createRegistrationForm(ProfileInfoHolder.fieldHashValues);

        } else {
            Toast.makeText(EditProfileActivity.this, "Null JSON",
                    Toast.LENGTH_SHORT).show();
        }
    }

    private void createRegistrationForm(
            LinkedHashMap<String, ArrayList<Field>> fieldHashValues) {

        if (fieldHashValues.size() > 0) {
            fieldMapList = new LinkedHashMap<String, Field>();
            Iterator it = fieldHashValues.entrySet().iterator();
            fieldValuesArray = new ArrayList<Field>();
            count = count + 1;
            while (it.hasNext()) {
                Map.Entry pairs = (Map.Entry) it.next();

                // create field header and its run time UI
                String fieldHeader = (String) pairs.getKey();
                addFieldHeaderTextView(fieldHeader);

                // iteratively create fields of each field header
                fieldValuesArray = (ArrayList<Field>) pairs.getValue();
                int length = fieldValuesArray.size();
                count = count + 1;

                for (int i = 0; i < length; i++) {

                    Field field = fieldValuesArray.get(i);
                    count = count + 1;
                    int lastCoreFieldId = count++;

                    View view = runTimeUiLibs.getRuntimeView(field,
                            lastCoreFieldId);

                    if (view != null) {
                        LinearLayout fieldAndPrivacyLayout = UtilityClass
                                .getLinearLayoutHorizontal(EditProfileActivity.this);

                        view.setLayoutParams(layoutParamsBasic);

                        fieldAndPrivacyLayout.addView(view);

                        editPageLayout.addView(fieldAndPrivacyLayout);
                    }
                    Log.i("Id Checkin in Edit Profile",
                            String.valueOf(view.getId()));
                    fieldMapList.put(String.valueOf(view.getId()), field);
                }
            }
            it.remove(); // avoids a ConcurrentModificationException
            addSaveButton();
        } else {
            Toast.makeText(this, "Error fetching json file", Toast.LENGTH_LONG)
                    .show();
        }
    }
    public void addFieldHeaderTextView(String fieldHeader) {
        TextView tv = new TextView(EditProfileActivity.this);
        tv.setText(fieldHeader);
        layoutParamsBasic = UtilityClass.getLayoutParamsFillParentWidth();
        tv.setLayoutParams(layoutParamsBasic);
        editPageLayout.addView(tv);
    }
    public void addSaveButton() {
        // add save button at the last of the fields
        saveBtn = UtilityClass.getButton(EditProfileActivity.this, "Save");
        layoutParamsBasic = UtilityClass.getLayoutParamsFillParentWidth();
        layoutParamsBasic.setMargins(intToDpi10, intToDpi10, intToDpi10,
                intToDpi10);
        saveBtn.setLayoutParams(layoutParamsBasic);
        saveBtn.setOnClickListener(saveButtonClickListener);
        editPageLayout.addView(saveBtn);
    }
    private OnClickListener saveButtonClickListener = new OnClickListener() {
        @Override
        public void onClick(View v) {
            // the runtime user inputs and privacy fields are accessed through
            // getRegistration form fields
            storeDynamicFormValues.getRegistrationFormFields(editPageLayout,
                    fieldMapList);
            if (storeDynamicFormValues.inCorrectFieldsList.size() > 0) {
                Toast.makeText(EditProfileActivity.this,
                        "Please Fill Form Correctly", Toast.LENGTH_SHORT)
                        .show();
                storeDynamicFormValues.inCorrectFieldsList.clear();
            } else {
                sendEditProfileDataToServer();
            }
        }
    };
    public void sendEditProfileDataToServer() {
        UtilityClass.EDIT_PROFILE_WITH_FIELD_NAME = storeDynamicFormValues.displayFieldsMap;
        finish();
    }
}

Here, we have also create a UtilityClass that stores the codes which can be reused again and again throughout our project. The content of Utility Class is as folllow:

public class UtilityClass {
    public static LinkedHashMap<String, String> EDIT_PROFILE_RUNTIME_INPUT = new LinkedHashMap<String, String>();
    public static LinkedHashMap<String, String> EDIT_PROFILE_WITH_FIELD_NAME = new LinkedHashMap<String, String>();
    public static Button getButton(Context context, String text) {
        Button button = new Button(context);
        LinearLayout.LayoutParams layoutParamsBasic = getLayoutParams();
        layoutParamsBasic.gravity = Gravity.RIGHT;
        layoutParamsBasic.width = intToDpi(55, context);
        layoutParamsBasic.height = intToDpi(40, context);
        button.setLayoutParams(layoutParamsBasic);
        button.setText(text);
        button.setTextSize(12);
        button.setGravity(Gravity.CENTER);
        button.setShadowLayer(1, 1, 1, Color.WHITE);
        int intToDpi2 = intToDpi(2, context);
        button.setPadding(intToDpi2, intToDpi2, intToDpi2, intToDpi2);
        return button;
    }    
    public static int intToDpi(int integer, Context context) {
        return (int) (integer * context.getResources().getDisplayMetrics().density);
    }
    public static LinearLayout.LayoutParams getLayoutParams() {
        return new LinearLayout.LayoutParams(
                ViewGroup.LayoutParams.WRAP_CONTENT,
                ViewGroup.LayoutParams.WRAP_CONTENT);
    }
    public static LinearLayout.LayoutParams getLayoutParamsFillParentWidth() {
        return new LinearLayout.LayoutParams(
                ViewGroup.LayoutParams.MATCH_PARENT,
                ViewGroup.LayoutParams.WRAP_CONTENT);
    }    
    public static LinearLayout getLinearLayoutHorizontal(Context context) {
        LinearLayout linearLayout = new LinearLayout(context);
        int intToDpi5 = intToDpi(5, context);
        linearLayout.setPadding(intToDpi5, intToDpi5, intToDpi5, intToDpi5);
        linearLayout.setOrientation(LinearLayout.HORIZONTAL);
        LinearLayout.LayoutParams layoutParamsBasic = getLayoutParams();
        linearLayout.setLayoutParams(layoutParamsBasic);
        return linearLayout;
    }
}

The project is stored in GitHub. Download it and check it out.
Hope you enjoyed reading my blog. See you next time through my blog. Until then, Happy Coding 🙂

Custom ListView in Android Using Custom ArrayAdapter


ListView:
ListView is a view group. It displays a list of scrollable items. The list items may be stored as an array or database query. These list items are converted into a view that is placed into the list by Adapters. Adapter automatically insert these list items into the list.

Adapter:
The Adapter behaves as a middle-man between the data source and the AdapterView layout. Adapter retrieves the data and converts each entry into a view that can be addad into the AdapterView layout.

Building Layouts With an Adapter:
Layouts are build with an Adapter to dynamically insert views using an Adapter. ListView are used frequently to display dynamic data extracted from the web or database. In such situations, the layout content for the layout of the list must be dynamic. We can use a layout that subclasses AdapterView to populate the layout with views at run time. A subclass of the AdapterView class uses an Adpater to bind data to its layout.

Filling an adapter view with data:
We can populate an AdapterView such as ListView by binding the AdapterView instance to an Adapter. ArrayAdapter, CursorAdapter are the subclasses of Adapter. They are useful for retrieving different kinds of data and building views for and AdapterView.

To clarify the above discussion, we will create a listview that list all the inbox message stored in the device. The listview uses a custom adapter. Custom adapter is used inorder to give the listview a custom layou. Here are the seried of steps that we need to follow:

1. Create the main layout for the project.
This main layout consists of the listview as shown below:

<?xml version="1.0" encoding="UTF-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent" >
    <TextView
        android:id="@+id/list_textview"
        android:layout_width="0dp"
        android:layout_height="match_parent"
        android:gravity="center"
        android:text="Inbox Messages"
        android:textSize="14sp" />
    <ListView
        android:id="@+id/messageList"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:layout_marginLeft="4dp"
        android:layout_marginRight="4dp"
        android:layout_marginTop="5dp"
        android:cacheColorHint="@null"
        android:divider="@null"
        android:dividerHeight="6dp"
        android:listSelector="@android:color/transparent" />
</RelativeLayout>

2. Create the layout for the list items.
Layout for the list items are only required if we wish to use a custom adapter, else, we can use the default layout provided by the android. In our listview, we intend to show the sms senders number and the message content. So, we use two text box in our layout.

<?xml version="1.0" encoding="UTF-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/layout_appwid_detail_msg"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="horizontal" >
    <TextView
        android:id="@+id/txt_msgTO"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="8dp"
        android:lineSpacingExtra="3dp"
        android:paddingLeft="5dp"
        android:paddingRight="5dp"
        android:shadowColor="#FFFFFF"
        android:shadowDy="1"
        android:shadowRadius="0.01"
        android:singleLine="true"
        android:textColor="#000000"
        android:textSize="12sp"
        android:textStyle="bold" />
    <TextView
        android:id="@+id/txt_messageContent"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="8dp"
        android:lineSpacingExtra="3dp"
        android:paddingLeft="5dp"
        android:paddingRight="5dp"
        android:shadowColor="#FFFFFF"
        android:shadowDy="1"
        android:shadowRadius="0.01"
        android:singleLine="true"
        android:textColor="#000000"
        android:textSize="12sp"
        android:textStyle="bold" />
</LinearLayout>

3. Create a holder class that holds the senders number and the message content.
We name this class as Message. Its content is given below:


public class Message {
    public String messageNumber, messageContent;
}

4. In the main activity, we will do the following things:
a. We will intialize the array and the listview .
b. We will fetch the inbox message from the device. Since, the number of message may count to hundred, we are using threads so that application thread is not overloaded. Thread helps to fetch the inbox message efficiently. The thread is attached to the activity through “weak reference”. Weak references are useful for mappings that should have their entries removed automatically once they are not referenced any more (from outside).
c. We store the inbox message into the array.
d. We initialize our custom adapter and pass the array into that adapter.
e. Then, we set our ListView with that adapter.
The demo class is as follows:


public class MainActivity extends Activity {
    private static final int TYPE_INCOMING_MESSAGE = 1;
    private ListView messageList;
    private MessageListAdapter messageListAdapter;
    private ArrayList<Message> recordsStored;
    private ArrayList<Message> listInboxMessages;
    private ProgressDialog progressDialogInbox;
    private CustomHandler customHandler;
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        initViews();
    }
    @Override
    public void onResume() {
        super.onResume();
        populateMessageList();
    }
    private void initViews() {
        customHandler = new CustomHandler(this);
        progressDialogInbox = new ProgressDialog(this);
        recordsStored = new ArrayList<Message>();
        messageList = (ListView) findViewById(R.id.messageList);
        populateMessageList();
    }
    public void populateMessageList() {
        fetchInboxMessages();
        messageListAdapter = new MessageListAdapter(this,
                R.layout.message_list_item, recordsStored);
        messageList.setAdapter(messageListAdapter);
    }
    private void showProgressDialog(String message) {
        progressDialogInbox.setMessage(message);
        progressDialogInbox.setIndeterminate(true);
        progressDialogInbox.setCancelable(true);
        progressDialogInbox.show();
    }
    private void fetchInboxMessages() {
        if (listInboxMessages == null) {
            showProgressDialog("Fetching Inbox Messages...");
            startThread();
        } else {
            // messageType = TYPE_INCOMING_MESSAGE;
            recordsStored = listInboxMessages;
            messageListAdapter.setArrayList(recordsStored);
        }
    }
    public class FetchMessageThread extends Thread {
       public int tag = -1;
        public FetchMessageThread(int tag) {
            this.tag = tag;
        }
        @Override
        public void run() {
            recordsStored = fetchInboxSms(TYPE_INCOMING_MESSAGE);
            listInboxMessages = recordsStored;
            customHandler.sendEmptyMessage(0);
        }
    }
    public ArrayList<Message> fetchInboxSms(int type) {
        ArrayList<Message> smsInbox = new ArrayList<Message>();
        Uri uriSms = Uri.parse("content://sms");
            Cursor cursor = this.getContentResolver()
                .query(uriSms,
                        new String[] { "_id", "address", "date", "body",
                                "type", "read" }, "type=" + type, null,
                        "date" + " COLLATE LOCALIZED ASC");
        if (cursor != null) {
            cursor.moveToLast();
            if (cursor.getCount() > 0) {
                do {
                    Message message = new Message();
                    message.messageNumber = cursor.getString(cursor
                            .getColumnIndex("address"));
                    message.messageContent = cursor.getString(cursor
                            .getColumnIndex("body"));
                    smsInbox.add(message);
                } while (cursor.moveToPrevious());
            }
        }  return smsInbox;
    }
    private FetchMessageThread fetchMessageThread;
    private int currentCount = 0;
    public synchronized void startThread() {
        if (fetchMessageThread == null) {
            fetchMessageThread = new FetchMessageThread(currentCount);
            fetchMessageThread.start();
        }
    }
    public synchronized void stopThread() {
        if (fetchMessageThread != null) {
            Log.i("Cancel thread", "stop thread");
            FetchMessageThread moribund = fetchMessageThread;
            currentCount = fetchMessageThread.tag == 0 ? 1 : 0;
            fetchMessageThread = null;
            moribund.interrupt();
        }
    }

    static class CustomHandler extends Handler {
        private final WeakReference<MainActivity> activityHolder;
        CustomHandler(MainActivity inboxListActivity) {
            activityHolder = new WeakReference<MainActivity>(inboxListActivity);
        }
        @Override
        public void handleMessage(android.os.Message msg) {
            MainActivity inboxListActivity = activityHolder.get();
            if (inboxListActivity.fetchMessageThread != null
                    && inboxListActivity.currentCount == inboxListActivity.fetchMessageThread.tag) {
                Log.i("received result", "received result");
                inboxListActivity.fetchMessageThread = null;
               inboxListActivity.messageListAdapter.setArrayList(inboxListActivity.recordsStored);
                inboxListActivity.progressDialogInbox.dismiss();
            }
        }
    }
    private OnCancelListener dialogCancelListener = new OnCancelListener() {

        @Override
        public void onCancel(DialogInterface dialog) {
            stopThread();
        }
    };
}

5. Finally we create our custom adapter
In this class we implement following methods of Android.Widget.Adapter super class, in order to get things working:
getCount() – returns the total number of elements in your data array
getIterm() – returns the data item associated with the specified position in the data set. Not implemented at this point.
getItemId() – returns the row id associated with the specified position in the list. Not implemented at this point.
getView() – returns the row that has to be drawn. This is the method we’re interested in the most. It will be called every time the ListView draws a new row.
Here, we can control what gets drawn in a particular row, by selecting a layout and setting data into it.A list has a transparent background in normal conditions which makes you see the image that you set as the layout background, but when the List is scrolled, it doesn’t remain transparent any more.If you want you background image to be visible while scrolling too, set cacheColorHint of your ListView.
The demo class is as follows:


public class MessageListAdapter extends ArrayAdapter<Message> {
    private Context ctx;
    public ArrayList<Message> messageListArray;
    public static boolean isDialogOpen = true;
    public MessageListAdapter(Context context, int textViewResourceId, ArrayList<Message> messageListArray) {
        super(context, textViewResourceId);
        this.messageListArray = messageListArray;
        this.ctx = context;
    }
    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        Holder holder;
       LinearLayout layout;
        if (convertView == null) {
            holder = new Holder();
           layout = (LinearLayout) View.inflate(ctx,R.layout.message_list_item, null);
            holder.messageTo = (TextView) convertView1.findViewById(R.id.txt_msgTO);
            holder.messageContent = (TextView) convertView1.findViewById(R.id.txt_messageContent);
            layout.setTag(holder);
        } else {
             layout = (LinearLayout) convertView;
            holder = (Holder) layout.getTag();
        }
        Message message = getItem(position);
        holder.messageTo.setText(message.messageNumber +" : ");
        holder.messageContent.setText(message.messageContent);
        return layout;
    }
    @Override
    public int getCount() {
        return messageListArray.size();
    }
    @Override
    public Message getItem(int position) {
        return messageListArray.get(position);
    }
    public void setArrayList(ArrayList<Message> messageList) {
        this.messageListArray = messageList;
        notifyDataSetChanged();
    }
    private class Holder {
        public TextView messageTo, messageContent;
    }
}

The entire project is stored in the github.
Thanks for reading my blog.Hope you enjoyed and Happy coding 😀

References:
http://developer.android.com/reference/android/widget/Adapter.html
http://www.vogella.com/articles/AndroidListView/article.html
http://developer.android.com/guide/topics/ui/layout/listview.html
http://developer.android.com/guide/topics/ui/declaring-layout.html#AdapterViews