Pages

Showing posts with label background process. Show all posts
Showing posts with label background process. Show all posts

Monday, October 7, 2013

Keyword Density Checker

There are many keyword density checkers on the web. The novice webmasters always use the keyword density checker to get information about their web page content. A good keyword checker helps the webmaster to have pages that conform to SEO (Search Engine Optimization) guidelines so that their web pages will get high ranks from the search engines. Many SEO experts consider the optimum keyword density to be 1 to 3 percent. The percent that is greater than this will be considered as spam and be penalized by the search engines.

In this post, you will learn to create a simple app to be used to check keywords density of a web page from Android devices. I called this project KeywordDensityChecker. You need to create a project in Eclipse and name it KeywordDensityChecker. The KeywordDensityChecker reads content of web page and analyze it to produce a keyord density table that shows keywords, frequencies, and percentages. The frequency of each keyword represents the number of the same keywords found in the web page. Its percentage is calculated by multiplying 100 to the divisional result of the frequency and the total keywords analyzed.
For the user interface of this simple keyword density checker, we need one EditText, one Button, and one ListView. The EditText allows the user to enter a web page address. The Button will be pushed to read the content of the web page and shows the analyzed result on the ListView. These views or components are defined in the activity_main.xml file that is the resource of the Main_Actity class.

activity_main.xm file

<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:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context=".MainActivity"
    android:orientation="vertical"

     >

  <EditText
        android:id="@+id/txt_input"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:inputType="text"
        android:hint="Enter web page adress"
        />
     
<Button
          android:id="@+id/bt_extract"
          android:layout_width="match_parent"
          android:layout_height="wrap_content"
          android:text="Check"
          android:onClick="check"
          />

 
        <ListView
            android:id="@+id/density_list"
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:paddingBottom="5dp"
            android:paddingTop="5dp"
     
            />    

</LinearLayout>


The ListView displays keywords, frequencies, and percentages. Thus, the ListView needs to be customized to show these three items. The layout file (listlayout.xml) of the ListView will have three TextView components defined. One TextView displays the keyword; another one displays the frequency; and the last one displays the percentage.

keyword density checker



The content of the listlayout.xml file is shown below.

listlayout.xml file

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

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:orientation="horizontal"
    android:padding="5dip" >

<TextView
    android:id="@+id/keyword"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:padding="10sp"
        android:textSize="20sp"
        android:textColor="#0000ff"
        android:textStyle="bold"
       
         >
</TextView>

<TextView
        android:id="@+id/count"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:padding="10sp"
        android:textSize="20sp"
        android:textColor="#0000ff"
 
        android:textStyle="bold" >
</TextView>

<TextView
    android:id="@+id/percent"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:padding="10sp"
        android:textSize="20sp"
        android:textColor="#0000ff"
   
        android:textStyle="bold" >
</TextView>
</LinearLayout>


Now we take a look at the MainActivty.java file that contains the MainActivity class. This will be the start point of our KeywordDensityChecker app. In this class, the check method is defined. The check method will be called when the Check button is pushed. In this method, we reference to te EditText address and the density_list views. The Checker object is created. All methods are used to read content of the web page, analyze, and prepare the necessary data (keywords, frequencies, and percentages) to construct the keyword density table are defined in the Checker class.

MainActivity.java file

package com.example.keyworddensitychecker;

import android.os.AsyncTask;
import android.os.Bundle;
import android.app.Activity;
import android.app.ProgressDialog;
import android.content.Context;
import android.view.Menu;
import android.view.View;
import android.widget.EditText;
import android.widget.ListView;
import android.widget.Toast;


public class MainActivity extends Activity {

private Context context;
private ListView list;
private Checker checker;
ProgressDialog pd;
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        context=this;      
    }
 
    @Override
    protected void onDestroy() {
    if (pd!=null) {
pd.dismiss();

}
    super.onDestroy();
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.main, menu);
        return true;
    }
 
 
 
    public void check(View view){
    EditText txtaddress=(EditText)findViewById(R.id.txt_input);
    String address=txtaddress.getText().toString();
    list=(ListView)findViewById(R.id.density_list);
        checker=new Checker();
    if(address.length()>0){
    BackTask bt=new BackTask();
    bt.execute(address,null,null);
    }
    else
    Toast.makeText(context, "Please input web page address", Toast.LENGTH_SHORT).show();
    }
 
    private class BackTask extends AsyncTask<String,Void,Void>{  
   
   
    protected void onPreExecute(){
    super.onPreExecute();
    //show process dialog
    pd = new ProgressDialog(context);
pd.setTitle("Checking...");
pd.setMessage("Please wait.");
pd.setCancelable(true);
pd.setIndeterminate(true);
pd.show();
   
   
    }
    protected Void doInBackground(String...params){    
    try{
    //start analyzing the web page
    checker.processChecking(params[0]);
    }catch(Exception e){
    pd.dismiss();   //close the dialog if error occurs
    }
return null;
   
    }
   
   
   
    protected void onPostExecute(Void result){
    //close the progress dialog
    pd.dismiss();
    //create ListAdapterModel object for the ListView so that
    //the keyword density table can be displayed
    ListAdapterModel lam=new ListAdapterModel(context,R.layout.listlayout,checker.getWords(),checker.getCounts(),checker.getPercents());
    list.setAdapter(lam);
    }
   
    }



 
}


Reading content of the web page, analyzing it, and displaying the result can take long time. So it is a good idea to place these processed in background thread. In the previous post (Web Downloader), you learn to use the IntentService class to put the web downloading process in background thread so that it runs without locking the user interface. In this KeywordDensityCheck, you learn another tool to do the background process. This tool is called AsyncTask. The AsyncTask has three important methods--onPreExecute, doInBackground, and onPostExecute. Int the onPreExecute mehtod, you can write code to do something before the background process starts. In this app, the ProgressDialog is displayed to tell the user to wait.

Progress Dialog to wait while analyzing the web page


In the doInBackground method, you will place code that represents the background process. In this case, the proessChecking method of the Checker class is called to read the content of the web page, analyze it, and prepare the necessary data to construct the keyword density table. After the background process completes you can write code to do something in the onPostExecute method. In our KeywordDensityChecker app, the task to do after the background process completes is closing the progress dialog and constructing the ListAdapterModel object for the ListView component so that the keyword density table can be shown to the user.
The ListView component needs the ArrayAdapter object be its data source. In default, the ListView can display only a single TextView component. In this app, the ListView needs three TextView components. The first step to customize the ListView is to define the three TextView components in the layout file as i mentioned above. The additional step to customize the ArrayAdapter to enable the ListView to show the keyword density table that consists of keywords, frequencies, and percentages. Generally, to customize the ArrayAdapter class, you need to extend it. The ListAdapterModel extends the ArrayAdapter class. The important part of the ArrayAdapter class that must be overridden is the getView method. In this method, code is written to allow the ListView to show the keyword on the keyword TextView, frequency on the count TextView, and the percentage on the percent Textview. Below is the content of the ListAdapterModel class.

ListAdapterModel.java file

package com.example.keyworddensitychecker;

import java.util.ArrayList;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.TextView;

public class ListAdapterModel extends ArrayAdapter<String>{
int groupid;
ArrayList<String> words;
Context context;
ArrayList<String> counts;
ArrayList<String> percents;

public ListAdapterModel(Context context, int vg,ArrayList<String> words,ArrayList<String> counts,ArrayList<String> percents){
super(context,vg, words);
this.context=context;
groupid=vg;
this.words=words;
this.counts=counts;
this.percents=percents;
}
public View getView(int position, View convertView, ViewGroup parent) {

        LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        View itemView = inflater.inflate(groupid, parent, false);
        TextView textKeyword = (TextView) itemView.findViewById(R.id.keyword);
        TextView textCount= (TextView) itemView.findViewById(R.id.count);
        TextView textPercent= (TextView) itemView.findViewById(R.id.percent);
        textKeyword.setText(words.get(position));
        textCount.setText(counts.get(position));
        textPercent.setText(percents.get(position));
        return itemView;
     
}

}


The last code fragment that defines the backgroud process is in the Checker class. In this class, there are four methods. The first method ,readWords will be called to read the content of the web page, split the content in to words. Each word is separated by a single space or many spaces. The split method is used to split the content in to words. A word might contain symbols. The regular expression is used to remove all symbols from the words. The Pattern class is used to define string pattern ("\\W+") to match all symbols (except underscore) and the replaceAll method of the Matcher is called to remove all the symbols from the words. To learn more about regular expression in Java, read this page. The words are filtered again to ignore the words that are not keywords. The filtering task is performed by invoking the addToList method. The addToList method adds every keyword to the LinkedList, keywordsList. The countWords method is invoked to count the frequency of each keyword. This method also adds the every keyword and its frequency to the HashMap, denMap. The prepareTable method prepares data necessary to construct the keyword density table. The keywords will be stored in the ArrayList, keywords; the frequencies are stored in the ArrayList, counts; and the percentages are stored int he ArrayList, percents. The processChecking method wraps the readWords, countWords, and prepareTable methods so that they can be called at once. The getWords, getCounts, and getPercents methods are called from the MainActivity class to return the list of keywords, the list of frequencies, and the list of percents to be used in the ListAdapterModel class. The content of the Checker class is shown below.

Checker.java file

package com.example.keyworddensitychecker;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.Set;
import java.util.TreeMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;


class Checker{
private String address;
private TreeMap<String, Integer> denMap;
private LinkedList<String> keywordsList;
private ArrayList<String> keywords;
private ArrayList<String> counts;
private ArrayList<String> percents;

Checker(){
denMap=new TreeMap<String,Integer>();
keywordsList=new LinkedList<String>();
keywords=new ArrayList<String>();
counts=new ArrayList<String>();
percents=new ArrayList<String>();
}

public void readKeywords(){
Pattern pattern=Pattern.compile("\\W+");
try {
//read the web page content
URL url=new URL(address);
HttpURLConnection con=(HttpURLConnection)url.openConnection();
InputStream is=con.getInputStream();
BufferedReader br = new BufferedReader(new InputStreamReader(is, "UTF-8"));
String strLine;
while((strLine=br.readLine())!=null){
String[] words=strLine.split("[ ]+"); //split content in to words
for(String word:words){
Matcher mat=pattern.matcher(word); //remove all symbols except underscore
word=mat.replaceAll("");
addToList(word.toLowerCase());
}
}

br.close();
} catch (Exception ex) {
// TODO Auto-generated catch block
ex.printStackTrace();
}
}

public void addToList(String word){

//filter non-keywords
//there are more non-keywords that are not filtered in this simple program
//you can add those non-keywords in the nonKeyword array below.
boolean isNonKey=false;
String[] nonKeywords={"a","an","after","and","are","as","at","above","before"
,"below","beyond","br","div","for","in","is","li","of","on","p","the"
,"span","that","this","those","tr","td","to","ul","under","when","where","you"};
for(String nonKey:nonKeywords){
if(nonKey.equals(word)){
isNonKey=true;
break;
}
}
//add only the keyword to the list
if(!isNonKey)
keywordsList.add(word);
}


public void countKeywords(){
int count=1;
String word="";
for(int i=0;i<keywordsList.size();i++){
word=keywordsList.get(i);
for(int j=i+1;j<keywordsList.size();j++){
if(word.equals(keywordsList.get(j))){
count++;
}
}

addToMap(word,count);
count=1;
}

}

public void addToMap(String word, int count){
//place keyword and its frequency in TreeMap
if(!denMap.containsKey(word) && word.length()>=1){
denMap.put(word, count);
}

}


public void prepareTable(){

Set<String> keys=denMap.keySet();
int numWord=keys.size();
Iterator<String> iterator=keys.iterator();
while(iterator.hasNext()){
String word=iterator.next();
int count=denMap.get(word);
int p=100*count/numWord;
if(p>0){
keywords.add(word);
counts.add(String.valueOf(count));
percents.add(p+"%");

}
}



}

public void processChecking(String address){

this.address=address;
readKeywords();
countKeywords();
prepareTable();
}

public ArrayList<String> getWords(){
return keywords;
}
public ArrayList<String> getCounts(){
return counts;
}
public ArrayList<String> getPercents(){
return percents;
}


}


Before starting to run the KeywordDensityChecker app, you need to allow Android to use the internet by placing the below code to the AndroidManifest.xml file.

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

Download the apk file of the KeywordDensityChecker app

Wednesday, September 18, 2013

Web download

In this post, you learn to create a simple web donwloader app in Android. The web downloader app can download any file (including html pages) that is referred to by a link shown on a browser or a link that the user types in the address text box. To start developing the web download app, you need to create a project in Eclipse. The project name will be called WebDownloader.

web downloader user interface


On the app user interface, we need one EditText, two Buttons, and one Listview. The EditText view allows the user to enter the address or url of the file to be downloaded. If the user selects a link from the browser, this text box will display the address or url of the link. The first Button labeled "Add to download list" is pushed to add the address in the text box to the ListView for later downloading. The second Button will be pushed to start the download process. It is labeled "OK". These views are defined in the activity_main.xml as shown below.

<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:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context=".MainActivity"
android:orientation="vertical"
android:background="@drawable/back_style"
>

<EditText
android:id="@+id/txt_input"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:inputType="text"
android:hint="@string/txt_hint"
/>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
>
<Button
android:id="@+id/bt_add"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/bt_label"
android:onClick="addClick"
/>
<Button
android:id="@+id/bt_ok"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/bt_ok"
android:onClick="okClick"
/>
</LinearLayout>
<TextView
android:id="@+id/txt_view"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
/>
<ListView
android:id="@+id/url_list"
android:layout_width="fill_parent"
android:layout_height="300dp"
android:paddingBottom="20dp"
android:paddingTop="20dp"
android:background="#f8f8f8"
/>

</LinearLayout>

To apply the backround to user interface, we need a resource file called back_style.xml stored in the drawable directory. This file defines the look and feel of the app background. To apply the background style to the user interface this file must be defined as a value of background property of the LinearLayout view (android:background="@drawable/back_style").

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item>
<shape>
<gradient
android:startColor="#ffffff"
android:endColor="#992211"
android:angle="270" />
<stroke
android:width="1dp"
android:color="#171717" />
<corners
android:radius="4dp" />
<padding
android:left="10dp"
android:top="10dp"
android:right="10dp"
android:bottom="10dp" />
</shape>
</item>
</selector>


Some string resources that are used by the views are written in the strings.xml file. The content of the strings.xml file is shown below.

<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name">WebDownloader</string>
<string name="action_settings">Settings</string>
<string name="bt_label">Add to download list</string>
<string name="txt_hint">Enter web page address</string>
<string name="bt_ok">OK</string>
</resources>

As mentioned above, the user can launch the web downloader app by two ways. One is by selecting a link from the browser and another is by launching it directly from the Android applications laucher. For the first way, the web downloader app needs to be listed in the app choices list to view or download the file that is refered to by the link. You can have your apps to diplay in the popup list by defining intent filters in the AndroidManifest.xml file of the app. The intent filters specify the capacity that the web downloader app can do in terms of actions to be performed, and data to operate on. By doing this, the Android system know how to capture intents that match the capacity of the app and ignore intents that do not match. An intent is a data structure that contains data and operations on the data. It can be used to send and receive data in an app or between apps.

Display web downloader in popuup list of apps choices


To define an intent filter, you need to use the <intent_filter> tag. This tag will be placed in the activity, service, or broadcast receiver that will receive the intent. When defining an intent filter, there are three parts that need to be considered. The first part specifies the action the app can do. In this example app, the action that the app can do is VIEW action. The VIEW action tells the Android that the web downloader app can download and display the content of the link. When this action is specified, the intent will contain the address or url of the link and so we can write code to get the link and download data from the link. The code to do these tasks are written in the MainActivity.java file that is shown later in this post. The second part determines the kind of the intents that will be received by the web downloader appp. The DEFAULT category allows the Android system to capture intents that do not have a category. In most cases, we use this category. The BROWABLE category tells Android to filter intents that are in browsable category (displayable and downloadable). The final part can be used to specify the type of data, or url (including scheme, host, and file path). In this app, we allow any file to be downloaded so the type of data can be specified by a value of "*/*" to the mimeType property. Sine the file to be downloaded is from the internet and from any host, the value of the scheme property will be "http" and the value for the host is "*".

<intent-filter>
<action android:name="android.intent.action.VIEW"></action>
<category android:name="android.intent.category.DEFAULT"></category>
<category android:name="android.intent.category.BROWSABLE"></category>
<data android:mimeType="*/*" android:scheme="http" android:host="*"></data>
</intent-filter>

For reference documentation about defining intent filter and its related parts, you can visit android developer website.

In this example app, the AndroidManifest.xml looks like the following.

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.webdownloader"
android:versionCode="1"
android:versionName="1.0" >
<uses-sdk
android:minSdkVersion="8"
android:targetSdkVersion="17" />
<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"/>
<application
android:allowBackup="true"
android:icon="@drawable/wd"
android:label="@string/app_name"
android:theme="@style/AppTheme" >
<activity android:configChanges="orientation"
android:name="com.example.webdownloader.MainActivity"
android:label="@string/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"></action>
<category android:name="android.intent.category.DEFAULT"></category>
<category android:name="android.intent.category.BROWSABLE"></category>
<data android:host="*" android:scheme="http"></data>
</intent-filter>
</activity>

<service
android:name="DownloadService"
android:icon="@drawable/ic_launcher"
android:label="MYSERVICE">
</service>
</application>
</manifest>


In the AndroidManifest.xml file we also define the permissions for the web downloader app. The first permision allows the app to use the internet, the second permision to access the network connection states, and the final permision to read and write data to the external storage (sdcard).

<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"/>

The last part of the AndroidManifest.xml file, we declare a service. The name of the service is the same as the class that extends the Service or IntentService. A service can be used to do operations that need long time to complete. When doing long-time operations, the user interface is locked. So it does not respond to the user and this situation might make the user feel uncomfortable with your app. To avoid this problem, the operations need to be run in background. The IntentService class is used to do the downloading process in background in the web downloader appp because it can take long time to complete. A service can receive data (stored in an intent object) from an activity that requires the long-time process to be passed to the service and send the feedback information to the activity. In this app, the data to be sent from the main activity are the ArrayList object that store the urls of the links that were added to the ListView to download and the output directory to store the files downloaded. You need to override the onHandleIntent method to get the data stored in an intent object that is sent by the main activity.

DownloadService.java file

package com.example.webdownloader;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import android.app.IntentService;
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;

public class DownloadService extends IntentService {
private ArrayList<String> urlist;
private String storeDir;
public DownloadService() {
super("SERVICE");
// TODO Auto-generated constructor stub
}

public void onHandleIntent(Intent intent){
Bundle b=intent.getExtras();
if(b!=null){
urlist=b.getStringArrayList("URLS");
storeDir=b.getString("OUTDIR");
startDownload();
}


}

public void startDownload(){
String feedback="";
if(urlist.size()>0){
try {
for(int i=0;i<urlist.size();i++){    

URL url=new URL(urlist.get(i));
readDataStream(url);

}

feedback="Complete! Check the webdownload directory on your sdcard";

} catch (MalformedURLException e) { e.printStackTrace();}
}
else{

feedback="No url is added to the download list.";
}
//send feedback message to the main activity
Intent backIntent=new Intent("com.example.webdownloader");
backIntent.putExtra("BACKMESS", feedback);
sendBroadcast(backIntent);

}
public void readDataStream(URL url){
    try {
   
    File f=new File(storeDir);
    if(f.exists()){
    HttpURLConnection con=(HttpURLConnection)url.openConnection();
    InputStream is=con.getInputStream();
    String path=url.getPath();
    String filename=path.substring(path.lastIndexOf('/')+1);
    FileOutputStream fos=new FileOutputStream(storeDir+"/"+filename);
    int data=0;
    while((data=is.read())!=-1){
    fos.write(data);
    }
    is.close();
    fos.flush();
    fos.close();
    }
    else
    Log.e("Error","Not found "+storeDir);

} catch (IOException e) {
e.printStackTrace();
}
   
    }
}


To download content of a file that is refered to by a link, you can use the HttpURLConnection class. You can have HttpURLConnection object by using the openConnection method of the URL class.

HttpURLConnection con=(HttpURLConnection)url.openConnection();

HttpURLConnection class has the getInputStream method that returns an InputStream object. When you have the InputStream object, you can use its read method to read content of the file and write it to an output file by using the FileOutputStream class.

The final part of the web downloader app is MainActivity.java file. In this file, we use the ArrayList to store the addresses or urls that are added to the ListView. To use the ArrayList object as an datasource of the ListView, you need to create an ArrayAdapter object to encaptulate the ArrayList object. When creating the ArrayAdapter object you need to provide three values: context, layout of the ListView, and the ArrayList object. Then supply this adapter object to the ListView by using its setAdapter method.

aa=new ArrayAdapter<String>(this,R.layout.listlayout,urlist);
lv.setAdapter(aa);

The layout of the ListView view is defined in the listlayout.xml file in res/layout directory.

<?xml version="1.0" encoding="utf-8"?>
<!-- Single List Item Design -->
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/label"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:padding="10sp"
android:textSize="16sp"
android:textStyle="bold" >
</TextView>

MainActivity.java file

package com.example.webdownloader;
import java.io.File;
import java.util.ArrayList;
import android.net.Uri;
import android.os.Bundle;
import android.os.Environment;
import android.app.Activity;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.util.Log;
import android.view.Menu;
import android.view.View;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ListView;
import android.widget.TextView;


public class MainActivity extends Activity {

    private ListView lv;
    private ArrayList<String> urlist;
    private ArrayAdapter<String> aa;
    private String storeDir;
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        urlist=new ArrayList<String>();
        aa=new ArrayAdapter<String>(this,R.layout.listlayout,urlist);
        lv=(ListView)findViewById(R.id.url_list);
        lv.setAdapter(aa);
        Intent intent=getIntent();
        String action=intent.getAction();      
        if(Intent.ACTION_VIEW.equals(action)){        
        Uri uri=intent.getData();
        EditText txturl=(EditText)findViewById(R.id.txt_input);
        txturl.setText(uri.toString());
       
        }

     
    }

    protected void onStart(){
    super.onStart();
    createStoreDir();
    }
 
    protected void onResume(){
    super.onResume();
    registerReceiver(receiver, new IntentFilter("com.example.webdownloader"));    
    }
 
    protected void onPause(){
    super.onPause();
    unregisterReceiver(receiver);
   

    }
 
    private BroadcastReceiver receiver=new BroadcastReceiver(){
    public void onReceive(Context context,Intent intent){
   
        Bundle b=intent.getExtras();
        if(b!=null){
        TextView tv=(TextView)findViewById(R.id.txt_view);
        tv.setText(b.getString("BACKMESS"));
        }
    }
    };

 
    public void createStoreDir(){
    storeDir=Environment.getExternalStorageDirectory()+"/webdownload";
File f=new File(storeDir);
if(!f.exists())
if(!f.mkdir()){
Log.e("Error","Can't create webdownload directory");
disableButtons();
}
    }
 
    public void disableButtons(){
    Button btadd=(Button)findViewById(R.id.bt_add);
    Button btok=(Button)findViewById(R.id.bt_ok);
    btadd.setEnabled(false);
    btok.setEnabled(false);
    }
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.main, menu);
        return true;
    }
     
    public void addClick(View view){
    EditText txturl=(EditText)findViewById(R.id.txt_input);
    String url=txturl.getText().toString();
    if(url.length()>0){
    urlist.add(url);
    aa.notifyDataSetChanged();
    }
    }
    public void okClick(View view){
    TextView tv=(TextView)findViewById(R.id.txt_view);
    tv.setText("Please wait...");
    Intent newIntent=new Intent(this,DownloadService.class);
    newIntent.putStringArrayListExtra("URLS", urlist);
    newIntent.putExtra("OUTDIR",storeDir);    
    startService(newIntent);

    }

}


The Intent object (called intent) can be obtained by using the getIntent method of the Activity class. This intent object contains the url of the selected link. To get the url from the intent object, you need to use the getData method of the Intent class.
Intent intent=getIntent();
String action=intent.getAction();
if(Intent.ACTION_VIEW.equals(action)){
Uri uri=intent.getData();
//more code here
}

To receive feedback message from the Service (DownloadService), the MainActivity needs to register a BroadcastRecevier by using the registerReceiver method when the activity starts.

registerReceiver(receiver, new IntentFilter("com.example.webdownloader"));

The receiver can be unregistered by using the unregisterReceiver method. This code fragment is usually placed in the onPause or onStop mehtod of the activity.

unregisterReceiver(receiver);

In the onReceive method of the BroadcastReceiver class, you can write some code to get the feedback message sent by the Service (stored in the intent object). The intent object has a method called getExtra that returns a Bundle object that the message is stored.

Bundle b=intent.getExtras();
if(b!=null){
TextView tv=(TextView)findViewById(R.id.txt_view);
tv.setText(b.getString("BACKMESS"));
}

When the user clicks the Add to download list button, the addClick method is called to add the address shown in the text box to the ArrayList and the ListView. When the OK button is pushed and the okClick method is called, the main activity sends an intent object that contains the ArrayList and output directory to the DownloadService. In this case, the intent object is constructed by specifying the context and the service class to start (DownloadService). Then you can call the startService to start the service.

Intent newIntent=new Intent(this,DownloadService.class);
newIntent.putStringArrayListExtra("URLS", urlist);
newIntent.putExtra("OUTDIR",storeDir);
startService(newIntent);

Now run the web downloader app. Then upload and install the WebDownload.apk to your real device. In your real device, make sure the internet is working. Start your web browser and find a download link. Select the download link and choose WebDownloader from the popup list. When the address of the download link shown in the text box, select the Add to download list button to add the address to the ListView then click the OK button.

run web download app for testing

Download apk file of the Web Downloader app