Custom Hyperlink Control for Android

I developed a custom control to mimic the Hyperlink behavior as Android doesn’t have a built-in/native Hyperlink Control.

Implementation

public class CustomHyperlink extends TextView {

    private int mDefaultColor = Color.BLACK;
    private int mPressedColor = Color.GREEN;

    //region #Constructors

    public CustomHyperlink(Context context) {
        super(context);

        this.setOnTouchListener(new OnTouchListener() {
            @Override
            public boolean onTouch(View v, MotionEvent event) {
                if (event.getAction() == MotionEvent.ACTION_DOWN || event.getAction() == MotionEvent.ACTION_MOVE)
                    setTextColor(mPressedColor);
                if (event.getAction() == MotionEvent.ACTION_UP || event.getAction() == MotionEvent.ACTION_OUTSIDE
                        || event.getAction() == MotionEvent.ACTION_CANCEL) {
                    try {
                        Thread.sleep(500);
                    } catch (InterruptedException e) {
                        Thread.currentThread().interrupt();
                    }
                    setTextColor(mDefaultColor);
                }
                return false;
            }
        });
    }

    public CustomHyperlink(Context context, AttributeSet attrs) {
        super(context, attrs);
        setCustomAttrValues(context, attrs);

        this.setOnTouchListener(new OnTouchListener() {
            @Override
            public boolean onTouch(View v, MotionEvent event) {
                if (event.getAction() == MotionEvent.ACTION_DOWN || event.getAction() == MotionEvent.ACTION_MOVE)
                    setTextColor(mPressedColor);
                if (event.getAction() == MotionEvent.ACTION_UP || event.getAction() == MotionEvent.ACTION_OUTSIDE
                        || event.getAction() == MotionEvent.ACTION_CANCEL) {
                    try {
                        Thread.sleep(500);
                    } catch (InterruptedException e) {
                        Thread.currentThread().interrupt();
                    }
                    setTextColor(mDefaultColor);
                }
                return false;
            }
        });
    }

    public CustomHyperlink(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        setCustomAttrValues(context, attrs);

        this.setOnTouchListener(new OnTouchListener() {
            @Override
            public boolean onTouch(View v, MotionEvent event) {
                if (event.getAction() == MotionEvent.ACTION_DOWN || event.getAction() == MotionEvent.ACTION_MOVE)
                    setTextColor(mPressedColor);
                if (event.getAction() == MotionEvent.ACTION_UP || event.getAction() == MotionEvent.ACTION_OUTSIDE
                        || event.getAction() == MotionEvent.ACTION_CANCEL) {
                    try {
                        Thread.sleep(500);
                    } catch (InterruptedException e) {
                        Thread.currentThread().interrupt();
                    }
                    setTextColor(mDefaultColor);
                }
                return false;
            }
        });
    }

    //endregion

    //region #Getters, Setters

    public int getDefaultColor() {
        return mDefaultColor;
    }

    public void setDefaultColor(int defaultColor) {
        this.mDefaultColor = defaultColor;
    }

    public int getPressedColor() {
        return mPressedColor;
    }

    public void setPressedColor(int pressedColor) {
        this.mPressedColor = pressedColor;
    }

    //endregion

    //region #Methods

    public void setLinkText(String value) {
        if (MetrixStringHelper.isNullOrEmpty(value)) return;

        SpannableString content = new SpannableString(value);
        content.setSpan(new UnderlineSpan(), 0, content.length(), 0);
        setText(content);

        setTextColor(mDefaultColor);j
    }

    public void setLinkText(String value, int start, int end) throws Exception {
        if (MetrixStringHelper.isNullOrEmpty(value)) return;

        if (end > value.length())
            throw new Exception(AndroidResourceHelper.getMessage("EndValIsGreaterThan"));

        SpannableString content = new SpannableString(value);
        content.setSpan(new UnderlineSpan(), start, end, 0);
        setText(content);

        setTextColor(mDefaultColor);
    }

    public String getLinkText() {
        return this.getText().toString();
    }

    private void setCustomAttrValues(Context context, AttributeSet attrs) {
        TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.CustomHyperlinkAttr);
		if (typedArray != null) {
			//region #setLinkText
			String s = typedArray.getString(R.styleable.CustomHyperlinkAttr_linkText);
			if (!MetrixStringHelper.isNullOrEmpty(s))
				setLinkText(s);
			//endregion
		}
    }

    //endregion
}

I hope you have some basic understanding about the android custom attributes. You can declare your own attributes in a xml file such as attrs.xml and it can be placed inside res\values\ folder.

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <declare-styleable name="CustomHyperlinkAttr">
        <attr name="linkText" format="string"></attr>
    </declare-styleable>
</resources>

Usage (Inside a XML layout)


<com.architecture.utilities.MetrixHyperlink
ndroid:id="@+id/web_url"
attr:linkText="www.abc.com"
android:gravity="center_horizontal"/>

Don’t forget to add the following line into the top most layout.
xmlns:attr=”http://schemas.android.com/apk/res-auto&#8221;

Ex:


<?xml version="1.0" encoding="utf-8"?> <LinearLayout style="@style/LinearBase.Normal.Vertical"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns="http://schemas.android.com/tools"
xmlns:attr="http://schemas.android.com/apk/res-auto">

Usage (Inside a Class)


CustomHyperlink webUrlHyperLink = (CustomHyperlink) findViewById(R.id.web_url);
webUrlHyperLink.setLinkText("www.xyz.com");

Happy coding…. 🙂

Forcing newly entered characters to be in UPPERCASE in a TextBox

For Windows Store Apps(Windows 8.1 & Windows Phone 8.1)


///
<summary>
/// Identify whether the pressed key is a Letter(Ex: a, B..)
/// </summary>

/// <param name="sender"></param>
/// <param name="e"></param>
static void textBox_KeyDown(object sender, Windows.UI.Xaml.Input.KeyRoutedEventArgs e)
{
	int keyValue = (int)e.Key;
	if (keyValue >= 0x41 && keyValue <= 0x5A)
		isLetter = true;
}

///
<summary>
/// Identify whether a text PASTE is happened.
/// </summary>

/// <param name="sender"></param>
/// <param name="e"></param>
static void textBox_Paste(object sender, TextControlPasteEventArgs e)
{
	var textBox = sender as TextBox;
	startingLocation = textBox.SelectionStart;
	pasteOccurred = true;
}

///
<summary>
/// Change the newly entered character's case to upper case / a set of characters (Ex: text Paste)
/// </summary>

/// <param name="sender"></param>
/// <param name="e"></param>
static void textBox_TextChanged(object sender, TextChangedEventArgs e)
{
	var textBox = sender as TextBox;
	var newText = textBox.Text;
	int currentPosition = textBox.SelectionStart;

	#region text paste

	if (pasteOccurred)
	{
		string newCharacters = newText.Substring(startingLocation, (currentPosition-startingLocation));
		newText = newText.Remove(startingLocation, newCharacters.Length).Insert(startingLocation, newCharacters.ToUpper());
		textBox.Text = newText;
		textBox.SelectionStart = currentPosition;

		pasteOccurred = false;
		startingLocation = -1;

		return;
	}

	#endregion

	if (!isLetter) return;

	currentPosition = textBox.SelectionStart - 1;
	if (currentPosition < 0) return; 	if (Char.IsLower(newText, currentPosition)) 	{ 		string newCharacter = newText.Substring(currentPosition, 1); 		newText = newText.Remove(currentPosition, 1).Insert(currentPosition, newCharacter.ToUpper()); 		textBox.Text = newText; 		textBox.SelectionStart = currentPosition + 1; 	} 	isLetter = false; } 

For Android(TextWatcher implementation)

 final EditText editText = (EditText) view; //new region force -> uppercase implementation
final TextWatcher textWatcher = new TextWatcher() {
	boolean shouldContinue = true;

	@Override
	public void beforeTextChanged(CharSequence s, int start, int count, int after) {
		//To avoid executing of text change listener -> onTextChanged when the time of data binding..
		shouldContinue = editText.hasFocus() ? true : false;
	}

	@Override
	public void onTextChanged(CharSequence s, int start, int before, int count) {
		if(!shouldContinue)return;

		int curCursorLoc = editText.getSelectionEnd();
		if(curCursorLoc < 1) return;

		CharSequence charSet = s.subSequence(start, (start + count));
		if(charSet != null) {
			String upperStr = charSet.toString().toUpperCase();
			StringBuilder stringBuilder = new StringBuilder(s);
			stringBuilder.replace(start, (start + count), upperStr);
			editText.setText(stringBuilder.toString());
			editText.setSelection(curCursorLoc);
		}
	}

	@Override
	public void afterTextChanged(Editable s) {
	}
};

editText.addTextChangedListener(textWatcher);
//endregion

For iOS (iPhone or iPad)


- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string {
    BOOL shouldChange;
    NSRange lowercaseCharRange;
	lowercaseCharRange = [string rangeOfCharacterFromSet:[NSCharacterSet lowercaseLetterCharacterSet]];

	if (lowercaseCharRange.location != NSNotFound) {
		textField.text = [textField.text stringByReplacingCharactersInRange:range
																 withString:[string uppercaseString]];
		UITextPosition *newCursorPosition = [textField positionFromPosition:textField.beginningOfDocument inDirection:UITextLayoutDirectionRight offset:(range.location + 1)];
		UITextRange *newCursorRange = [textField textRangeFromPosition:newCursorPosition toPosition:newCursorPosition];
		[textField setSelectedTextRange:newCursorRange];
		shouldChange = NO;
	}
	else
		shouldChange = YES;

    return shouldChange;
}

Dealing with EMFILE(Too many open files) & OOM(OutOfMemory) Exceptions in Android

Today I’m going to write about some of the issues I faced while developing an Image Gallery with caching techniques, efficient bitmap loading and what tactics that I’ve taken to overcome the issues.

I have developed a custom image gallery with the use of android GridView. Key requirement was to load large amount of images with limited time. In addition the user should be able to smoothly scroll up-down the gallery control.

In order to accomplish this, I have used caching techniques like LruCache, scale down the images before loading (low memory consumption) and loading the images asynchronously (with the use of AsyncTask).

I experienced the above mentioned EMFILE(Too many open files) exception while I was testing this. At this time there were 50-100 images were loaded into the Image Gallery and I was scrolling up and down for around 10-20 times.

EMFILE(Too many open files)

This was happening due a limitation in Android(We can say this is an inherited behavior of Linux). The reason behind this is Android limits the max open files per process to 1024. So this can be experienced in a scenario like Image Gallery with large amount of files, unless we carefully handle the file processing techniques.

But finally got a clue.

It was the process of scaling down the images. It had few FileInputStream objects which were not closed after consuming.

protected static Bitmap decodeFile(File imageFile, int requiredHeight, int requiredWidth) throws Exception {

	// Decode image size
	BitmapFactory.Options oSize = new BitmapFactory.Options();

	oSize.inJustDecodeBounds = true;
	oSize.inDither = false;
	oSize.inPurgeable = true;
	oSize.inInputShareable = true;

	//FileInputStream is used, but it's not closing after consuming...
	BitmapFactory.decodeStream(new FileInputStream(imageFile), null, oSize);

	// Find the correct scale value. It should be the power of 2.
	int width_tmp = oSize.outWidth, height_tmp = oSize.outHeight;
	int scale = 1;
	while (true) {
		if (width_tmp <= requiredWidth || height_tmp <= requiredHeight)
			break;
		width_tmp /= scale;
		height_tmp /= scale;
		scale *= 2;
	}

	// Decode with inSampleSize
	BitmapFactory.Options oPreview = new BitmapFactory.Options();
	oPreview.inSampleSize = scale;
	oPreview.inDither = false;
	oPreview.inPurgeable = true;
	oPreview.inJustDecodeBounds = false;
	oPreview.inInputShareable = true;

	//FileInputStream is used, but it's not closing after consuming...
	return BitmapFactory.decodeStream(new FileInputStream(imageFile), null, oPreview);

}

Following is the modified version of the code block, and I was able to get rid of EMFILE(Too many open files) exception.


protected static Bitmap decodeFile(File imageFile, int requiredHeight, int requiredWidth) throws Exception {

	FileInputStream fileInputStreamIn = null;
	FileInputStream fileInputStreamOut = null;
	
	try{
		// Decode image size
		BitmapFactory.Options oSize = new BitmapFactory.Options();
		
		oSize.inJustDecodeBounds = true;
		oSize.inDither = false;
		oSize.inPurgeable = true;
		oSize.inInputShareable = true;
		
		fileInputStreamIn = new FileInputStream(imageFile);
		BitmapFactory.decodeStream(fileInputStreamIn, null, oSize);

		// Find the correct scale value. It should be the power of 2.
		int width_tmp = oSize.outWidth, height_tmp = oSize.outHeight;
		int scale = 1;
		while (true) {
			if (width_tmp <= requiredWidth || height_tmp <= requiredHeight)
				break;
			width_tmp /= scale;
			height_tmp /= scale;
			scale *= 2;
		}

		// Decode with inSampleSize
		BitmapFactory.Options oPreview = new BitmapFactory.Options();
		oPreview.inSampleSize = scale;
		oPreview.inDither = false;
		oPreview.inPurgeable = true;
		oPreview.inJustDecodeBounds = false;
		oPreview.inInputShareable = true;
		
		fileInputStreamOut = new FileInputStream(imageFile);
		return BitmapFactory.decodeStream(fileInputStreamOut, null, oPreview);
	}
	finally{
		//Got rid of EMFILE Exception(Too many file open)
		if(fileInputStreamIn != null)
			fileInputStreamIn.close();
		if(fileInputStreamOut != null)
			fileInputStreamOut.close();
	}

}

OOM(OutOfMemory)

The application got crashed, soon after I left the screen which had the Image Gallery. This was due to OOM Exception. This issue was also bit tricky, since there was no straight forward way of identifying the reason behind this. The Image Gallery (Custom GridView) Adapter was fully enhanced to reuse the views (using of ViewHolder pattern which Android has suggested), so I thought this wasn’t causing the error. Then my eyes turned into the implementation of LruCache. There was a slight mistake in LruCache, because when the time of initializing it I have used a fixed size for it.

So, DON’T DO THIS,


mMemoryCache = new LruCache<String, Bitmap>(30)

because what Android has suggested is, set the size of LruCache dynamically depending of the available memory.


final int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);

final int cacheSize = maxMemory / 8;

mMemoryCache = new LruCache<String, Bitmap>(cacheSize) {
	@Override
	protected int sizeOf(String key, Bitmap bitmap) {
		return (bitmap.getRowBytes() * bitmap.getHeight()) / 1024;
	}
};

With the help of above modification, I was able to get rid of the OOM Exception.

Happy Coding…. 🙂

Creating a workable Android 5.0 (Lollipop) Emulator

At the beginning I was struggling while setup and running a Android Lollipop emulator. So I thought putting all the steps into one place and sharing it in a simpler manner.

This was fully tested in Windows 7 (64 bit) environment with Eclipse JUNO Version: 4.2.1.

1. Please make sure that you have the latest Android SDK Tools and Android SDK Platform-tools. If not please update them to the latest version.

2

2. Install necessary components of Android 5.0(API21). For the moment, I didn’t install the components related to Android TV.

4

3. Make sure that you have installed Android SDK Build-tools -> Rev. 21.

3

3. Make sure that you have the latest component of Android Support Library & Google Play services.

5

4. You must install Intel x86 Emulator Accelerator(HAXM installer). Don’t use the Android SDK Manager for installing that.

6

Because at the moment the SDK Manager gives you HAMX installer version 1.1.0. But to build up Android Lollipop emulator, it requires the latest version of HAMX which is 1.1.1. This can be found in the following link Intel x86 Emulator Accelerator(HAMX installer)

5. Next go and build the Android Lollipop emulator.

6. I have selected Nexus 5 as the device.

11

7. When you are creating the Android Lollipop emulator(AVD) make sure to select Android 5.0-API Level 21 as the Target.

7

If you select Google APIs – API Level 21, you might get a warning even if you have installed Google APIs Intel Atom(x86) system image.

Warning

8. For CPU/ABI, I have chosen Google APIs Intel Atom(x86_64).

8

9. Make sure that you have selected Use Host GPU option rather than Snapshot in Emulation Options. Otherwise when the emulator starts you will see nothing but a black blank screen only.

9

Finally you are ready to go. Start your newly created Android Lollipop emulator(AVD) from the AVD list.

12

Generic Parcelable Implementation

Today I’m going to post you about a generic implementation of Parcelable interface in Android.

Implementation


import android.os.Parcel;
import android.os.Parcelable;

public class GenericParcelable<T> implements Parcelable {

	private T mValue;
	private static ClassLoader mClassLoader;

	@Override
	public int describeContents() {
		return 0;
	}

	@Override
	public void writeToParcel(Parcel parcelOut, int flags) {
        parcelOut.writeValue(mValue);
    }

    @SuppressWarnings("rawtypes")
	public static final Parcelable.Creator<GenericParcelable> CREATOR = new Parcelable.Creator<GenericParcelable>() {

        public GenericParcelable createFromParcel(Parcel in) {
            return new GenericParcelable(in);
        }

        public GenericParcelable[] newArray(int size) {
            return new GenericParcelable[size];
        }
    };

    public GenericParcelable(T value){
    	this.mValue = value;
    	if(this.mValue != null)
    		GenericParcelable.mClassLoader = value.getClass().getClassLoader();
    } 

	@SuppressWarnings("unchecked")
	private GenericParcelable(Parcel parcelIn) {
    	try{
            //reading the passed value
    		mValue = (T)parcelIn.readValue(GenericParcelable.mClassLoader);
    	}
    	catch(Exception e){
    		e.printStackTrace();
    	}
    }

    //generic method of obtaining the parcel value
	public T getValue(){
		return (T) mValue;
    }

}

Usage

This is the way of adding a value to Parcelable object in an Activity class.


Intent intent = new Intent(this, NextActivity.class);
//Create the Parcelable object with an int value
intent.putExtra("myKey", new GenericParcelable<Integer>(15));
startActivity(intent);

Obtaining the value in a different Activity.

int passedValue;
GenericParcelable<Integer> genericParcelable = getIntent().getParcelableExtra("myKey");
if (genericParcelable != null){
   passedValue = genericParcelable .getValue();
}

Happy coding 🙂

Unexpected behavior of text color transparency in Android 4.0.4

Came across an unexpected behavior when setting the text color of a Check box while developing an android application. Actually the text color which is transparent color was set in the XML layout like below.

<CheckBox
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="@string/text_state"
    android:textColor="#00000000">
</CheckBox>

The code above worked as expected in Android 2.3.4, 4.1.2. But the transparency trick didn’t work in Android 4.0.4. But when I refer this color via the resource file, it worked in all versions including 4.0.4

Ex:

<CheckBox
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="@string/text_state"
    android:textColor="@color/my_transparent">
</CheckBox>

Developing a Simple Google Map Application in Android [debug/release modes] (Android 4.0, Google Play Service, Google Maps Android API v2)

Today I’m going to post about an android application which uses Google Maps. I’m going to use Google Maps Android API v2(since Google Maps Android API v1 is deprecated) and it is not easy to use as Google Maps Android API v1. The steps which you have to follow is mentioned below.

Remember, please don’t waste time try running this application with an Emulator. I have seen many people tried run these types of application with an Emulator, but most of the time it has failed.

My Development Environment

Samsung S2(GT-I9100) with Android version 4.0.3(which was upgraded from Android 2.0+). Also this is quite important, I had the latest Google Play Service installed in it.

If you have this kind of environment I guaranteed that the application will run smoothly.

Because of Google Maps Android API v2, is strongly connected with Google Play Service library, you need to download it first. You can easily do it using the Android SDK Manager.

Play

Once it is downloaded you can find a folder called google-play-services_lib. It will be available in your Android-SDK -> Extras -> Google -> google_play_services -> libproject folder.
Ex: C:\Program Files (x86)\Android\android-sdk\extras\google\google_play_services\libproject\google-play-services_lib

Import it as an android project to your Eclipse environment/your workspace.

project-import

Once the project is imported, we can start implementing our Android Map Application.

First you have to create an Android Project, in my case I set the target Platform into Android 4.0 & API Level 14. My project name was Rakimap and the package name was com.rakimap. The Activity name was MainActivity, which is added by default.

Then what you have to do is you have to reference the google-play-services_lib project which you have imported earlier.

import-play

Then it comes to the most easiest but trickiest part of the project, In order to you the Google Map API Services you have to obtain a key. Whether you are in the debug environment or release environment you have to use a key in order to use the facilities of this API. So here’s how you do it.

Debug Environment

You need to use the debug.keystore file, if you are using Eclipse IDE you will be able to find it under Users -> <current-user-name> -> .android folder. Ex: C:\Users\rawilk\.android

The open the command prompt and navigate to you jdk-version\bin folder (Ex: C:\Program Files (x86)\Java\jdk1.5.0\bin), where you can find the keytool.exe. In there you have to execute this command.

keytool.exe -list -v -keystore “<file path>\debug.keystore” -alias androiddebugkey -storepass android -keypass android

debug key

Once it is executed it will response back with set of lines.

debug key-sha

There you can find two types of certificate fingerprints and you have to copy the SHA1 value for later usage.

Then you should visit this link Google Api Console. As I prefer the new Google Cloud API Console, you will be seen a page like this once you logged-in. You have to go to APIs section which is under APIs & auth. In there if the Google Maps Android API v2 status is OFF, turn it ON.

google-api-console

Once it’s done move to the Credentials section. In there click the Create new Key button.

google-api-console-new-key

Once you click it a popup dialog will be opened. There you press the button named as Android key.

google-api-console-new-key-android

Once you click it you will be seen another pop-up screen which asks the SHA1 value which you have copied earlier. Once you put it, then enter a semi-colon and then enter the name of the package as it is in the project. (This is the package which contains the Activity which will be used for displaying the Google Map later in this). In my case it was com.rakimap.

google-api-console-new-key-android-sha

Once you click the Create button you will be able to get the API key. Copy it and keep it somewhere because we are going to use it later in the project that you have created.

Finally here we go with the blank activity which you created earlier. Let’s make it really simple. Here’s how the MainActivity class would be.

package com.rakimap;

import android.app.Activity;
import android.os.Bundle;
import com.google.android.gms.maps.CameraUpdateFactory;
import com.google.android.gms.maps.GoogleMap;
import com.google.android.gms.maps.MapFragment;
import com.google.android.gms.maps.model.LatLng;
import com.google.android.gms.maps.model.Marker;
import com.google.android.gms.maps.model.MarkerOptions;

public class MainActivity extends Activity {

	static final LatLng COLOMBO = new LatLng(6.927, 79.861);
	private GoogleMap map;

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

		map = ((MapFragment) getFragmentManager().findFragmentById(R.id.map))
				.getMap();

		Marker markerColombo = map.addMarker(new MarkerOptions().position(
				COLOMBO).title("Hamburg"));

		// Move the camera to Colombo area with the zoom level of 20.
		map.moveCamera(CameraUpdateFactory.newLatLngZoom(COLOMBO, 20));

		// Animating the camera while zooming.
		map.animateCamera(CameraUpdateFactory.zoomTo(10), 2000, null);
	}

}

Make the layout file like this. In my case it was the activity_main.xml.


<RelativeLayout 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"
    tools:context=".MainActivity" >

    <fragment
        android:id="@+id/map"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        class="com.google.android.gms.maps.MapFragment" />

</RelativeLayout>

Here’s how the AndroidManifest.xml is going to be.

You have to put the obtained Google API Key inside a meta-data tag block, as well as set some additional user permissions as below mentioned.  Also remember to use the correct package name where necessary.
Ex: com.rakimap.permission.MAPS_RECEIVE

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.rakimap"
    android:versionCode="1"
    android:versionName="1.0" >

    <uses-sdk
        android:minSdkVersion="8"
        android:targetSdkVersion="14" />

    <permission
        android:name="com.rakimap.permission.MAPS_RECEIVE"
        android:protectionLevel="signature" />

    <uses-feature
        android:glEsVersion="0x00020000"
        android:required="true" />

    <uses-permission android:name="com.rakimap.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" />
    <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" >
        <activity
            android:name="com.rakimap.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>

        <meta-data
            android:name="com.google.android.maps.v2.API_KEY"
            android:value="AIzaSyAb8-sm4w3X9SXAHFCDhFdDZkayPfLjgiw" />
        <meta-data
            android:name="com.google.android.gms.version"
            android:value="@integer/google_play_services_version" />

    </application>

</manifest>

Once this is done, try building the project to rectify any compile time errors. If there are no errors, plug-in your android device(I hope you have the per-requisites knowledge of how to handle an android device in development environment) and try deploying it.

You will be seen this if All Is Well.

Screenshot_2014-01-10-10-53-55

Release Environment

When it comes to release the application as an APK, obtaining of the Google API Key will same, but you need to create a separate key-store for this.(you won’t automatically get a key-store like debug.keystore).

Open command prompt and navigate to the location of keytool.exe. Once you there you have to execute the below command in order to create the private key which you will be used in exporting the android application.

keytool.exe -genkey -v -keystore “<path of the release key to be stored>\<name of the file>” -alias <release key alias name> -keyalg RSA -keysize 2048 -validity 10000

Ex: keytool.exe -genkey -v -keystore “C:\RakiMap\raki-release.keystore” -alias raki-release-key -keyalg    RSA -keysize 2048 -validity 10000

release-key1

Once you execute this, you will be asked a series of questions which you need to be answered. Answering for some of the questions are not needed. Refer the below shown image to get an idea.

release-key2

If the key generation is successful(please locate where it resides), you can follow the same steps to obtain a new Google API Key for release mode. Before that you need to get the SHA1 value for the newly generated key. To do that execute the below command.

keytool.exe -list -v -keystore “<path of the release key to be stored>\<name of the file>”

Ex: keytool.exe -list -v -keystore “C:\RakiMap\raki-release.keystore”

release-key3

So once you obtain the new Google API Key for release mode, you have to modify the AndroidManifest.xml file.

Then you can rebuild the program and you are ready to export the application for releasing. I hope you know the initial steps of exporting the android application when it comes to the Keystore selection dialog, you have to set the newly generated keystore file and use the same password which you used when building the key.

release1

Once you press next you will be seen a dialog like this. There you have to select the alias name which you have given when you building the key. Use the same password as well.

release2

After that it is pretty straightforward, you have to set a path to generate the APK file. Once the APK file is generated try installing it in the device. If All Is Well you will be seen this again.

Screenshot_2014-01-10-10-53-55