Categories
Android

Sketchbook app for Android

I have a Samsung Galaxy Note II. As you may already know, it has a big screen and the fact that it comes with a note taking app made by Samsung (I think) makes it very useful to jot down ideas, doodle, etc. I, however, wondered if there was an app that would facilitate doodling even more – a Photoshop caliber doodling app, if you will. Thus, began my search for a graphic program.

I tried the Sketchbook Express app and fell in love with it. It’s a bit limiting but you get an idea of what the paid version can do. I like the way the UI works, too. It doesn’t have any menus or anything visible on the screen – just a dot at the bottom. You tap the dot and out pop all tools. It has all sorts of brushes, color picker, the eye-dropper, etc. You can make a custom brush if preset ones don’t meet your needs.

After fiddling with the free version, I decided to get the paid version of the app (which was for $2.13 with taxes when I bought it). I am not an artist but after messing with the free and paid versions, I was able to draw these images.

This picture took me 2-3 hours on the free version:

Sunset

I started making this one in the free version and completed it in the paid version (took me 3 weeks to complete – a few minutes here, a few minutes there):

Desk

Just to be safe: No one is allowed to use these images without my written permission.

Categories
Android

Nexus S and TMobile data connection

Here’s an interesting tidbit. If you go out of the country and come back. Nexus S won’t automatically connect to TMobile’s 3G or edge network. You have to switch it off and start it again for it to connect to the data network. It will, however, let you make phone calls, and send and receive SMS without restarting it.

Categories
Android

Setting up your Android environment

I have seen quite a few posts on some lists asking about how to set up a programming environment for Android. While it is very straight forward, the documentation is all over the place. So, I decided to write this tutorial :).

In this tutorial, we will install Android SDK in eclipse (Galileo), we will add an SD Card to your emulator and put some pictures in it. I have written this tutorial with Windows users in mind but the process should not be very different for other platforms.

I have written this tutorial assuming that you already have JDK installed. If you don’t have it already installed then please install it before reading this. You can find a tutorial on how to install here.

Installing the SDK

  1. Download the latest eclipse IDE from
    http://www.eclipse.org/downloads/.

    You can also download Galileo directly from here.

  2. Create a folder in your c:\ drive and name it android. The file you just downloaded (at least from the direct link I provided above) is a zip file which contains a folder called eclipse. Extract the zip file and place the folder in, c:\android. Now, you have eclipse installed in c:\android. Go into the eclipse folder. You will see an executable called eclipse. Double click it to run eclipse.

    Click to enlarge
    Click to enlarge
  3. Now, download the android SDK 1.6 (the latest as of this writing) for windows from the link below:http://developer.android.com/sdk/download.html?v=android-sdk-windows-1.6_r1.zipTo download the SDK for other platforms, go here :http://developer.android.com/sdk/1.6_r1/index.html
  4. Extract the contents of the zip file in c:\android\. Now, you will have 2 folders in c:\android\. One is eclipse and the other is android-sdk-windows-xxx.android-sdk-windows-xxx folder contains another folder called tools. We need to add this folder to the system’s PATH variable. To do that, Click on Start, then right click on “My Computer” and click on “Properties” in the menu that pops up.

The “System Properties” dialog will pop up. Click on the “Advanced” tab and then click on “Environment variables”. Another dialog will appear.

Run eclipse. Click on Help -> Install New Software.

Click to enlarge
Click to enlarge

An install dialog will appear. Click on “Add”, another dialog will appear. Type in Android for name and put in https://dl-ssl.google.com/android/eclipse for Location. Then click OK. Click on next, check both the checkboxes and click next. Eclipse will start downloading the plugin and install it. You will be asked to restart eclipse after the installation is done. Click on “Yes” when it asks you to restart. Eclipse will restart and you can, now, create Android projects!

Click to enlarge
Click to enlarge

Now that the basic installation is done. Let’s setup the SC Card.

Setting up the SD Card

Creating the SD card is very easy.

  1. Click on Start -> Run. The run dialog will appear
  2. Type “cmd” (without the quotes) and hit enter or click the OK button. A commant prompt window will appear.
  3. Type “android list targets” in it and hit enter. It will show you a list of targets. Get the id for Android 1.6. It will most probably be 2
  4. Type the command “android create avd -n testavd -t <target_id> -c 128M” (without the quotes) and hit enter. Make sure to subsitute the <target_id> with the actual ID you got from “android list targets” command. Since it will most probably be 2, the command will look like this “android create avd -n testavd -t 2 -c 128M (again without quotes).
  5. Press enter 1 more time

Viola! you have created your SD Card for the emulator. The next time you start the emulator, you will be able to access it. Below is an illustration to make things easy.

Click to enlarge
Click to enlarge

When I created my SD card, I needed to get some pictures on it. So, I used an external program called winimage to do that.

Working with your SD card

  1. Download winimage from http://www.winimage.com/download.htm. You can either get the zip file or the exe file. I downloaded the zip file, extracted it and use that.
  2. Run winimage.exe and make sure your emulator is not running.
  3. Click on Open image icon. Browse to c:\android\sd_card\ folder. Select sdcard and click OK button. Winimage will open the sdcard image. Now, you can browse the file structure and add files, delete files, etc. After you are done, make sure to close winimage or you won’t be able to access the SD card from the emulator.

As always here’s an illustration:

Click to enlarge
Click to enlarge

Now, we have an the SD Card image open. Let’s create a folder called “media” and put some pictures in it. When you take pictures with your phone’s camera they are stored in the media folder in  your SD Card.

To create the media folder, click on Image, then on Create Folder. A dialog box will appear. Type “MEDIA” in it. and Click OK. After the folder is created, you can drag and drop pictures from your computer to the media folder just like you would normally do it when copying files from one location on your computer to another.

WinImage
Click to Enlarge
Create Folder
Click to enlarge
Copy pictures
Click to enlarge

Categories
Android Java

Android HttpRequest class (version 2.1)

Version of of this class had some bugs so I corrected them and here is the latest version 🙂

Enjoy!

package moz.http;

import java.net.*;
import java.io.*;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Map.Entry;
import org.apache.http.client.HttpClient;
import org.apache.commons.*;
import android.util.Log;
/**
* HTTP Request class
*
* You can use this class and distribute it as long as you give proper credit
* and place and leave this notice intact :). Check my blog for updated
* version(s) of this class (http://moazzam-khan.com)
*
* Usage Examples:
*
* Get Request
* --------------------------------
* HttpData data = HttpRequest.get("http://example.com/index.php?user=hello");
* System.out.println(data.content);
*
* Post Request
* --------------------------------
* HttpData data = HttpRequest.post("http://xyz.com", "var1=val&var2=val2");
* System.out.println(data.content);
* Enumeration keys = dat.cookies.keys(); // cookies
* while (keys.hasMoreElements()) {
* 		System.out.println(keys.nextElement() + " = " +
* 				data.cookies.get(keys.nextElement() + "rn");
*	}
* Enumeration keys = dat.headers.keys(); // headers
* while (keys.hasMoreElements()) {
* 		System.out.println(keys.nextElement() + " = " +
* 				data.headers.get(keys.nextElement() + "rn");
*	}
*
* Upload a file
* --------------------------------
* ArrayList files = new ArrayList();
* files.add(new File("/etc/someFile"));
* files.add(new File("/home/user/anotherFile"));
*
* Hashtable ht = new Hashtable();
* ht.put("var1", "val1");
*
* HttpData data = HttpRequest.post("http://xyz.com", ht, files);
* System.out.println(data.content);
*
* @author Moazzam Khan
*/
public class HttpRequest {

        /**
        * HttpGet request
        *
        * @param sUrl
        * @return
        */
        public static HttpData get(String sUrl) {
                HttpData ret = new HttpData();
                String str;
                StringBuffer buff = new StringBuffer();
                try {
                        URL url = new URL(sUrl);
                        URLConnection con = url.openConnection();

                        BufferedReader in = new BufferedReader(new InputStreamReader(con.getInputStream()));
                        while ((str = in.readLine()) != null) {
                                buff.append(str);
                        }
                        ret.content = buff.toString();
                        //get headers
                        Map> headers = con.getHeaderFields();
                        Set>> hKeys = headers.entrySet();
                        for (Iterator>> i = hKeys.iterator(); i.hasNext();) {
                                Entry> m = i.next();

                                Log.w("HEADER_KEY", m.getKey() + "");
                                ret.headers.put(m.getKey(), m.getValue().toString());
                                if (m.getKey().equals("set-cookie"))
                                ret.cookies.put(m.getKey(), m.getValue().toString());
                        }
                } catch (Exception e) {
                        Log.e("HttpRequest", e.toString());
                }
                return ret;
        }




        /**
        * HTTP post request
        *
        * @param sUrl
        * @param ht
        * @return
        * @throws Exception
        */
        public static HttpData post(String sUrl, Hashtable ht) throws Exception {
                String key;
                StringBuffer data = new StringBuffer();
                Enumeration keys = ht.keys();
                while (keys.hasMoreElements()) {
                        key = keys.nextElement();
                        data.append(URLEncoder.encode(key, "UTF-8"));
                        data.append("=");
                        data.append(URLEncoder.encode(ht.get(key), "UTF-8"));
                        data.append("&");
                }
                return HttpRequest.post(sUrl, data.toString());
        }
        /**
        * HTTP post request
        *
        * @param sUrl
        * @param data
        * @return
        */
        public static HttpData post(String sUrl, String data) {
                StringBuffer ret = new StringBuffer();
                HttpData dat = new HttpData();
                String header;
                try {
                        // Send data
                        URL url = new URL(sUrl);
                        URLConnection conn = url.openConnection();
                        conn.setDoOutput(true);
                        OutputStreamWriter wr = new OutputStreamWriter(conn.getOutputStream());
                        wr.write(data);
                        wr.flush();

                        // Get the response

                        Map> headers = conn.getHeaderFields();
                        Set>> hKeys = headers.entrySet();
                        for (Iterator>> i = hKeys.iterator(); i.hasNext();) {
                                Entry> m = i.next();

                                Log.w("HEADER_KEY", m.getKey() + "");
                                dat.headers.put(m.getKey(), m.getValue().toString());
                                if (m.getKey().equals("set-cookie"))
                                dat.cookies.put(m.getKey(), m.getValue().toString());
                        }
                        BufferedReader rd = new BufferedReader(new InputStreamReader(conn.getInputStream()));
                        String line;
                        while ((line = rd.readLine()) != null) {
                                ret.append(line);
                        }

                        wr.close();
                        rd.close();
                } catch (Exception e) {
                        Log.e("ERROR", "ERROR IN CODE:"+e.getMessage());
                }
                dat.content = ret.toString();
                return dat;
        }
        /**
        * Post request (upload files)
        * @param sUrl
        * @param files
        * @return HttpData
        */
        public static HttpData post(String sUrl, ArrayList files)
        {
                Hashtable ht = new Hashtable();
                return HttpRequest.post(sUrl, ht, files);
        }
        /**
        * Post request (upload files)
        * @param sUrl
        * @param params Form data
        * @param files
        * @return
        */
        public static HttpData post(String sUrl, Hashtable params, ArrayList files) {
                HttpData ret = new HttpData();
                try {
                        String boundary = "*****************************************";
                        String newLine = "rn";
                        int bytesAvailable;
                        int bufferSize;
                        int maxBufferSize = 4096;
                        int bytesRead;

                        URL url = new URL(sUrl);
                        HttpURLConnection con = (HttpURLConnection) url.openConnection();
                        con.setDoInput(true);
                        con.setDoOutput(true);
                        con.setUseCaches(false);
                        con.setRequestMethod("POST");
                        con.setRequestProperty("Connection", "Keep-Alive");
                        con.setRequestProperty("Content-Type", "multipart/form-data;boundary="+boundary);
                        DataOutputStream dos = new DataOutputStream(con.getOutputStream());

                        //dos.writeChars(params);

                        //upload files
                        for (int i=0; i 0) {
                                        dos.write(buffer, 0, bufferSize);
                                        bytesAvailable = fis.available();
                                        bufferSize = Math.min(bytesAvailable, maxBufferSize);
                                        bytesRead = fis.read(buffer, 0, bufferSize);
                                }
                                dos.writeBytes(newLine);
                                dos.writeBytes("--" + boundary + "--" + newLine);
                                fis.close();
                        }
                        // Now write the data

                        Enumeration keys = params.keys();
                        String key, val;
                        while (keys.hasMoreElements()) {
                                key = keys.nextElement().toString();
                                val = params.get(key);
                                dos.writeBytes("--" + boundary + newLine);
                                dos.writeBytes("Content-Disposition: form-data;name=""
                                + key+""" + newLine + newLine + val);
                                dos.writeBytes(newLine);
                                dos.writeBytes("--" + boundary + "--" + newLine);

                        }
                        dos.flush();

                        BufferedReader rd = new BufferedReader(
                        new InputStreamReader(con.getInputStream()));
                        String line;
                        while ((line = rd.readLine()) != null) {
                                ret.content += line + "rn";
                        }
                        //get headers
                        Map> headers = con.getHeaderFields();
                        Set>> hKeys = headers.entrySet();
                        for (Iterator>> i = hKeys.iterator(); i.hasNext();) {
                                Entry> m = i.next();

                                Log.w("HEADER_KEY", m.getKey() + "");
                                ret.headers.put(m.getKey(), m.getValue().toString());
                                if (m.getKey().equals("set-cookie"))
                                ret.cookies.put(m.getKey(), m.getValue().toString());
                        }
                        dos.close();
                        rd.close();
                } catch (MalformedURLException me) {

                } catch (IOException ie) {

                } catch (Exception e) {
                        Log.e("HREQ", "Exception: "+e.toString());
                }
                return ret;
        }
}

You will also need the class below:

package moz.http;
import java.util.Hashtable;
public class HttpData {
      public String content;
      public Hashtable cookies = new Hashtable();
      public Hashtable headers = new Hashtable();
}
Categories
Android

HTTP Request class for Android (version 2)

Hi Everyone,

After some people pointed out the problems they had with version 2 of this class, I took it all out.  Please go to: http://moazzam-khan.com/?p=490 for version 2.1

Categories
Android Uncategorized

HTTP Request class for Android


Warning: Illegal string offset 'language' in /app/wp-content/plugins/igsyntax-hiliter/classes/frontend.php on line 510

Warning: ksort() expects parameter 1 to be array, string given in /app/wp-content/plugins/igsyntax-hiliter/classes/frontend.php on line 513

Here is a class that can be used to make HTTP get and HTTP post requests. I haven’t commented everything but the usage should be pretty clear from the method signatures.

Note: This is for Android 1.5.

import java.net.*;
import java.io.*;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Map.Entry;


import org.apache.http.client.HttpClient;

import org.apache.commons.*;
import android.util.Log;

public class HttpRequest {
	
	/**
	 * HttpGet - doesn't read cookies
	 * 
	 * @param sUrl
	 * @return
	 */
	public static HttpData get(String sUrl) {
		HttpData ret = new HttpData();
		String str;
		StringBuffer buff = new StringBuffer();
		try {
			URL url = new URL(sUrl);
			
			BufferedReader in = new BufferedReader(new InputStreamReader(url.openStream()));
			while ((str = in.readLine()) != null) {
				buff.append(str);
			}
			ret.content = buff.toString();
		} catch (Exception e) {
			Log.e("HttpRequest", e.toString());
		}
		return ret;
	}
	
	

	
	/**
	 * HTTP post request
	 * 
	 * @param sUrl
	 * @param ht
	 * @return
	 * @throws Exception
	 */
	public static HttpData post(String sUrl, Hashtable<String, String> ht) throws Exception {
		StringBuffer data = new StringBuffer();
		Enumeration<String> keys = ht.keys();
		while (keys.hasMoreElements()) {
			data.append(URLEncoder.encode(keys.nextElement(), "UTF-8"));
			data.append("=");
			data.append(URLEncoder.encode(ht.get(keys.nextElement()), "UTF-8"));
			data.append("&");
		}
		return HttpRequest.post(sUrl, data.toString());		
	}
	/**
	 * HTTP post request
	 * 
	 * @param sUrl
	 * @param data
	 * @return
	 */
	public static HttpData post(String sUrl, String data) {
		StringBuffer ret = new StringBuffer();
		HttpData dat = new HttpData();
		String header;
		try {
			// Send data
			URL url = new URL(sUrl);
			URLConnection conn = url.openConnection();
			conn.setDoOutput(true);
			OutputStreamWriter wr = new OutputStreamWriter(conn.getOutputStream());
			wr.write(data);
			wr.flush();
	    
			// Get the response
			
			Map<String, List<String>> headers = conn.getHeaderFields();
			Set<Entry<String, List<String>>> hKeys = headers.entrySet();
			for (Iterator<Entry<String, List<String>>> i = hKeys.iterator(); i.hasNext();) {
				Entry<String, List<String>> m = i.next();
				
				Log.w("HEADER_KEY", m.getKey() + "");
				dat.headers.put(m.getKey(), m.getValue().toString());
				if (m.getKey().equals("set-cookie"))
					dat.cookies.put(m.getKey(), m.getValue().toString());
			}
			BufferedReader rd = new BufferedReader(new InputStreamReader(conn.getInputStream()));
			String line;
			while ((line = rd.readLine()) != null) {
				ret.append(line);
			}
			
			wr.close();
			rd.close();
		} catch (Exception e) {
			Log.e("ERROR", "ERROR IN CODE:"+e.toString());
	    }
		dat.content = ret.toString();
		return dat;		
	}
}

You will also need this class along with it.

  1. import java.util.Hashtable;
  2.  
  3. public class HttpData {
  4.     public String content;
  5.     public Hashtable<String, String> cookies = new Hashtable<String, String>();
  6.     public Hashtable<String, String> headers = new Hashtable<String, String>();
  7. }
Categories
Android Uncategorized

Android 1.5 SDK Released !

Google has released Android 1.5 SDK. This release is based on the cupcake branch in Android’s repository. Cupcake was a modified version of the SDK written by a group of independent developers and functioned better than the 1.0 SDK. Android 1.5 SDK also has other enchancements like a better profiler. For a list of all changes and enhancements, please visit: http://developer.android.com/sdk/RELEASENOTES.html

Categories
Android

Sending data to other activities in Android

Let’s say you have a list activity which lists some information and you have another activity which adds/edits information in the list. How do you tell the edit activity which item (in the list of the first activity) needs to be edited? 

 

In the first activity, you will do this :

/*********************************************
* These constants will be passed to the second activity and
* the second activity will return this back when it returns its
* result
**********************************************/
private static final int ALERT_ADD = 100;
private static final int ALERT_EDIT = 110;

public void callAddEditActivity() {
	// get the unique ID of the selected
	long itemId = this.getListView().getSelectedItemId();

	/** If no item was selected, then this condition will be true */
	if (itemId == ListView.INVALID_ROW_ID) {
		//put an alert here asking to select something
		return;
	}
	/**************************************************
	* Now, we will get the position of the item in the list. From that
	* we can get the cursor object, which will contain other information
	* about it. In this case, I want to get the name  and phone number.
	*
	* The cursor object is a regular cursor you use with a database.
	* Refer to my post about using databases with Android for more
	* information on it.
	***************************************************/
	int pos  = this.getListView().getSelectedItemPosition();
	Cursor c = (Cursor) this.getListView().getAdapter().getItem(pos);
	
	/***************************************************
	* Now, you can create a "bundle" which can be passed to setCotntact.
	* SetContact is the other activity which is used for adding/editing
	* names and phone numbers. It will use the bundle to get the data
	* for the item it is supposed to edit.
	****************************************************/

	Intent in1  = new Intent();
	Bundle bun = new Bundle();
	
	bun.putLong("id", c.getLong(3));
	bun.putString("name", c.getString(0));
	bun.putString("phone", c.getString(1));
	in1.setClass(this, SetContact.class);
	in1.putExtras(bun);
	
	startActivityForResult(in1, ALERT_EDIT);
}

Let’s take a look at SetContact activity which be used to edit (or add) data to the list.

package gContact.gContact;

import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.widget.EditText;

public class SetContact extends Activity {
	private EditText txtName;
	private EditText txtPhone;
	private long id;

	@Override
	public void onCreate(Bundle savedInstanceState) {
		/** This piece is not important */
		super.onCreate(savedInstanceState);
		setContentView(R.layout.set_contact);
		txtName = (EditText) findViewById(R.id.alertText);
		txtPhone = (DatePicker) findViewById(R.id.alertDate);

		/********************************************
		* When data is passed to any activity in a bundle, it can be
		* accessed through getExtras(). If nothing is passed, then this
		* bundle will be null.
		*
		* If the bundle is null, then that means we are adding a new item
		* otherwise we are editing an existing item
		*********************************************/
		Bundle bun = getIntent().getExtras();
		if (null == bun) return;

		/*********************************************
		* If this is an edit, then set the proper values in the text boxes
		**********************************************/
		this.id     = bun.getLong("id");
		String text = bun.getString("name");
		String phone = bun.getString("phone");

		if (null != text) txtName.setText(text);
		if (null != phone) txtPhone.setText(phone);
	}

	/*****************************************
	* This creates a menu with 2 buttons  - save and cancel
	******************************************/
	public boolean onCreateOptionsMenu(Menu menu) {
		super.onCreateOptionsMenu(menu);
		MenuItem item1 = menu.add(0, 0, 0,"Save");
		MenuItem item2 = menu.add(0, 1, 1, "Cancel");
		return true;
	}

	/*********************************************
	* This function is run when one of our menu items is selected.
	**********************************************/
	public boolean onOptionsItemSelected(MenuItem item) {
		switch (item.getItemId()) {
		/**************************************
		*  If Save was clicked/pressed/tapped then we
		* gather name and phone number and pass it to the
		* main activity so it can add it to the database.
		*
		* Adding to the database can be done here too, but
		* I like doing it in the main activity
		***************************************/
		case 0:
			Bundle conData = new Bundle();
			String name = txtName.getText().toString();
			String phone = txtPhone.getText().toString();

			conData.putString("name", name);
			conData.putString("phone", phone);
			conData.putLong("id", this.id);

			Intent mIntent = new Intent();
			mIntent.putExtras(alertData);
			setResult(RESULT_OK, mIntent);
			break;

		case 1: // Cancel
			break;
		}
		finish();
		return true;
	}
}

When the second activity sends data back to the first activity, onActivityResult method is called (in the first activity). It will look something like this :

protected void onActivityResult(int requestCode, int resultCode, Intent data){
	switch (requestCode) {
	case ALERT_ADD:
		if (resultCode != RESULT_CANCELED) {
			Bundle res = data.getExtras();

			/***********************************************
			* After we get the new information from the second activity, we
			* can add it to the database and then refresh the list. If the below
			* statement is unclear, refer to my blog post on databases and
			* Android
			************************************************/
			db.createContact(
				res.getString("name"),
				res.getString("phone")
			);
			fillData();
		}
		break;

	case ALERT_EDIT:
		if (resultCode != RESULT_CANCELED) {
			Bundle res = data.getExtras();
			db.updateContact(res.getLong("id"), res.getString("name"), res.getString("phone"));
			fillData();
		}
		break;
	}
}
Categories
Android

More Android phones on the way

Yes, you heard it. More Android based phones (at least 2)are on their way some time in 2009. Motorola, Sony, Samsung, etc are also going to come out with Android phones some time by 2010. HTC’s CEO said that they will be releasing at least 3 phones this year. We know one of them is the G2, which means that HTC will be releasing 2 more Android phones. 

For those of you who are thinking of buying an Android based phone: you may want to wait a few more months and see if the new phones are better than G1 (which has an annoying design honestly)

Here is some more information on this topic :

Information week

Beta news

Engadget


Categories
Android Uncategorized

Android Exeda – A more detailed look

I had written a post about CompuLab releasing an Android based phone called Exeda. It’s official now and they are making it available in March. They will make the price available along with the phone. It has a 3.5 inch screen which is sun-readable. It also has a touchpad which acts as a mouse. The phone itself seems very small in the picture so it might not be meant for big fingers. The keys are very small in size. It also comes with Windows Mobile and they say they will add support for Linux in the future (yay! Linux).

According to its maker

“Exeda is designed to serve as an enterprise digital assistant (EDA). Comprised of embedded components, the exeda has guaranteed long-term availability and can be custom configured when ordered in volume. “

For more information go to http://www.exedamobile.com/web/?gclid=CJHqrvG4qJgCFQrAGgodLyYvnw and http://www.exedamobile.com/web/index.php?option=com_content&view=article&id=3&Itemid=7

Exeda
Exeda
Exeda
Exeda