Back to project page NoteOut.
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.
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)); } } }