Saturday, 28 March 2015

Creating a Dialog fragment in Android

Background

In this post we are going to see how to create a simple custom dialog fragment in Android.


So we will create a dialog box that looks like above. It is a simple dialog box that asks user if he wants to go to google search or not. If user presses "Not Now" then we simply close the dialog box and if user presses "Yes" then we open browser with url as "google.com" using implicit intent.

To The Code...

Code for it is as follows  -

/**
 * 
 * @author athakur
 *
 */
public class VisitGoogleDialog extends DialogFragment {
    
    private static final String GOOGLE_URL = "http://www.google.com/";
    
    Context appContext;
    
    public VisitGoogleDialog(Context context) {
        this.appContext = context;
    }

    @Override
    public Dialog onCreateDialog(Bundle savedInstanceState) {
        // Use the Builder class for convenient dialog construction
        AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
        TextView dialogMessage = new TextView(appContext);
        dialogMessage.setText(R.string.google_dialog_info);
        dialogMessage.setGravity(Gravity.CENTER_HORIZONTAL);
        builder.setView(dialogMessage)
               .setPositiveButton(R.string.pos_dialog_text, new DialogInterface.OnClickListener() {
                   public void onClick(DialogInterface dialog, int id) {
                       Intent viewGoogleIntent = new Intent(Intent.ACTION_VIEW,Uri.parse(GOOGLE_URL));
                       startActivity(viewGoogleIntent );
                   }
               })
               .setNegativeButton(R.string.neg_dialog_text, new DialogInterface.OnClickListener() {
                   public void onClick(DialogInterface dialog, int id) {
                       dialog.dismiss();
                   }
               });
        // Create the AlertDialog object and return it
        return builder.create();
    }

}

Creating this dialog from any activity is also quite simple. Following is the code to bring this dialog up by selecting "more info" option from menu.

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        int id = item.getItemId();
        if (id == R.id.more_info) {
            DialogFragment googleDialogFragment = new VisitGoogleDialog(this);
            googleDialogFragment.show(getSupportFragmentManager(), "GoogleDialog");
            return true;
        }
        return super.onOptionsItemSelected(item);
    }

For me it finally looked like - 

Adding custom layout to the Dialog

Inside  onCreateDialog() method you can add your custom layout to the dialog - 

        AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
        final View customView =  getLayoutInflater(savedInstanceState).inflate(R.layout.modern_art_ui_layout, null);
        builder.setView(customView);

And in onClickListener you can easily get Views in the layout and do any processing if needed - 

               builder .setPositiveButton(R.string.pos_dialog_text, new DialogInterface.OnClickListener()  {
                   public void onClick(DialogInterface dialog, int id) {
                       View leftImage= customView.findViewById(R.id.leftImage);
                       // process leftImage
                   }
               })


Note : I am not providing resource  file data as I think it should be trivial to understand. For eg value for String resource "google_dialog_info" should be set to "Continue to google search ?" for this demo. Same goes to other resources like layout files.


Related Links

Thursday, 26 March 2015

Intercepting Android network calls using Fiddler Web Proxy

Background

Some time it is important to intercept and inspect network calls that are being made from your android device. Specially of you have made an android app want to debug the outgoing network calls. In a browser it is very simple , you simply right click and then inspect element. Network tab will give you the network calls made with the details. Each browser has their own native inspector. I generally use firebug in my firefox. But as I was saying this is not possible in android devices. So we have to divert all device network calls via a proxy and inspect our calls there.  For this demo I am going to use Fiddler as a proxy.


Getting started

You can download and install Fiddler web proxy from here.

After installing it go ahead and start it up. You should see following screen - 


Note : You can use shortcut Ctrl + X shortcut to clear the captured network data.

Lets set up some settings so that we can use this as a proxy for out android device.

Go to Tools -> Fiddler Options . Navigate to Connections tab and select "Allow Remote computers to connect" option.




After you select this you will see an alert stating that remote connection network traffic will bounce via your computer. Select OK. 



Note1 : You will have to restart Fiddler for changes to take effect.

Note2 : After selecting Accept Remote connections restart fiddler. In case windows firewall prompts you to allow access, check all three checkboxes and click the Allow Access button

Just to make sure your Fiddler web proxy is up and running you can type "localhost:8888" in your browser and Fiddler echo service should respond back 200 status.




If you don not want to inspect and decrypt https connection skip to Configuring Client section.

Decrypting HTTPS Connections


If you want to intercept and decrypt https (SSL) traffic as well. You can again go in Tools -> Fiddler Options. This time navigate to "HTTPS" tab and select "Decrypt HTTS traffic". You will get a prompt to trust a root CA certificate fiddle provides. For now select "No".  Will explain why in some time.


Even if you press yes next window should warn you again.


 Take the hint. It's not safe :) Atleast there is no need for us. We will see why in below section.

You need to add this certificate in list of trusted certificates in your Android device. I will show that in the end of configuring Client section.

What is this CA ROOT certificate that we have to trust and how is it related to HTTPS traffic decryption? 

This section is not really required for testing network traffic. I am just adding for people who are interested in understanding the concept. Others can skip this section.

Whenever https is enabled on a server container like apache tomcat you need to do some SSL configurations. Important thing to provide to the server is the keystore which stores the certificate that the private keys. When any client connects to this server, server provides a copy of the SSL certificate to the client. Now client (Android device in our case) has something called a trust store that which has certificates that the client can trust. If the client does not trust the certificate server has provided then connection errors out.  There certificate are typically generated and provided by know certificate providers like Digi Cert, Go Daddy etc. For more information on Certificate providers read the Wiki - 
What happens in Fiddler is as follows - Client makes an https call. Fiddler intercepts it and forwards it to the actual server. When it gets a response Fiddler uses it's own certificate and forwards the response to the client. Why? Think if Fiddler forwarded the request directly yo the client. Whatever client sends in subsequent https   connection will be signed with the public key that can only be decrypted by the server that has it's private key which in this case will be the mail site server where request is sent. Since Fiddle does not have that Fiddler sends it's own certificate to the client. Client signs subsequent data with that certificates public key and since Fiddle has this certificate (with it's private key) it can decrypt it.

Wow that a security hazard? Well it a well known attack actually. Also called man-in-the-middle (Read Wiki on it) attack. Do not worry about it. Your client should trust that certificate in order for it to work.

So why we did not accept the certificate to trust? Because this on the machine which hosts the Fiddler and we want to intercept the calls from our Android device. So Android device is the client and not the machine that hosts Fiddler. You can accept the certificate as trusted one in your android device. As a test if you go on any https site (on your machine) now you will get the unstrusted certificate warning. Do not accept and proceed :)



After this is your Fiddler is all set to Intercept your calls. All you have to do is configure the client (your android device) to use this as the proxy.
 

Configuring the client (Android device)

Long tap on your Wifi (Note your machine where Fiddler is running and the Android device should be on the same wifi network).  Click on "Show Advance options".

Select Manual as the proxy type. Put in proxy hostname as your machines IP address. Change the port to 8888 which Fiddler runs on by default.

Let every other setting be as it is. 



You can then see captured data in the Fiddler. I tried a simple http site - http://moma.org


Lets try an https site now. https://google.com



Opps what happened? Well we have not added fiddlers certificate to the list of trusted certificate list on device yet. Lets do that now. Again this is only for decrypting https data. Skip below for http connections -

In your browser of android device go to following URL -
You will get the prompt to install certificate. Provide some certificate name. Choose "Wifi" in the credentials Use section and click Ok. Https connections should work now.


  
Note : Fiddler should be running for this.

After you install the certificate. Typically you should see a toast that says certificate installed -

Don't mind my delay in taking above screenshot :) The toast is almost faded. Anyway after this you should see a notification on your device saying your network activity is monitored -


and when you click on "Check trusted credential" you can see the user installed cert on your system -



After you are done testing you can remote the cert from here. tap on the cert entry and you should see new activity with cert info. Scroll to the bottom and click Remove to remove this cert from your system. Do not forget to remove manual proxy and all goes back to normal :)


Resolving  “enter the password for credential storage” issue?


Note if you have trouble installing certificate as I was (i was continuously getting following prompt).



then remove all security you have for lock screen - I had pattern. Now try installing the certificate again. You will get prompt to add lock security - password or pin.



Go ahead and set it up. Then certificate should get installed.

Important Note : This is a good tool to analyze your devices network data but can be misused if you do not know what are you up to. So don't try this on your personal device with sensitive data . Probably try on emulator first or a test device. I don't take any responsibility for any testing that you do using information in this post!

Related Links

Saturday, 21 March 2015

Recording Android Device screen with ADB

Background

There are various app / recording software's that you get in market to record your Android device videos. But there is an inbuilt way too similar to the the backup method we saw in one of the previous posts - 
From Android 4.4 Kitkat (API 19) there is an inbuilt command to take screencast  of your device. You can read the same in Kitkat official documentation -

"With the screen recording utility, you can capture video of your device screen contents and store the video as an MP4 file on the device. You can record at any device-supported resolution and bitrate you want, and the output retains the aspect ratio of the display. By default, the utility selects a resolution equal or close to the device's display resolution in the current orientation. When you are done recording, you can share the video directly from your device or pull the MP4 file to your host computer for post-production."

So now you can record your device , save it in mp4 format and share it easily. For this you will need Android SDK (ADB ) .

Note : You can also do this directly from your device but your device should be rooted for that.

Prerequisites :  Android device running Android Kitkat (API 19) or higher, USB cable for debugging and machine with adb setup.


 Getting Started with Recording


Connect your device to your machine having ADB setup.  Ensure debugging is enabled. Next open command prompt and type -

C:\Users\athakur>adb devices
List of devices attached
ZX1B32P5VL      device


Make sure you see your device listed as shown above. If it does not show up you may want to troubleshoot it first -

After this you can connect to your device using adb shell and execute the  screenrecord command to start the video capture. Command to be executed is -
  • screenrecord filePathToMP4File
For example I can do something like following

C:\Users\athakur>adb shell
shell@condor_umtsds:/ $ screenrecord /sdcard/test.mp4
shell@condor_umtsds:/ $ exit
C:\Users\athakur>adb pull /sdcard/test.mp4 .
3197 KB/s (2076164 bytes in 0.634s)


Note : Notice after video has been recorded I have pulled it back to my Laptop using adb pull  command.

Yes it's that easy :)

You can read more about the command and it's option by executing following command -
  • adb shell screenrecord --help

Attention! - Not available in Emulators

This does not work on Android Emulator's. This is a bug and does not look like is going to be fixed in near future. If you try above approach on Emulator command will fail with following Error -

ERROR: unable to create encoder input surface (err=-38)
WARNING: failed at 320x480, retrying at 720x1280
ERROR: unable to create encoder input surface (err=-38)

This bug is tracked in 
if you wish to read the discussion around it.

I created following screencast today using this method as it was required by Android course that i am doing from coursera -




Related Links

Saturday, 14 March 2015

Understanding Explicit and Implicit intents in Android

Background

Intents form the very basics of Android operating Systems. Primarily they are used to start new activities and transfer data between activities. There are two broad categories in which Intents can be classified - 
  1. Explicit Intents
  2. Implicit Intents
In Explicit intents we specify which exact Activity need to be started by giving the class of the Activity to be started. Android OS will start that exact Activity.

Implicit intents on the other hand behave differently. In implicit intents you provide various parameters like action, category, data etc. Activity that supports these parameters will be started by Android OS. For eg. lets say we provide "view" action which is the action to view a web page in browser. Android OS will see a browser (lets say chrome) supports this action and will start it. But wait a minute - what happens when multiple activities support these parameters. Like when we have multiple browsers installed. In this case Android OS will simply give option to the user to choose from list of Activities that support those combination of parameters.




We will see these is detail a little later. But I hope you got the overview of the basic difference between explicit and implicit intents.

Explicit Intents

As mentioned earlier explicit intents are used to start a specific Activity directly. Lets create two activities 
  1. PrimaryActivity and
  2. SecondaryActivity
Primary Activity will have a button which on press will start Secondary Activity using an explicit intent.

I am going to skip contents of other files like manifest file or resources file as aim of this post is to demonstrate types of intents. If you wish to learn resources and other folder that constitutes an Android project see one of my previous posts -

PrimaryActivity.java :

 package com.example.explicitintentdemo;

import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;

public class PrimaryActivity extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_primary);
    
        Button explicitIntentButton = (Button) findViewById(R.id.explicit_intent_button);
        explicitIntentButton.setOnClickListener(new OnClickListener() {

            @Override
            public void onClick(View v) {
                Intent explicitIntent = new Intent(PrimaryActivity.this, SecondaryActivity.class);

                // explicit intent
                startActivity(explicitIntent);
            }
        });
        
    }
}


SecondaryActivity.java :

package com.example.explicitintentdemo;

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

public class SecondaryActivity extends Activity {

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



You can test it on your Android device or emulator. Pressing on "Start Secondary Activity" loads the secondary Activity.









So when the button is pressed we create a intent with Application context and class of Activity that needs to be started (Explicit intent). Then we simply start the activity using startActivity() method.



Now lets see how implicit intents are used.

Implicit Intents

As mentioned earlier implicit intents are kind of generic intents that are used to fire up Activities supporting some action. For example activities that can understand web page URLs and render them (typically browsers). As we know there can be many such activities (many browsers installed or same android OS for instance). In this case Android OS lets user decide which activity should be started. 

Lets see this in code. We will try to open simple google URL - "http://www.google.com" using proper intent.

Lets refactor our Primary Activity to use implicit intent and render the google URL.

PrimaryActivity.java :

package com.example.explicitintentdemo;

import android.app.Activity;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;

public class PrimaryActivity extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_primary);
    
        Button explicitIntentButton = (Button) findViewById(R.id.explicit_intent_button);
        explicitIntentButton.setOnClickListener(new OnClickListener() {

            @Override
            public void onClick(View v) {
                Intent baseIntent =  new Intent(Intent.ACTION_VIEW, Uri.parse("http://www.google.com"));
                Intent chooserIntent = Intent.createChooser(baseIntent, "Choose your browser");
                startActivity(chooserIntent);
            }
        });
        
    }
}

Here the logic in onClick() method is slightly changed. Instead of using an Intent with Application Context and the class of Activity to be started we are providing it with an Action (Intent.ACTION_VIEW) and a data which is essentially an URI.

Note : Think of chooserIntent as a wrapper Intent. When the dialog is shown to choose from multiple activities that support the action (category, data etc) the title text  can be supplied using such Chooser Intents [See the second argument of Intent.createChooser() method call.]. 

Lets see this in action.

UI for PrimaryActivity is the same. Click on the "StartSecondaryActivity" to launch implicit intent.




Notice the title of the chooser dialog ? It is the same String we supplied in Chooser intent - 
  • Intent chooserIntent = Intent.createChooser(baseIntent, "Choose your browser");
I hope you got the difference between the two - implicit and explicit intents. If you are wondering what is the action (Intent.ACTION_VIEW) and the data that we supplied are and how intents are related see following documentation links.

After going through the documentation if you are wondering how intent- filter would look for the Activity that supports viewing URL like the one in above example then it is as follows -

        <activity
            android:name=".MyBrowserActivity"
            android:label="@string/my_app_name" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
            <intent-filter>
                <action android:name="android.intent.action.VIEW" />
                <category android:name="android.intent.category.DEFAULT" />
                <data android:scheme="http"/> 
              </intent-filter>
        </activity>


Infact the MyBrowser Activity you saw in chooser list is one of the browser like demo app that I had created.
 So to give summary about intent filters - they define what kind of operation the Activity is capable of handling and is specified in the Applications manifest file inside the respective activity tag. If you would recollect starting activity of each application have intent filters like -

            <intent-filter>

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

            </intent-filter>


These intent filters tell Android OS that this activity is a launcher activity.

Note : I have used startActivity() everywhere to start new Activity but if you want your new started activity to return some data/result to the starter activity you can use startActivityForResult() method. For more details on that you can refer -

Related Links

Tuesday, 10 March 2015

Escaping special characters of XML in Java

Background

In previous post I had shown how to parse XML from String - 
 So lets parse a simple String that contains Google Play App name. Eg - 
  • <AppName>Temple Run</AppName>
Code is as follows -

    public static void main(String args[]) throws SAXException, IOException, ParserConfigurationException {
            String xmlString = "<AppName>Temple Run</AppName>";
            DocumentBuilder db = DocumentBuilderFactory.newInstance().newDocumentBuilder();
            Document doc = db.parse(new InputSource(new StringReader(xmlString)));
            System.out.println(doc.getFirstChild().getTextContent());
    }

and output is as expected - Temple Run

Now lets change out input xml string/ app name as follows -
  • <AppName>Angels & Demons</AppName>
 Run the code again with above xml String input. You will get following Exception -

[Fatal Error] :1:18: The entity name must immediately follow the '&' in the entity reference.
Exception in thread "main" org.xml.sax.SAXParseException; lineNumber: 1; columnNumber: 18; The entity name must immediately follow the '&' in the entity reference.
    at com.sun.org.apache.xerces.internal.parsers.DOMParser.parse(Unknown Source)
    at com.sun.org.apache.xerces.internal.jaxp.DocumentBuilderImpl.parse(Unknown Source)
    at StringXMLParser.main(StringXMLParser.java:20)


Reason being '&' is a special character and you need to escape it in a String before parsing the String as XML. Same goes for HTML as well. Special characters like '&' should be escaped. '&'
 in it's escaped form looks like '&amp;'. So the input should be something like -
  • <AppName>Angels '&amp; Demons</AppName>

Special Characters in XML

Special characters in XML are  - 
  1. & - &amp;
  2. < - &lt;
  3. > - &gt;
  4. " - &quot;
  5. ' - &apos;
So when you are creating an XML from some input that has these special characters then you need to take care of it. Obviously you wont expect your clients to enter the app name as ''Angels &amp; Demons".



Reason for escaping these so called special characters is that these have special meaning in XML and when used in data will led to parsing errors as the one show in the code snippet above. For example & character is used to import other XML entities.

Escaping Input for XML in Java

You can very well write your own piece of code to parse these special characters from the input and replace them with their escaped version. For this tutorial I am going to use Apache commons lang’s StringEscapeUtils class which provide escaping for several  languages like XML, SQL and HTML.

As usual I am using Ivy as my dependency manager and Eclipse as my IDE. To install and configure Apache Ivy refer to the link provided in "Related Links" section at the bottom.

My ivy file looks like following - 

<ivy-module version="2.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:noNamespaceSchemaLocation="http://ant.apache.org/ivy/schemas/ivy.xsd">
    <info
        organisation="OpenSourceForGeeks"
        module="XMLEscaper"
        status="integration">
    </info>
    
    <dependencies>
        <dependency org="org.apache.commons" name="commons-lang3" rev="3.3.2"/>        
    </dependencies>
    
</ivy-module>

now lets get to the code -

import java.io.IOException;
import java.io.StringReader;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;

import org.apache.commons.lang3.StringEscapeUtils;
import org.w3c.dom.Document;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;


public class StringXMLParser {
    
    public static void main(String args[]) throws SAXException, IOException, ParserConfigurationException {
           
            String appNameInput = "Angels & Demons";
            System.out.println("App Name Before Escaping : " + appNameInput);
            String escapedInput = StringEscapeUtils.escapeXml(appNameInput);
            System.out.println("App Name After Escaping : " + escapedInput);
            String xmlString = "<AppName>" + escapedInput + "</AppName>";
            DocumentBuilder db = DocumentBuilderFactory.newInstance().newDocumentBuilder();
            Document doc = db.parse(new InputSource(new StringReader(xmlString)));
            System.out.println(doc.getFirstChild().getTextContent());
    }
}
 

Compile and run above code. You should get the following output - 

App Name Before Escaping : Angels & Demons
App Name After Escaping : Angels &amp; Demons
Angels & Demons

No Exception. I have just shown this demo for '&' special character but you can do the same for all special characters mentioned in  "Special Characters in XML" section above.



Note: Only the characters "<" and "&" are strictly illegal in XML. The greater than character is legal, but it is a good habit to replace it.


Related Links

t> UA-39527780-1 back to top