Pages

Saturday, September 21, 2013

Text Viewer

TextViewer is a simple app that can be used to view files that store text. For this simple text viewer app, you can view txt, rtf, xml, java, and html files. To start developing this app, you need to create a project called TextViewer in Eclipse.
There are two sub-interfaces in this app. For the first sub-interface, there are one EditText, and one ListView. The EditText allows you to input and displays the part of a directory of file. It is used as a search box in a dictionary. The ListView shows the files and directories of the directory that is shown in the EditText.

TextViewer Files browse interface



While you are making change to the text in the EditText, the contents (files and directories) of the valid directory shown in the EditText are displayed in the ListView. This sub-interface displays immediately when the user clicks the TextViewer app to launch and this is the place that the user can choose a file to open. The class that represents this sub-interface is called BrowseFragment. This class extends the Fragment class. The layout file for this sub-interface is browse_view.xml. Below are the contents of the BrowseFragment.java and browse_view.xml files. If you are new to the uses of Fragment and FragmentAcvitity in Android, please go back to this previous post Login.

BrowseFragent.java file

package com.example.textviewer;

import com.actionbarsherlock.app.SherlockFragment;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
public class BrowseFragment extends SherlockFragment {
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
return inflater.inflate(R.layout.browse_view, container, false);
}

public static BrowseFragment newInstance(String str){

return(new BrowseFragment());
}
/*
public void onAttach(Activity activity){
super.onAttach(activity);
mAct=activity;
}
*/

public void onStart(){
super.onStart();

}


}


browse_view.xml file

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

    <EditText
        android:id="@+id/txt_input"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:hint="@string/text_hint" />
     <TextView
        android:id="@+id/txt_view"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
         android:layout_below="@+id/txt_input"
      />
    <ListView
        android:id="@+id/files_list"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:layout_below="@+id/txt_view"
        >
     
    </ListView>

</RelativeLayout>


The strings.xml file that declares strings resources to be used in this app is written as shown below:
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <string name="app_name">TextViewer</string>
    <string name="action_settings">Settings</string>
    <string name="text_hint">Type a path</string>

</resources>


The second sub-interface that is represented by the ContentFragment class contains a TextViewer to display the content of the selected file. The ContentFragment class also extends the Fragment class. The layout file for this sub-interface is content_view.xml file. Now you take a look at the ContentFragment.java file and then the content of the content_view.xml file.

ContentFragment.java

package com.example.textviewer;

import java.io.FileInputStream;
import java.io.IOException;

import com.actionbarsherlock.app.SherlockFragment;

import android.app.Activity;
import android.os.Bundle;
import android.text.method.ScrollingMovementMethod;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;

public class ContentFragment extends SherlockFragment{
//@Override
private static String arg;
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
// Inflate the layout for this fragment
return inflater.inflate(R.layout.content_view, container, false);
}
public static ContentFragment newInstance(String str){
arg=str;
return(new ContentFragment());
}


public void onAttach(Activity activity){
super.onAttach(activity);
}

public void onStart(){
super.onStart();
showContent(arg);
}

public void showContent(String path){

TextView txtview = (TextView)getActivity().findViewById(R.id.cont_view);
txtview.setMovementMethod(new ScrollingMovementMethod());
try{
FileInputStream fs=new FileInputStream(path);
int data=0;
while((data=fs.read())!=-1){
char c=(char)data;
txtview.append(c+"");
}
fs.close();
}catch(IOException ie){}
}



}


In the ContentFragment class, the arg variable is used to store the file path selected by the user. This file path is sent from the main activity that is represented by the MainActivity class discussed later in this post. Once the file path is obtained, the content of the file can be read by invoking the showContent method. FileInputStream class is also available in Android to read content of a binary file.

content_view.xml file

<?xml version="1.0" encoding="utf-8"?>
<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"  
    android:background="#dfdfdf"    
     >

 <TextView
     android:id="@+id/cont_view"
     android:layout_width="fill_parent"
     android:layout_height="fill_parent"
     android:maxLines="20"
     android:scrollbars="vertical"
     android:textSize="13sp"
     android:textColor="#000000" />

</RelativeLayout>


Besides launching the TextView app directly from the Android apps launcher, the user can select a text file from Android to view. To make this possible, you have to write some intent filters in AndroidManifest.xml file. The content of the AndroidManifest.xml file is shown below.

AndroidManifest.xml file

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.textviewer"
    android:versionCode="1"
    android:versionName="1.0"
 
     >

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

    <application    
        android:allowBackup="true"
        android:icon="@drawable/textview"
        android:label="@string/app_name"
        android:theme="@style/Theme.Sherlock.Light.DarkActionBar"
     
       
         >
        <activity
            android:configChanges="orientation"
            android:name="com.example.textviewer.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 android:name="android.intent.action.PICK"/>
            <category android:name="android.intent.category.DEFAULT"/>
            <category android:name="android.intent.category.BROWSABLE"/>
            <data android:scheme="file"/>
            <data android:mimeType="*/*"/>
            <data android:pathPattern=".*\\.txt"/>
            <data android:host="*"/>
        </intent-filter>
        <intent-filter>
            <action android:name="android.intent.action.VIEW"/>
            <action android:name="android.intent.action.PICK"/>
            <category android:name="android.intent.category.DEFAULT"/>
            <category android:name="android.intent.category.BROWSABLE"/>
            <data android:scheme="file"/>
            <data android:mimeType="*/*"/>
            <data android:pathPattern=".*\\.rtf"/>
            <data android:host="*"/>
        </intent-filter>
        <intent-filter>
            <action android:name="android.intent.action.VIEW"/>
            <action android:name="android.intent.action.PICK"/>
            <category android:name="android.intent.category.DEFAULT"/>
            <category android:name="android.intent.category.BROWSABLE"/>
            <data android:scheme="file"/>
            <data android:mimeType="*/*"/>
            <data android:pathPattern=".*\\.java"/>
            <data android:host="*"/>
        </intent-filter>
        <intent-filter>
            <action android:name="android.intent.action.VIEW"/>
            <action android:name="android.intent.action.PICK"/>
            <category android:name="android.intent.category.DEFAULT"/>
            <category android:name="android.intent.category.BROWSABLE"/>
            <data android:scheme="file"/>
            <data android:mimeType="*/*"/>
            <data android:pathPattern=".*\\.xml"/>
            <data android:host="*"/>
        </intent-filter>
        <intent-filter>
            <action android:name="android.intent.action.VIEW"/>
            <action android:name="android.intent.action.PICK"/>
            <category android:name="android.intent.category.DEFAULT"/>
            <category android:name="android.intent.category.BROWSABLE"/>
            <data android:scheme="file"/>
            <data android:mimeType="*/*"/>
            <data android:pathPattern=".*\\.html"/>
            <data android:host="*"/>
        </intent-filter>
       
        </activity>
    </application>

</manifest>


Since the TextView app is able to view txt, rtf, xml, java, and html file, you need to have five separate intent filters for these file types. Each intent filter is almost the same, except the path pattern. In each path pattern, different file extension has to be specified for the supported file type. If you are new to the use of intent filter, i recommend you to read the previous post Web Downloader app.

Now we take a look at our main interface. The main interface that is represented by the MainActvity class. This class extends the FragmentAcvity class. On the main interface, the sub-interfaces (BrowseFragment and ContentFragment) are dynamically added and replaced. When the app firstly run (by launching it directly from the Android apps launcher), the first sub-interface is added to the main interface by the code fragment below.

BrowseFragment bf=new BrowseFragment();
getSupportFragmentManager().beginTransaction().add(R.id.fragment_container,bf).commit();


The first sub-interface is replaced by the second sub-interface, when the user selects a file from the ListView to view  so that the content of the selected file is read in the ContentFragment to display on the TextView, cont_view.

ContentFragment cf=ContentFragment.newInstance(path);
FragmentTransaction transact=getSupportFragmentManager().beginTransaction();
transact.replace(R.id.fragment_container, cf);
transact.addToBackStack(null);
transact.commit();

As i mentioned about, you can open the TextViewer app by two ways. One way is opening it directly from the Android apps launcher. For the second way, the user can select a file from Android to open it. How do you know which file is selected by the user? Of course, it is simple to understand. When the user select the file, an intent object that stored path of the file is broadcast by Android. Android lists apps that can open the file. Our TextViewer app is capable to open the file because we used intent filters to inform Android already. So Android will show the TextViewer app in its list. To get the intent object sent by Android, you need to use the getIntent method. When you have the intent object, you can get the path of the selected file. And then its content is read and displayed by calling the openFile method. This method subsequently calls the show method. The code fragment below does the job.

Intent intent=getIntent();
String action=intent.getAction();
String type =intent.getType();
if(Intent.ACTION_VIEW.equals(action) || Intent.ACTION_PICK.equals(action) && type!=""){
openFile(intent);

}

MainActivity.java file

package com.example.textviewer;

import java.io.File;
import com.actionbarsherlock.app.SherlockFragmentActivity;
import com.actionbarsherlock.view.MenuItem;
import android.content.Intent;
import android.os.Bundle;
import android.support.v4.app.FragmentTransaction;
import android.text.Editable;
import android.text.TextWatcher;
import android.util.TypedValue;
import android.view.View;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.ArrayAdapter;
import android.widget.EditText;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;

public class MainActivity extends SherlockFragmentActivity {
float nsize=13.0f;
protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        //Get intent object broadcast by other Android
        //when the user selects a file to open from Android
        Intent intent=getIntent();
        String action=intent.getAction();      
        String type =intent.getType();
        if(Intent.ACTION_VIEW.equals(action) || Intent.ACTION_PICK.equals(action) && type!=""){        
        openFile(intent); //open and read the content of the file
       
        }

        //If no intent, display the BrowseFragment to show a list of files and directories.
        //From this interface the user will select a file to view

        else{
        BrowseFragment bf=new BrowseFragment();
        getSupportFragmentManager().beginTransaction().add(R.id.fragment_container,bf).commit();
        }
    }

public void openFile(Intent intent){
String path=intent.getData().getPath();//read file path from the intent
//then show the content of the file
show(path);
}

public void onStart(){
super.onStart();
regControls(); //register event listeners with EditText and ListView
TextView txtview = (TextView) findViewById(R.id.cont_view);
if(txtview==null)
getSherlock().getActionBar().hide(); //hide ActionBar when you are not on the ContentFragment    
else
getSherlock().getActionBar().show(); //show ActionBar when you are on the ContentFragment
//This will show up zoom in and zoom out image icons on the ActionBar
}

//Handle hardware back button press
public void onBackPressed() {
TextView txtview = (TextView) findViewById(R.id.cont_view);
if(txtview!=null){
BrowseFragment df=new  BrowseFragment();
FragmentTransaction transact=getSupportFragmentManager().beginTransaction();
transact.replace(R.id.fragment_container, df);
transact.addToBackStack(null);
transact.commit();
onStart();
}
else{

System.exit(0);
}
}

public void regControls(){
EditText et=(EditText) findViewById(R.id.txt_input);
if(et!=null){
et.addTextChangedListener(new ChangeListener());//listen to text change event
et.setText("/"); //initialize the path to /
ListView lv=(ListView) findViewById(R.id.files_list);
lv.setOnItemClickListener(new ClickListener());//listen to item click event
}
}

public void zoomin(View view){
TextView tv=(TextView) findViewById(R.id.cont_view);
nsize++;
tv.setTextSize(TypedValue.COMPLEX_UNIT_DIP,nsize);

}

public void zoomout(View view){
TextView tv=(TextView) findViewById(R.id.cont_view);
nsize--;
tv.setTextSize(TypedValue.COMPLEX_UNIT_DIP,nsize);

}

class ChangeListener implements TextWatcher{
   
    public void beforeTextChanged(CharSequence s, int start, int before, int count){
   
   
    }
    //Making change to text of the EditText, txt_input
    public void onTextChanged(CharSequence s, int start, int before, int count){
    EditText et=(EditText) findViewById(R.id.txt_input);
    String txt=et.getText().toString();
    listDirContents(txt);//display the files and directories in the ListView
    }
   
    public void afterTextChanged(Editable ed){
   
   
    }
    }
 
    class ClickListener implements OnItemClickListener{
      public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
      // selected item
            String selectedItem = ((TextView) view).getText().toString();
            EditText et=(EditText) findViewById(R.id.txt_input);
            et.append("/"+selectedItem);//append selected item to the path
            show(et.getText().toString());//show the content of the input path if it is a file.
         
           
      }
      public void onNothingSelected(AdapterView<?> parent){
     
      }
     
     
      }
 
 
 
    public void listDirContents(String path){
    ListView l=(ListView) findViewById(R.id.files_list);
    if(path!=null){
    try{
    File f=new File(path);    
    if(f!=null){
    String[] contents=f.list();
    if(contents.length>0){
    ArrayAdapter<String> aa=new ArrayAdapter<String>(this,R.layout.listlayout,contents);
    l.setAdapter(aa);
    }
    }
    }catch(Exception e){}
    }
   
 
   
    }

    public void show(String path){
    Toast.makeText(getBaseContext(), "Opening "+path, Toast.LENGTH_SHORT).show();
    try{
    File f=new File(path);
    if(f.isFile()){//display ContentFragment to display the content of the selected file
    ContentFragment cf=ContentFragment.newInstance(path);
    FragmentTransaction transact=getSupportFragmentManager().beginTransaction();
    transact.replace(R.id.fragment_container, cf);
    transact.addToBackStack(null);
    transact.commit();
    getSherlock().getActionBar().show();
    }
    }catch(Exception e){}
    }
 
 
    @Override
    public boolean onCreateOptionsMenu(com.actionbarsherlock.view.Menu menu) {
        getSupportMenuInflater().inflate(R.menu.main, menu);
        return super.onCreateOptionsMenu(menu);
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        super.onOptionsItemSelected(item);
        TextView txtview = (TextView) findViewById(R.id.cont_view);
     
        if(txtview!=null){
        switch(item.getItemId()){
       
            case R.id.zoomin:
            zoomin(txtview);
                break;

            case R.id.zoomout:
            zoomout(txtview);
                break;

        }
     
        return true;
        }
        else return false;
    }

 
 
}


Set up ActionBar for TextViewer app


ActionBar displays at the top your user interface. Generally, the ActionBar are used to placed some actions that are frequently used. In the TextViewer app, the zoom in and zoom out actions are placed on the ActioBar. The items of the ActionBar are written in the main.xml file (in res/menu directory). This ActionBar is shown only if the second sub-interface (ContentFragment) displays.


Here is the content of the main.xml file.
<menu xmlns:android="http://schemas.android.com/apk/res/android"
    style="@style/Theme.Sherlock" >
    <item
        android:id="@+id/zoomin"
        android:showAsAction="ifRoom|withText"
        android:icon="@drawable/zoomin"
        style="@style/Theme.Sherlock"
    />

    <item
        android:id="@+id/zoomout"
        android:showAsAction="ifRoom|withText"
        android:icon="@drawable/zoomout"
        style="@style/Theme.Sherlock"
    />

</menu>

You will have two small images in res/drawable directory. One represents the zoom in action and another one for zoom out action.
The main activity displays the ActionBar in its onCreateOptionsMenu method. The code fragment below shows this point.

public boolean onCreateOptionsMenu(com.actionbarsherlock.view.Menu menu) {
getSupportMenuInflater().inflate(R.menu.main, menu);
return super.onCreateOptionsMenu(menu);
}

You will need to override the onOptionsItemSelected method to determine which item (zoom in or zoom out) of the ActionBar is selected. The switch case statement can be used to check the id of the item that is selected.

The library that is used to set up the ActionBar in the TextViewer is ActionBarSherkLock. The reason to choose this library is that it supports older Android versions (lower than 3.0) and still can be used in newer versions. You can download the ActionBarSherkLock library from the link http://actionbarsherlock.com/download.html. You will get a zip file that contains the ActionBarSherkLock project inside. Extract the zip file and place its contents to a place that you can find it easily.
Now you need to add the ActionBarSherkLock project to the TextViewer app. In the File menu, select New then select Project...When the New Project window displays, Click Android to expand it. Then select Android Project from Existing Code. By clicking the Next button, you will get the Import Projects window. Click the Browse button to find the directory that contains the ActionBarSherkLock project (from the zip file you just extracted).

image actionbarsherklock project


Now the ActionBarSherkLock project is included in the Package Explorer (shown at top left corner).

sherklocklock added to the project


For the next step, you need to add the ActonBarSherkLock project as a library of the TextViewer app. You can accomplish this task by right-click on the TextViewer then selection Android. Click the Add... button to open the Project Selection dialog then select actionbarsherlock. Click Ok to close the dialog. Please the see the picture below to clarify these steps.

Add actionbarsherklock as a library to the TextViewer app


ActionBarSherkLock uses android-support-v4 library jar file. This will conflict with the android-support-v4 libary jar file that you have when you first create the TextView app. To avoid this problem, you need to delete android-support-v4 libary jar file that comes with the TextView app.

Now you are ready to run the TextViewer app. Upload the TextView.apk file and install it on your real device and test it. If your any question regarding this sample app, please leave comments below.

Download apk file of the Text Viewer 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

Sunday, September 15, 2013

Login

In this post, you learn to create a simple login app. Now open your Eclipse and create a new project called Login. If you don't know how to create a project in Eclipse, you probably need to read this post Helloworld app.
In this app, you are introduced to the Fragment class in Android. The Fragment class can be treated as a sub-activity or sub-interface. You can create many fragments as your want. Each user interface that is created by every fragment can be added, replaced, or removed from the main activity so that you have dynamic user interface in your app. To create a fragment, you need to extends the Fragment class and save it with java extension. In the Login app, we need to fragments. One fragment is called LoginFragment. This fragment is added to the main activity when the app first opens.

LoginFragment.java
package com.example.login;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
public class LoginFragment extends Fragment {
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
     // Inflate the layout for this fragment
       return inflater.inflate(R.layout.fragment_login, container, false);
    }

}

On this sub-interface, there are one TextView, two EditText, and one Button views. These views are loaded from the xml file called fragment_login. The content of the fragment.login.xml file is shown below.

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:background="@drawable/back_style"
>
<TextView
android:id="@+id/txt_view"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="@string/label"
android:textSize="20sp"
/>
<EditText
android:id="@+id/txt_user"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:hint="@string/user_hint"
android:inputType="text" />
<EditText
android:id="@+id/txt_pwd"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:hint="@string/pwd_hint"
android:inputType="textPassword" />
<Button
style="@style/AppTheme"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="doLogin"
android:text="@string/bt_login" />
</LinearLayout>

Invalid login



The TextView view is used to display the text "Login" at the top of the login form. One EditText allows the user to input the user name and another EditText is to input the password. The Login button validates the input data. If the login is invalid, the app only shows a message "Invalid login". Otherwise, the second fragment or sub-interface is displayed a welcome message "Welcome! You have loginned successfully.". For this simple app, the user name and password that are used in the validation process are defined as string resources in the strings. xml file. The string resources that are used in this layout file are defined in the strings.xml file. This is the content of the strings.xml file.

<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name">Login</string>
<string name="action_settings">Settings</string>
<string name="hello_world">Hello world!</string>
<string name="user_hint">Enter user name</string>
<string name="pwd_hint">Enter password</string>
<string name="bt_login">Login</string>
<string name="label">Login</string>
<string name="username">myusername</string>
<string name="password">mypassword123</string>

</resources>
The second fragment is used to display the welcome message as mentioned above. This sub-interface shows when the login is valid. The layout file for this fragment is shown below.
<?xml version="1.0" encoding="utf-8"?>
<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:background="@drawable/back_style"
>
<TextView
android:id="@+id/txtsucess_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="20sp"
/>

</LinearLayout>
The backround of each fragment is defined by a resource file called back_style.xml. This file is stored in the res/drawable directory. The content of this file is shown below.
<?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="#00ff00"
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>

The second fragment is defined in the java file called LogSucessFragment. To pass a value from the main activity to a fragment you need to define the constructor of the fragment class. This constructor is called newInstance. In this app, once the login is valid, the welcome message is passed from the main activity to the second fragment through this constructor. To refer to a view in the fragment, you need to user the getActivity method of the fragment class. This method returns the activity that passes data to the fragment. In this case it is the main activity. When you got the activity object, you can use its findViewById method to refer to any view within the fragment class.

LogSuccessFragment.java
package com.example.login;
import android.app.Activity;import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
public class LogSuccessFragment extends Fragment {
//@Override
//Activity mAct;
static String arg;
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
// Inflate the layout for this fragment

     return inflater.inflate(R.layout.fragment_success, container, false);
}
public static LogSuccessFragment newInstance(String str){
    arg=str;
    return(new LogSuccessFragment());
}

public void onStart(){
    super.onStart();
    TextView txtview = (TextView)getActivity().findViewById(R.id.txtsucess_view);
     txtview.setText(arg);
}

}

Valid Login




The MainActivity.java file that acts as the main interface of the Login app is written as shown below. To support older Android versions (lower than 3.0) you need to use the FragmentActivity instead of Activity class. The FragmentActivity and all other fragments used in this app are in Support Library v4. If you got the latest Android SDK installed on your machine, you will have this library automatically in your project. In your project window, you can find this library by expanding or double-clicking the Android Private Libraries.
To add a fragment to the main activity. You need to create the instance of the fragment. Then use the add method of the Transaction object to to add the fragment instance to the main activity. You can get the Transaction object by using the beginTransaction method from the fragment manager. Replacing or removing a fragment also follows this process except you need to change from add to replace or remove method.

package com.example.login;
import android.os.Bundle;
import android.app.Activity;
import android.support.v4.app.FragmentActivity;
import android.support.v4.app.FragmentTransaction;
import android.view.Menu;
import android.view.View;
import android.widget.EditText;
import android.widget.Toast;
public class MainActivity extends FragmentActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
       super.onCreate(savedInstanceState);
       setContentView(R.layout.activity_main);
       LoginFragment lf=new LoginFragment();
       getSupportFragmentManager().beginTransaction().add(R.id.fragment_container,lf).commit();
}

@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 doLogin(View view){
    EditText txtuser=(EditText)findViewById(R.id.txt_user);
   EditText txtpassword=(EditText)findViewById(R.id.txt_pwd);
   String username=getResources().getString(R.string.username);
   String password=getResources().getString(R.string.password);
   if(txtuser.getText().toString().equals(username) && txtpassword.getText().toString().equals(password)){
       String mess="Welcome! You have logined successfully.";
       LogSuccessFragment logf=LogSuccessFragment.newInstance(mess);
       FragmentTransaction transact=getSupportFragmentManager().beginTransaction();
       transact.replace(R.id.fragment_container, logf,"log");
       transact.addToBackStack(null);
       transact.commit();
        }
 else
     Toast.makeText(this, "Invalid login", Toast.LENGTH_SHORT).show();

    }

}

The layout file of the main activity simply contains a FragmentLayout container. This file is called activity_main.xml file.
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/fragment_container"
android:layout_width="match_parent"
android:layout_height="match_parent"
/>

Download apk file of the Login app
compass app flashLight app

Wednesday, September 11, 2013

Ideal Body Weight Calculator

In this post, you learn to create the Ideal Body Weight Calculator (IBWC) app. The app allows the user to specify his/her sex and height. Then the recommended weight of the user is calculated. The Devine formula is used to calculate the IBW for men. This formula is shown below.
Ideal Body Weight =50kg+2.3* (height-60)

Another formula used in Ideal Body Weight Calculator app is Robinson formula. This formula is appropriate for women. So, we use this formula to calculate the IBW for women.
Ideal Body Weight=49kg+1.7*(height-60)

It is noted that these formulas are appropriate for people who are taller than 5 feet or 60 inches.

For the user interface, we need two TextView, one CheckBox, one EditText, one Spinner, and one Button. The first TextView displays the question text "Are you male?". Next to this TextView, it is a CheckBox view. The CheckBox displays "Yes" next to the tick mark. The user answers the question by selecting or unselecting the CheckBox. We use this CheckBox to determine the sex of the user.

The EditText view is used to allow height input. This value can be in inch or centimeter. The user can specify the measurement of the height value by choosing in or cm from the Spinner view next to this EditView. The file applies layout to the spinner is called spinner_style.xml. This file locates in res/layout directory. Here is the content of the spinner_style.xml file.

<?xml version="1.0" encoding="utf-8"?>
<CheckedTextView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@android:id/text1"
style="?android:attr/spinnerDropDownItemStyle"
android:singleLine="true"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:ellipsize="marquee" />

The last TextView is used to display a note to the user regarding the limitation of the two formulas.
The Button view displays the text "Calculate". When the Calculate button is pushed, the calculateIBW method is called to perform the calculation. The android:onClick property links the Button view and the calculateIBW method. This method is defined in the MainActivity.java file. The background of the Button view is defined by a drawable resource that is written in the bt_style.xml file. The bt_style.xml file is stored in res/drawable directory. Here is the content of the bt_style.xml file:
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_pressed="true" >
<shape>
<solid
android:color="#ff00ff" />
<stroke
android:width="1dp"
android:color="#171717" />
<corners
android:radius="3dp" />
<padding
android:left="10dp"
android:top="10dp"
android:right="10dp"
android:bottom="10dp" />
</shape>
</item>
<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>

Ideal Body Weight Calculator Interface


The activity_main.xml file that is the source for IBWC app user interface can be written 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/bgimage">

<LinearLayout
android:layout_marginTop="20dp"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="horizontal" >

<TextView
android:id="@+id/txt_qa"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/txt_q"
/>
<CheckBox
android:id="@+id/ch_y"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/ch_y"

/>

</LinearLayout>

<LinearLayout
android:layout_marginTop="20dp"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="horizontal" >

<EditText
android:id="@+id/txt_height"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:hint="@string/hint_h"
android:inputType="numberDecimal" />
<Spinner
android:id="@+id/unit_spinner"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />

</LinearLayout>

<LinearLayout
android:layout_marginTop="20dp"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="horizontal" >
<Button
android:id="@+id/bt_calc"
android:background="@drawable/bt_style"
android:text="@string/bt_text"
android:layout_width="wrap_content"
android:layout_height="40dp"
android:layout_marginRight="20sp"
android:onClick="calculateIBW"
/>
<TextView
android:id="@+id/txt_result"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:textColor="#992211"
android:textSize="20sp"
/>
</LinearLayout>
<TextView
android:id="@+id/txt_note"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:textSize="20sp"
android:layout_marginTop="20sp"
android:text="@string/txt_note"
/>
</LinearLayout>

As you have seen in the picture above, the IBWC app has a background image. You can specify the background image of an Android app by using the android:background property of the top LinearLayout. In this app, the image that is used as the background when the activity is active is called bgimage that is stored in the res/drawable directory.
android:background="@drawable/bgimage"

Some string values used in the activity_main.xml are defined in the string.xmlf file. The content of the strings.xml file is shown below.
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name">IBWCalculator</string>
<string name="action_settings">Settings</string>
<string name="txt_q">Are you male?</string>
<string name="ch_y">Yes</string>
<string name="hint_h">Enter your height</string>
<string name="bt_text">Calculate</string>
<string name="txt_note">Note: This calculation is appropriate for people who are taller than 5 feet or 60 inches.</string>

</resources>

The last important file that you have to look and modify is MainActivity.java file. In this file, you need to add some methods such as setUpSpinnerData, getHeight, calculateIBW, and showAlert.

package com.example.ibwcalculator;
import java.text.DecimalFormat;
import java.text.NumberFormat;
import android.os.Bundle;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.view.Menu;
import android.view.View;
import android.widget.ArrayAdapter;
import android.widget.CheckBox;
import android.widget.EditText;
import android.widget.Spinner;
import android.widget.TextView;

public class MainActivity extends Activity {

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

    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 onStart(){
    super.onStart();
    setUpSpinnerData(); //populate data in spinner view
       
    }


    //This method will be invoked to setup data of the spinner view
    public void setUpSpinnerData(){
    Spinner sp=(Spinner)findViewById(R.id.unit_spinner);
    String[] items={"in","cm"};
    ArrayAdapter<String> aa=new ArrayAdapter<String>(this,R.layout.spinner_style,items);
    sp.setAdapter(aa);
   
    }
 
  //This method returns the height in inch
    public double getHeight(String strvalue){
    Spinner sp=(Spinner)findViewById(R.id.unit_spinner);    
   
    double value=Double.valueOf(strvalue);
    if(sp.getSelectedItem().toString().equals("cm")){
    return(value/2.54); //1inch=2.54cm
   
    }
    else {
    return(value);
    }
    }
   
 
 
    //This method calculates the IBW  
    public void calculateIBW(View view){
    double ibw=0.0f;
    CheckBox ch=(CheckBox)findViewById(R.id.ch_y);
    EditText txth=(EditText)findViewById(R.id.txt_height);
    String strvalue=txth.getText().toString();
    if(strvalue.length()>0){
    double height=getHeight(strvalue);    
    if(ch.isChecked()) //calculate ideal body weight for men
    ibw=50+2.3*(height-60);
    else //calculate ideal body weight for women
    ibw=49+1.7*(height-60);
    //create decimal formatter object to format the result
    NumberFormat nf=new DecimalFormat("###.00");    
    //display the result in txt_result
    TextView txtresult=(TextView)findViewById(R.id.txt_result);    
    txtresult.setText("IBW="+nf.format(ibw)+"kg");
   
    }
   
    else{
    showAlert();
    }
    }
 
 
     //This method will be invoked to display alert dialog
     public void showAlert(){
   
    AlertDialog.Builder builder = new AlertDialog.Builder(this);
         builder.setMessage("Please enter your height!");
         builder.setCancelable(true);
         builder.setPositiveButton("OK", new OkOnClickListener());
         AlertDialog dialog = builder.create();
         dialog.show();
   
     }
     private final class OkOnClickListener implements
     DialogInterface.OnClickListener {
      public void onClick(DialogInterface dialog, int which) {
      dialog.dismiss();
      }
     }

 
}


The setupSpinnerData is invoked when the app starts to populate entries of the spinner. These entries are in and cm. You specify the entries of the spinner in the ArrayAdapter object then set this object to the Spinner view by using its setAdapter method. When you create ArrayAdapter object from the ArrayAdapter class, you need to provides three values to constructor of the ArrayAdapter class. The first value is the context. You can use this for this value to refer to the current activity. The second value is the layout of the Spinner view. This layout is defined in the spinner_style.xml file as mentioned above. The last value is the array that contains entries of the spinner.
The getHeight method returns the height input in inch. This value will be used in the calculateIBW method to calculate the ideal body weight for the user.
The calculateIBW is invoked when the user pushed the Calculate button. This method calculate the ideal body weight of the user based on his/her sex. If the user is male (CheckBox is selected), the Davine formula is used. Otherwise, the Robinson formula is used.
The showAlert method is invoked to display a dialog to ask the user to enter his/her height in the EditText, txt_height. This alert dialog is displayed only when the user leaves the EditText, txt_height blank and pushes the Calculate button. In Android, the alert dialog box can be created by using the Builder class. This class is a sub-class of the AlertDialog class.

Testing ideal weight calculator

Download apk file of the Ideal Body Weight Calculator app Merge or Combine PDF, Txt, Images

Monday, September 9, 2013

Temperature Converter

In this post, you will learn to create a simple app to convert a temperature in Celsius to Fahrenheit and vise versa. Now you need to open Eclipse program and create a new project and name it as CtoFConverter. If you don't know how to create a project in Elipse, please read this guide Helloworld app.
For the app interface, we need two radiobuttons, one textbox, and one textview. See the picture below.


temperature converter


After creating the CtoFConverter project, please open the activity_main.xml file (in res/layout directory). This file is the source that defines views to be placed on the main activity. Views in Android can be said as components or controls in Java or C#. You need to modify the activity_main.xml file to include the two RadioButtons, one EditText, and one TextView views. So the activity_main.xml file is as shown below.

<?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="fill_parent"
android:orientation="vertical">
   >
<RadioGroup
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<RadioButton android:id="@+id/ctof_radio"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/ctof_radio"
android:onClick="onRadioButtonClicked"
android:checked="true"
/>
<RadioButton android:id="@+id/ftoc_radio"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/ftoc_radio"
android:onClick="onRadioButtonClicked"/>
</RadioGroup>

<EditText
    android:id="@+id/txt_input"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:hint="@string/hint_ctof"
    android:inputType="numberDecimal" />

<TextView
    android:id="@+id/txt_result"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:textColor="#992211"
    android:textSize="20sp"
/>
</LinearLayout>

The two RadioButtons are placed in the RadioGroup so that only one of them can be selected at a time. The first RadioButton is for Celsius to Fahrenheit conversion. In contrast, the second RadioButton is for Fahrenheit to Celsius conversion.
To define id property for each view, we use android:id property. The @ symbol tells the compiler to read text after the forward slash (/). By using the plus sign (+) before the id, you want to tell the compiler that this id is new and needs to be created. The width and height of a view can be defined by using android:layout_width and android:layout_height respectively. The wrap_content value of these properties specifies that the view wraps its content. The fill_parent value allows the view to be expanded to fit the screen. Besides, wrap_content and fill_parent, you can specify the width and height of the values in dp or sp unit.
To assign text to each RadioButton or other views that support text property, you need to use the android:text property. For the first RadioButton the value of the text property is taken from the string resource called ctof_radio. This string and other strings that are used for this interface construction are defined in strings.xml file (in res/values directory). You need to place @string before the name of the string to indicate that this string is defined in the strings.xml resource file.

This is the content of the strings.xml file used in this app:
<?xml version="1.0" encoding="utf-8"?>

<resources>

    <string name="app_name">CtoFConverter</string>
    <string name="action_settings">Settings</string>
    <string name="ctof_radio">Celsius to Fahrenheit</string>
    <string name="ftoc_radio">Fahrenheit to Celsius</string>
    <string name="hint_ctof">Enter celsius value</string>
    <string name="hint_ftoc">Enter fahrenheit value</string>

</resources>

The EditText view is a textbox that allows the user to type a number in Celsius or Fahrenheit. The inputType property specifies the input type of the textbox. The value of the input type can be one of the followings:
-number allows integer number input.
-numberDecimal allows floating-point number input.
-textPassword allows text input. The original text is kept. However, the password symbols are displayed instead of the actual characters.
-textMultiLine allows the text to expand to multiple lines.
-textCapWords displays a capital character at the beginning of each word.
-textCapSentences displays a capital character at the beginning of each sentence.
-date allows a value in date format input.
-time allows a value in time format input.
-datetime allows a combination of date and time input.

The last view that we use is TextView. The TextView view is used to display the result of the conversion. When the user types a value in the EditText view, the calculation is made to produce a result. The result is displayed in the TextView view.
The result can be a value in Celsius or Fahrenheit according to the RadioButtons selection. If the first RadioButton is selected, the user is prompted to enter a value in Celsius so that the conversion is from Celsius to Fahrenheit and the result is in Fahrenheit. Otherwise, the conversion is from Fahrenheit to Celsius so that the result is in Celsius.

All views are placed inside the LinearLayout. You can treat the LinearLayout in Andoid as a layout manager in Java. It is used to layout components in the app interface. The LinearLayout arranges the views in one orientation-vertical or horizontal. You can use its android:orientation property to specify vertical or horizontal orientation.

Receive click event from RadioButton

To perform an action when the RadioButton is clicked, you need to register to the click event to the RadioButton view by using its android:onClick property. The value of this property will be a method to perform the action. In this app, the method to perform the action when the RadioButon is clicked is onRadioButtonClicked. This method is defined in the MainActivity.java file. When the first RadioButton is selected, the hint text of the EditText changes to Enter Celsius value. If the second RadioButton is selected, the the hint text of the EditText changes to Enter Fahrenheit value.

MainActivity.java file
package com.example.ctofconverter;
import android.os.Bundle;
import android.app.Activity;
import android.text.Editable;
import android.text.TextWatcher;
import android.view.Menu;
import android.view.View;
import android.widget.EditText;
import android.widget.RadioButton;
import android.widget.TextView;

public class MainActivity extends Activity {

    private String strFrom="Celsius";
    private String strTo="Fahrenheit";
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }


    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 onStart(){
    super.onStart();
    EditText et=(EditText)findViewById(R.id.txt_input); //refer tot he EditText, txt_input
    et.addTextChangedListener(new ChangeListener()); //register the view with the text change listener
    et.setHint("Enter celsius value");
    }
 
    class ChangeListener implements TextWatcher{
   
    public void beforeTextChanged(CharSequence s, int start, int before, int count){
   
   
    }
   
    public void onTextChanged(CharSequence s, int start, int before, int count){
   
    convert(strFrom,strTo);  //do conversions  
    }
   
    public void afterTextChanged(Editable ed){
   
   
    }
    }
 
    public void convert(String from, String to){
    EditText et=(EditText) findViewById(R.id.txt_input);
    TextView tv=(TextView) findViewById(R.id.txt_result);
    double cvalue=0.0;
double fvalue=0.0;
    if(et.getText().toString().length()>0){
    if(from.equals("Celsius")) //convert from Celsius to Fahrenheit
    {
    cvalue=Double.parseDouble(et.getText().toString());
    fvalue=32+(cvalue*180.0/100.0);
    tv.setText(fvalue+to);
    }
    else{ //convert from Fahrenheit to Celsus
    fvalue=Double.parseDouble(et.getText().toString());
    cvalue=(fvalue-32)*100.0/180.0;
    tv.setText(cvalue+to);
    }
   
    }
   
    }
 
    public void onRadioButtonClicked(View view) {
    boolean checked = ((RadioButton) view).isChecked();
    EditText et=(EditText)findViewById(R.id.txt_input);

    switch(view.getId()) {
    case R.id.ctof_radio:
    if (checked){ //the first RadioButton is selected
    strFrom="Celsius";
    strTo="Fahrenheit";
    et.setHint("Enter celsius value");    
   
    }
    break;
    case R.id.ftoc_radio:
    if (checked){ //the second RadioButton is selected
    strFrom="Fahrenheit";
    strTo="Celsius";
    et.setHint("Enter fahrenheit value");
   
}
    break;
    }
    }
 
}

Refer a view in code

When you place views in the activity_main.xml file, the id of each view is recorded in the R.java file (in gen directory). When writing code for the main activity or other activities, referring to the views allows you to use the views. You can refer a view by using the findViewById(R.id.id_of_the_veiw) of the Activity class. For example, to refer the EditText, txt_input can be done by this line of code:

EditText et=(EditText)findViewById(R.id.txt_input);

Receive text change event

When the user types a value in the EditText, txt_input. This value immediately is calculated to produce a result. Then the result is displayed in the TextView, txt_result. The actions that convert and display the result happen when the user is making change to the value of EditText, txt_input. To perform an action when the text of EditText view is making changed, you need to register this view with the text change listener  by using the addTextChangedListener(TextWatcher tw) method. You will need a class to implement the TextWatcher interface. Then create an object of this class and supply it to the addTextChangedListener method. In this app, this class is called ChangeListener. You must implement all methods of the TextWatcher interface. These methods are beforeTextChanged, onTextChanged, and afterTextChanged.

doing temperature conversion when text change

compass app flashLight app

Do conversions

To convert a value input in the EditText, txt_input, to produce a result, the convert (String from, String to) is defined in the MainActivity.java file. This method will be placed in the onTextChanged method of ChangeListener class.
The conversion from Fahrenheit to Celsius value can be performed by using the formula below:
Celsius=(Fahrenheit-32)*100.0/180.0;
To convert from Fahrenheit to Celsius value, you can use the formula below:
Fahrenheit=32+(Celsius*180.0/100.0);

Merge or Combine PDF, Txt, Images