Android Open Source - NoteOut Main Activity






From Project

Back to project page NoteOut.

License

The source code is released under:

Apache License

If you think the Android project NoteOut listed in this page is inappropriate, such as containing malicious code/tools or violating the copyright, please email info at java2s dot com, thanks.

Java Source Code

package com.example.noteout;
/* w ww. j a  v  a  2s.  c om*/
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;

import android.app.Activity;
import android.app.AlertDialog;
import android.content.ActivityNotFoundException;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.media.MediaRecorder;
import android.net.Uri;
import android.os.Bundle;
import android.os.Environment;
import android.provider.MediaStore;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.widget.DrawerLayout;
import android.support.v7.app.ActionBar;
import android.support.v7.app.ActionBarActivity;
import android.text.Editable;
import android.text.TextWatcher;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.view.inputmethod.InputMethodManager;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.AdapterView.OnItemLongClickListener;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.CheckedTextView;
import android.widget.EditText;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;


public class MainActivity extends ActionBarActivity
        implements NavigationDrawerFragment.NavigationDrawerCallbacks {

  // Side bar Menu
    private NavigationDrawerFragment mNavigationDrawerFragment;
    
    private CharSequence mTitle;

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

        mNavigationDrawerFragment = (NavigationDrawerFragment)
                getSupportFragmentManager().findFragmentById(R.id.navigation_drawer);
        mTitle = getTitle();

        // Set up the drawer.
        mNavigationDrawerFragment.setUp(
                R.id.navigation_drawer,
                (DrawerLayout) findViewById(R.id.drawer_layout));
        
    }

    @Override
    public void onNavigationDrawerItemSelected(int position) {
        // update the main content by replacing fragments
        FragmentManager fragmentManager = getSupportFragmentManager();
        if (position == 0){
        fragmentManager.beginTransaction()
                        .replace(R.id.container, ShowNotesFragment.newInstance(position + 1))
                        .commit();
        }
        else  if (position == 1){
                    fragmentManager.beginTransaction()
                    .replace(R.id.container, MakeNotesFragment.newInstance(position + 1))
                    .commit();
        }
        else if(position == 2){
              fragmentManager.beginTransaction()
              .replace(R.id.container, DeleteNotesFragment.newInstance(position+1))
              .commit();
            
        }
          
    }
        
  
   // set up the title that has to be displayed 
    public  void onSectionAttached(int number) {
        switch (number) {
            case 1:
                mTitle = getString(R.string.title_section1);
                break;
            case 2:
                mTitle = getString(R.string.title_section2);
                break;
            case 3:
              mTitle = getString(R.string.title_section3);
           
        }
    }

    // Change the title in the action bar based on the fragment that is attached
    public void restoreActionBar() {
        ActionBar actionBar = getSupportActionBar();
        actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_STANDARD);
        actionBar.setTitle(mTitle);
    }


    // Change the action bar when sidebar menu is openened
    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        if (!mNavigationDrawerFragment.isDrawerOpen()) {
            getMenuInflater().inflate(R.menu.main, menu);
            restoreActionBar();
            return true;
        }
        return super.onCreateOptionsMenu(menu);
    }

    // Go to MakeNotes fragment when the user presses the new note icon on the actionbar
    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
      
      if (item.getItemId() == R.id.new_note){
        onNavigationDrawerItemSelected(1);
        onSectionAttached(2);
        restoreActionBar();
        return true;
      }
      return super.onOptionsItemSelected(item);
    }
   
    /**
     * Displays the available Notes in the external files Directory.
     *     Touch a filename to open in the viewer
     *     Touch and hold to share the notes
     */
    public static class ShowNotesFragment extends Fragment {
       
        private static final String ARG_SECTION_NUMBER = "section_number";
        private ListView dir;
       
        

        public static ShowNotesFragment newInstance(int sectionNumber) {
            ShowNotesFragment fragment = new ShowNotesFragment();
            Bundle args = new Bundle();
            args.putInt(ARG_SECTION_NUMBER, sectionNumber);
            fragment.setArguments(args);
            return fragment;
        }

        public ShowNotesFragment() {
        }

        @Override
        public View onCreateView(LayoutInflater inflater, ViewGroup container,
                Bundle savedInstanceState) {
            View rootView = inflater.inflate(R.layout.fragment_play_notes, container, false);
            dir = (ListView) rootView.findViewById(R.id.note_list);
            String[] values = null;
             File cwd = null;//Current Working directory
            Boolean can_open_files = false;
            if (Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())){
              cwd = getActivity().getExternalFilesDir(null);
              
              int no_files = cwd.listFiles().length;
              if (no_files != 0){
                can_open_files = true;
                values = new String [no_files];
                int i = 0;
                
                for(File f: cwd.listFiles()){
                      values[i++] = f.getName();
                  
                }
              }
              else{
                values = new String [1];
                  values[0] = "You Have no notes :(";
              }
              
            }
            
            if (values == null)
            {
              values = new String [1];
              values[0] = "External Storage is needed. Mount SDCARD :(";
            }
              
              ArrayAdapter<String> adapter = new ArrayAdapter<String>(getActivity(), R.layout.file_list,R.id.file_data,values);
                dir.setAdapter(adapter);
                if (can_open_files){
                  final String cwdCopy = cwd.getAbsolutePath();
                  dir.setOnItemClickListener(new OnItemClickListener() {

                  // Open the notes as a html file in the HTML viewer.
            @Override
            public void onItemClick(AdapterView<?> parent, View arg1,
                int position, long arg3) {
              String file = (String) dir.getItemAtPosition(position);
              Intent htmlViewer = new Intent(Intent.ACTION_VIEW);
              htmlViewer.setDataAndType(Uri.fromFile(new File(cwdCopy+File.separator+file+File.separator+file+".html")), "text/html");
              startActivity(htmlViewer);
              
            }
          });
                  dir.setOnItemLongClickListener(new OnItemLongClickListener() {

                    /*
                     * Each Notes is itself a folder containing - HTML file,images and recordings associated with that notes.
                     * To share a notes it is necessary to share the entire folder .
                     * Share each file on a seperate thread spwaned from the UI through bluetooth
                     * No need for AsyncTask because:
                     *       AsyncTask designed to run on a separate thread, all the while inform user on progress
                     *       Bluetooth informs user on its own,so we just spawned it on a separate thread.
                     */
            @Override
            public boolean onItemLongClick(AdapterView<?> parent,
                View arg1, int position, long arg3) {
              String file = (String) dir.getItemAtPosition(position);
              final File folder = new File(cwdCopy,file);
              for (final File f : folder.listFiles()){
                Runnable bluetoohSharer = new Runnable() {
                  @Override
                  public void run() {
                    String[] name_extension = f.getName().split("\\.");
                    Intent share = new Intent();
                    share.setAction(Intent.ACTION_SEND);
                    share.setPackage("com.android.bluetooth");
                    //Setting appropriate mimeTypes
                    if (name_extension[1].equals("html")){
                      share.setType("text/html");
                    }
                    else if (name_extension[1].equals("jpg")){
                      share.setType("image/*");
                    }
                    else if (name_extension[1].equals("mp3")){
                      share.setType("audio/*");
                    }
                    share.putExtra(Intent.EXTRA_STREAM, Uri.fromFile(f));
                    try{
                      startActivity(share);
                    }
                    catch(ActivityNotFoundException a){
                      Toast.makeText(getActivity(), "Something went wrong!Check data and try again",Toast.LENGTH_SHORT).show();
                    }
                    
                  }
                };
                // We realised very late that ANdroid doesnt have a well working bluetooth queue manager
                // so we just froze the UI thread to give enough time for the bluetooth request to atleast build send request properly
                bluetoohSharer.run();
                try {
                  Thread.sleep(10000);
                } catch (InterruptedException e) {
                  
                }
              }
              return true;
            }
          });
                }
            return rootView;
        }

        //Change title when the fragment becomes visible 
        @Override
        public void onAttach(Activity activity) {
            super.onAttach(activity);
            ((MainActivity) activity).onSectionAttached(
                    getArguments().getInt(ARG_SECTION_NUMBER));
        }
    }
    
    public static class DeleteNotesFragment extends Fragment {
        /**
         * To prevent cluttering of notes on the SD CARD, we have made it extremely simple and easy to delete notes.
         * By default all notes are chosen to be deleted,
         * unclick the notes you want to retain, and press the delete button
         */
        private static final String ARG_SECTION_NUMBER = "section_number";
        private ListView dir;
        public static boolean[] checked;
        private String[] values;
        private File cwd;
        
        @Override
        public void onCreate(Bundle savedInstance){
          super.onCreate(savedInstance);
          setRetainInstance(true);
          
        }
        

        public static DeleteNotesFragment newInstance(int sectionNumber) {
            DeleteNotesFragment fragment = new DeleteNotesFragment();
            Bundle args = new Bundle();
            args.putInt(ARG_SECTION_NUMBER, sectionNumber);
            fragment.setArguments(args);
            return fragment;
        }

        public DeleteNotesFragment() {
        }

        @Override
        public View onCreateView(LayoutInflater inflater, ViewGroup container,
                Bundle savedInstanceState) {
            View rootView = inflater.inflate(R.layout.fragment_delete_notes, container, false);
            dir = (ListView) rootView.findViewById(R.id.note_list_delete);
            
              cwd = null;//Current Working directory
            boolean open_file = false;
            if (Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())){
              cwd = getActivity().getExternalFilesDir(null);
              
              int no_files = cwd.listFiles().length;
              if (no_files != 0){
                open_file = true;
                values = new String [no_files];
                checked = new boolean [no_files];
                int i = 0;
                
                for(File f: cwd.listFiles()){
                      values[i++] = f.getName();
                      
                  
                }
              }
              else{
                values = new String [1];
                  values[0] = "You Have no notes :(";
              }
              
            }
            
            if (values == null)
            {
              values = new String [1];
              values[0] = "External Storage is needed. Mount SDCARD :(";
            }
              
              ArrayAdapter<String> adapter = new ArrayAdapter<String>(getActivity(), R.layout.file_list_delete,R.id.checkedname,values);
                dir.setAdapter(adapter);
                if (open_file){
                  dir.setOnItemClickListener(new OnItemClickListener() {
                    // findViewById is expensive. However the alternative wouldve been to maintain separate lists
                    // and update changes from the UI to that redundant list.
                    // Because in this case the user might be deleting all files a majority of the time, 
                    // it does seem like a logical pay off, especially when there are a lot of files and user wants to retain 
                    // very few of them (which will be the case most of the times) so it enhances UX
            @Override
            public void onItemClick(AdapterView<?> arg0, View rootView,
                int position, long arg3) {
              CheckedTextView check = (CheckedTextView)rootView.findViewById(R.id.checkedname);
              check.setChecked(!check.isChecked());
              DeleteNotesFragment.checked[position] = !DeleteNotesFragment.checked[position];
            }

            
          });
                  addListenerTodelete(rootView);
                
                }
            
           
            return rootView;
        }

        private void addListenerTodelete(View rootView) {
          Button delete = (Button)rootView.findViewById(R.id.deleteButton);
          delete.setOnClickListener(new OnClickListener() {
        //Altough it may seem like each note is a file,its a folder
            // SO we must empty the folder contents &
            // then delete the folder itself.
        @Override
        public void onClick(View arg0) {
          int i = 0;
          for (String filename: values){
            if (!(checked[i++])){
              File folder = new File(cwd,filename);
              if (folder.isDirectory()){
              for (File f:folder.listFiles()){
                
                f.delete();
              }
              }
              folder.delete();
            }
          }
          Toast.makeText(getActivity(), "Deleted files", Toast.LENGTH_SHORT).show();
          getActivity().getSupportFragmentManager().beginTransaction().replace(R.id.container, DeleteNotesFragment.newInstance(3)).commit();
        }
      });
    }


    @Override
        public void onAttach(Activity activity) {
            super.onAttach(activity);
            ((MainActivity) activity).onSectionAttached(
                    getArguments().getInt(ARG_SECTION_NUMBER));
        }
    }
    
    public static class MakeNotesFragment extends Fragment {
       
      
      //since we represent the note as a html file,making a lecture note is creating a html file.
      // the HTML tags are maintained on the heap as a stringbuilder (Which is mutable)
      // this is pushed as the contents of the file,
      //     -- if the user explicity requests a save
      //    -- the user creates data and forgets to save
        private static final String ARG_SECTION_NUMBER = "section_number";
        private StringBuilder html;
        private String filename;
        private String pureFilename;
        private EditText textData;
        //Static final strings are resolved at compile time
        private static final String H_INIT = "<html><body style=\"background:yellow;\"><center><h1 style='color:white;background:black;'>";
        private static final String P_TAG = "<p>";
        private static final String P_TAG_CLOSE = "</p><BR>";
        private static final String I_TAG = "<img src='";
        private static final String I_TAG_CLOSE = "' width=300 height=200 /img><BR>";
        private static final String AUDIO = "<audio controls><source src='";
        private static final String AUDIO_CLOSE = "' type='audio/mpeg'></audio><BR>";
        private static final String ENTER = "\n";
        private static final String H_CLOSE = "</h1><br>";
        private static  String APP_FILES_DIR;
        private static  char ch = 'a';// character appended to image and videofiles to differentiate when there are multiple files - adds a 26 file limitation
        private boolean canAddContent; //A mutex like variable ensuring that Text,Audio or Image input is not intefereing others'input. 
        private File imfile;
        private File audiofile;
        private InputMethodManager IMM;
        private boolean recording;
        private static  int empty_indicator;
        private MediaRecorder recorder;
        private TextView tellUser;
                
        public static MakeNotesFragment newInstance(int sectionNumber) {
          MakeNotesFragment fragment = new MakeNotesFragment();
            Bundle args = new Bundle();
            args.putInt(ARG_SECTION_NUMBER, sectionNumber);
            fragment.setArguments(args);
            return fragment;
            
        }

        
       
        public MakeNotesFragment() {
          html = new StringBuilder(H_INIT);
        }
        
        @Override
        public void onCreate(Bundle savedInstance){
          super.onCreate(savedInstance);
          
        }
        
        
        @Override
        public View onCreateView(LayoutInflater inflater, ViewGroup container,
                Bundle savedInstanceState) {
            View rootView = inflater.inflate(R.layout.fragment_create_notes, container, false);
            try{
              APP_FILES_DIR = getActivity().getExternalFilesDir(null).getAbsolutePath();
            }
            catch(Exception e){
              Toast.makeText(getActivity(), "Mount SD CARD!", Toast.LENGTH_LONG).show();
              getActivity().finish();
            }
            addButtonListeners(rootView);
            //Softkeypad Input 
            IMM = (InputMethodManager)getActivity().getSystemService(Context.INPUT_METHOD_SERVICE);
            hideInputFields(rootView);
             askFileName();
            canAddContent = true;
            return rootView;
        }
        
        // Every new note gets a new folder granted the filename doesn't have an extension 
        //         monkey runner came up with a way set filename to x.html | x.mp3 | x.jpg 
        
        private void makeNewFolder(){
          if(!filename.contains(".")){
          File folder = new File(APP_FILES_DIR, filename);
          folder.mkdir();
          pureFilename = filename;
          filename+=File.separator+filename;
          }
          
        }

        // When the user presses the Audio button again the voice data is dumped to a file, and the html page will include this voice dump
    private void stopRecording() {
        if(recorder != null){
           try{
             recorder.stop();
             recorder.release();
             recorder = null;
             recording = !recording;
             tellUser.setVisibility(View.GONE);
             Toast.makeText(getActivity(), "Added your recording",Toast.LENGTH_SHORT ).show();
             html.append(AUDIO+pureFilename+(ch++)+".mp3"+AUDIO_CLOSE);
             canAddContent = true;
           }
           catch(IllegalStateException e){
             recorder=null;
             Toast.makeText(getActivity(), "Your Mic is having an issue! closing the application for now", Toast.LENGTH_SHORT).show();
             getActivity().finish();
           }
        }
    }
    
    
    //Start recording voice 
    private void startRecording(){
      
      recorder = new MediaRecorder();
          recorder.setAudioSource(MediaRecorder.AudioSource.MIC);
          recorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4);
          audiofile = new File(APP_FILES_DIR, filename+ch+".mp3");
          recorder.setOutputFile(audiofile.getAbsolutePath());
          recorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);
          try{
            recorder.prepare();
            recorder.start();
            recording = true;
            tellUser.setVisibility(View.VISIBLE);
          }
          catch (IOException e){
            Toast.makeText(getActivity(), e.getMessage(), Toast.LENGTH_SHORT).show();
          }
    }
    
    @Override
    public void onStop(){
      if (recorder != null){
        recorder.stop();
        recorder.release();
        recorder = null;
      }
      
      if (filename != null){ 
        if (!filename.isEmpty()){  //Valid filename ==> folder exists
          if(!storeFile(false)){  // if we couldnt save the html file then no point in having this folder as there are no notes
            deleteFolder();     //delete the folder we had already created
          }
        }
      }
      super.onStop();
    }

    // The app functions under the assumption that the user will put data into the note and creates a folder for every note 
    // If the user doesnt put data, then we must delete this extra folder
    // If there was an IOException while saving-
    //      then we have lost user data, the folder simply exists without any data so we delete it at that time as well
    private void deleteFolder() {
      try{
      File folder = new File(APP_FILES_DIR,filename.split(File.separator)[0]);
      if (folder.list().length == 0){
        folder.delete();
      }
      }
      catch(Exception e){
        Toast.makeText(getActivity(), "You have corrupted some notes! Please delete the notes and restart the app",Toast.LENGTH_SHORT).show();
      }
    }



    private boolean storeFile(boolean displayAfterSaving){
      // if the user pressed SAVE - then we must open it for viewing
      // if the app itself pushed data don't show preview
      //  this happend - when the user went away from the app
      boolean success = false;
      if (html.length() != empty_indicator){ // if there is data
        File file = new File(APP_FILES_DIR, filename+".html");
        try{
          OutputStream os = new FileOutputStream(file);
          os.write(html.toString().getBytes());
          os.close(); // Write to file
          if (displayAfterSaving){ 
            Intent htmlViewer = new Intent(Intent.ACTION_VIEW);
            htmlViewer.setDataAndType(Uri.fromFile(file), "text/html");
            startActivity(htmlViewer);
          }
          success = true;
        }
        catch(IOException e){
          Toast.makeText(getActivity(), e.getLocalizedMessage(), Toast.LENGTH_SHORT).show();
        }
      }
      return success;
        
    }
    private void hideInputFields(View rootView) {
      // Theres a hiddden textbox in the center of the screen. when the user wants to add text its displayed.
      // WHen user presses enter key, its removed 
      textData = (EditText) rootView.findViewById(R.id.textData);
      textData.setVisibility(View.GONE);
      textData.addTextChangedListener(new TextWatcher() {
        
        @Override
        public void onTextChanged(CharSequence s, int start, int before, int count) {
          if (s.toString().contains(ENTER)){
            html.append(P_TAG+textData.getText()+P_TAG_CLOSE);
            textData.setText("");
            IMM.hideSoftInputFromWindow(textData.getApplicationWindowToken(), InputMethodManager.HIDE_IMPLICIT_ONLY);
            textData.setVisibility(View.GONE);
            textData.clearFocus();
            canAddContent = true;
            
          }
        }
        
        @Override
        public void beforeTextChanged(CharSequence s, int start, int count,
            int after) {
        }
        
        @Override
        public void afterTextChanged(Editable s) {
        }
      });
      tellUser = (TextView) rootView.findViewById(R.id.audioTellUser);
      tellUser.setVisibility(View.GONE);
    }

    private void addButtonListeners(View root) {
      Button save = (Button) root.findViewById(R.id.saveFile);
      save.setOnClickListener(new OnClickListener()
         {
               @Override
               public void onClick(View v)
               {
                 // THis button is not shown during typing, and is not on screen when taking an image.
                 // so we make sure audio input doesnt interfere here
                 if (!recording){
                  storeFile(true);
                 }
               }
               
         }); 
      
      Button text = (Button) root.findViewById(R.id.textInput);
      text.setOnClickListener(new OnClickListener()
         {
               @Override
               public void onClick(View v)
               {
                 //Show the text box and the keyboard
                 if (canAddContent && !recording){
                   textData.setVisibility(View.VISIBLE);
                   textData.requestFocus();
                   IMM.showSoftInput(textData, InputMethodManager.SHOW_IMPLICIT);
                   canAddContent = false;
                 }
               } 
         }); 
      Button image = (Button) root.findViewById(R.id.picImage);
      image.setOnClickListener(new OnClickListener()
         {
               @Override
               public void onClick(View v)
               {
                 // open the camera and let user take a pic
                 if (canAddContent && !recording ){
                  if ( html.length() == empty_indicator){
                    Toast.makeText(getActivity(), "You must add some text first!", Toast.LENGTH_SHORT).show();
                  }
                  else{
                  Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
                  imfile = new File(APP_FILES_DIR, filename+ch+".jpg");
                  Uri outputFileUri = Uri.fromFile(imfile);
                  intent.putExtra(MediaStore.EXTRA_OUTPUT, outputFileUri);
                  startActivityForResult(intent, 1);
                  canAddContent = false;
                  }
                 }
                  
               } 
         }); 
      Button audio = (Button) root.findViewById(R.id.voiceData);
      audio.setOnClickListener(new OnClickListener() {
        
        @Override
        public void onClick(View arg0) {
          
            // Either open the mic and start recording or close the mic and save voice data
            if (recording){
              stopRecording();
            }
            else{
              startRecording();
            }
        }
      });
    }
    

    // When the camera finishes,see if the user selected an image and add that to the note
    @Override
    public void onActivityResult(int requestCode, int resultCode, Intent data) { 
            if (resultCode == RESULT_OK) {
               html.append(I_TAG+pureFilename+(ch++)+".jpg"+I_TAG_CLOSE);
               Toast.makeText(getActivity(), "Image Added", Toast.LENGTH_SHORT).show();
            }
            
            canAddContent = true;
    }

    //Every file associated with a note needs its name
    // Ask this at the very beggining
    private void askFileName() {
      AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
      builder.setTitle("New Notes name");
      builder.setMessage("What would you like to name this lecture notes as?");
      final EditText name = new EditText(getActivity());
      builder.setView(name);
      builder.setPositiveButton("Create Notes", new DialogInterface.OnClickListener() {
        
        @Override
        public void onClick(DialogInterface dialog, int which) {
         filename = name.getText().toString();
         addMoreInfo();
        }
      } );
      builder.setNegativeButton("Cancel", new DialogInterface.OnClickListener(){

        @Override
        public void onClick(DialogInterface arg0, int arg1) {
          filename = "untitledNotes";
          addMoreInfo();
        }
        
      });
      builder.setCancelable(false); //Prevents user from clicking elsewhere and getting out of the dialog box
      builder.show();
    }
    
    //set up the folder for the note
    private void addMoreInfo(){
       html.append(filename).append(H_CLOSE);
       empty_indicator = html.length();
       makeNewFolder();
        
    }

    @Override
        public void onAttach(Activity activity) {
            super.onAttach(activity);
            ((MainActivity) activity).onSectionAttached(
                    getArguments().getInt(ARG_SECTION_NUMBER));
        }
    }

}




Java Source Code List

com.example.noteout.MainActivity.java
com.example.noteout.NavigationDrawerFragment.java