Pages

Showing posts with label ActionBar. Show all posts
Showing posts with label ActionBar. Show all posts

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