Click here to Skip to main content
15,867,308 members
Articles / Mobile Apps / Android

Simple Notepad for Android

Rate me:
Please Sign up or sign in to vote.
4.68/5 (31 votes)
21 Aug 2014CPOL2 min read 142.5K   34.1K   43   21
A simple notepad for Android

My development environment is jdk1.7, android 4.2, and eclipse 4.2.1 (Juno). It's the first Android application I created. Precisely, I created it referring to the sample that Android provides in the Android SDK and the Notepad lesson that Android developers have provided. For more details about the Notepad lesson, here's the link.

** 2013/4/1 - For those who are having bugs on return from NoteEdit activity to NoteList activity. Tried to download again for update to new version. I have updated the latest version to link. Before debugging the latest version to Android, the old version must be uninstalled first.

Here's how it looks like:

Left side is the note list and the right side is the note editor. It's not really difficult to write this program if you have certain experience to Android apps program. Basically, what I have used on this program mostly is fundamentally things like intent, SQLite database, options menu, list activity and of course life-cycle. If you are already tried the Notepad exercise that I mentioned above, basically you can understand what I'm writing about.

I'm going to show what I have written on this project.

Here's the Java code for note list:

Java
package com.example.note;
import android.os.Bundle;
import android.app.AlertDialog;
import android.app.ListActivity;
import android.content.DialogInterface;
import android.content.Intent;
import android.database.Cursor;
import android.view.ContextMenu;
import android.view.ContextMenu.ContextMenuInfo;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.Button;
import android.widget.ListView;
import android.widget.SimpleCursorAdapter;
import android.widget.TextView;
import android.widget.AdapterView.AdapterContextMenuInfo;

public class NoteList extends ListActivity { 
 
        private static final int DELETE_ID = Menu.FIRST;
 private int mNoteNumber = 1;
 
 private NotesDbAdapter mDbHelper;
  
 @Override
 protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.notelist);
  mDbHelper = new NotesDbAdapter (this);
  mDbHelper.open();
  fillData();    
  registerForContextMenu(getListView());
  Button addnote = (Button)findViewById(R.id.addnotebutton);
  addnote.setOnClickListener(new View.OnClickListener() {  
   @Override
   public void onClick(View v) {
    createNote();
    }
  });  
 }
 
 @Override
 public boolean onCreateOptionsMenu(Menu menu) {
  // Inflate the menu; this adds items to the action bar if it is present.
  getMenuInflater().inflate(R.menu.notelist_menu, menu);
  return true;  
 }
 
 @Override
 public boolean onOptionsItemSelected(MenuItem item) {
     switch (item.getItemId()) {
     case R.id.menu_about:
           
            AlertDialog.Builder dialog = new AlertDialog.Builder(NoteList.this);
            dialog.setTitle("About");
            dialog.setMessage("Hello! I'm Heng, the creator of this application. 
              This application is created based on learning." +
              " Used it on trading or any others activity that is related 
              to business is strictly forbidden."
              +"If there is any bug is found please freely e-mail me. "+
               "\n\tedisonthk@gmail.com"
              );
            dialog.setPositiveButton("OK", new DialogInterface.OnClickListener() {
    
             @Override
             public void onClick(DialogInterface dialog, int which) {
              dialog.cancel();
     
             }
            });
            dialog.show();            
            return true;
         
         default:
             return super.onOptionsItemSelected(item);
         }
     }
 
 private void createNote() {
  Intent i = new Intent(this, NoteEdit.class);
        startActivityForResult(i, ACTIVITY_CREATE);     
    }
 
    @Override
    protected void onListItemClick(ListView l, View v, int position, long id) {
        super.onListItemClick(l, v, position, id);
        Intent i = new Intent(this, NoteEdit.class);
        i.putExtra(NotesDbAdapter.KEY_ROWID, id);
        startActivityForResult(i, ACTIVITY_EDIT);
    }

 private void fillData() {
        // Get all of the notes from the database and create the item list
        Cursor notesCursor = mDbHelper.fetchAllNotes();
        startManagingCursor(notesCursor);
        

        String[] from = new String[] { NotesDbAdapter.KEY_TITLE ,NotesDbAdapter.KEY_DATE};
        int[] to = new int[] { R.id.text1 ,R.id.date_row};
        
        // Now create an array adapter and set it to display using our row
        SimpleCursorAdapter notes =
            new SimpleCursorAdapter(this, R.layout.notes_row, notesCursor, from, to);
        setListAdapter(notes);
    }
 
    @Override
    public void onCreateContextMenu(ContextMenu menu, View v,
            ContextMenuInfo menuInfo) {
        super.onCreateContextMenu(menu, v, menuInfo);
        menu.add(0, DELETE_ID, 0, R.string.menu_delete);
    }
    
    @Override
    public boolean onContextItemSelected(MenuItem item) {
        switch(item.getItemId()) {
            case DELETE_ID:
                AdapterContextMenuInfo info = (AdapterContextMenuInfo) item.getMenuInfo();
                mDbHelper.deleteNote(info.id);
                fillData();
                return true;
        }
        return super.onContextItemSelected(item);
    }
    
    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent intent) {
        super.onActivityResult(requestCode, resultCode, intent);
        fillData();        
    }    
}

Here's the XML code for note list:

XML
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@drawable/notelist">
   
    <TextView
        android:id="@+id/textViewTop"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:layout_alignParentTop="true"
        android:paddingBottom="35dp" />

    <ListView
        android:id="@android:id/list"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_above="@+id/btmLayout"
        android:layout_below="@+id/textViewTop"
        android:background="#FFF9C8"
        android:divider="#D3D3D3"
        android:dividerHeight="1sp" 
        android:footerDividersEnabled="true" />

    <LinearLayout
        android:id="@+id/btmLayout"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:layout_gravity="center_horizontal"
        android:gravity="center"        
        android:layout_alignParentBottom="true"
        android:layout_marginBottom="3dp"
        android:layout_marginTop="10dp">

     <Button
         android:id="@+id/addnotebutton"
         android:layout_width="50dp"
         android:layout_height="wrap_content"
         android:background="@drawable/create_note" />

    </LinearLayout>    
     
</RelativeLayout>

The name written on background for instance notelist and create_note is the png image files. You can download it at the top of this blog.

Here's the XML code for list row:

XML
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    
 <TextView
     android:layout_width="wrap_content"
     android:layout_height="wrap_content"
     android:layout_alignParentLeft="true"
     android:layout_marginLeft="10sp"
     android:textSize="25sp"
     android:hint="@string/no_title"
     android:id="@+id/text1"/>
 
 <TextView 
     android:layout_width="wrap_content"
     android:layout_height="wrap_content"
     android:textSize = "25sp"
     android:id="@+id/date_row"     
     android:layout_alignParentRight="true"     
     android:layout_marginRight="10sp"/>
</RelativeLayout>

Here's the Java code for note edit:

Java
package com.example.note;
import java.text.SimpleDateFormat;
import java.util.Date;

import android.app.Activity;
import android.app.AlertDialog;
import android.content.ContentValues;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.database.Cursor;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.os.Bundle;
import android.util.AttributeSet;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;

    public class NoteEdit extends Activity {
    public static int numTitle = 1; 
    public static String curDate = "";
    public static String curText = ""; 
    private EditText mTitleText;
    private EditText mBodyText;
    private TextView mDateText;
    private Long mRowId;

    private Cursor note;

    private NotesDbAdapter mDbHelper;
    
    public static class LineEditText extends EditText{
  // we need this constructor for LayoutInflater
  public LineEditText(Context context, AttributeSet attrs) {
   super(context, attrs);
    mRect = new Rect();
          mPaint = new Paint();
          mPaint.setStyle(Paint.Style.FILL_AND_STROKE);
          mPaint.setColor(Color.BLUE);
  }

  private Rect mRect;
     private Paint mPaint;     
     
     @Override
     protected void onDraw(Canvas canvas) {
   
         int height = getHeight();
         int line_height = getLineHeight();

         int count = height / line_height;

         if (getLineCount() > count)
             count = getLineCount();

         Rect r = mRect;
         Paint paint = mPaint;
         int baseline = getLineBounds(0, r);

         for (int i = 0; i < count; i++) {

             canvas.drawLine(r.left, baseline + 1, r.right, baseline + 1, paint);
             baseline += getLineHeight();

         super.onDraw(canvas);
     }
 }
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        
        mDbHelper = new NotesDbAdapter(this);
        mDbHelper.open();        
        
        setContentView(R.layout.note_edit);
        setTitle(R.string.app_name);

        mTitleText = (EditText) findViewById(R.id.title);
        mBodyText = (EditText) findViewById(R.id.body);
        mDateText = (TextView) findViewById(R.id.notelist_date);

        long msTime = System.currentTimeMillis();  
        Date curDateTime = new Date(msTime);
  
        SimpleDateFormat formatter = new SimpleDateFormat("d'/'M'/'y");  
        curDate = formatter.format(curDateTime);        
        
        mDateText.setText(""+curDate);
        

        mRowId = (savedInstanceState == null) ? null :
            (Long) savedInstanceState.getSerializable(NotesDbAdapter.KEY_ROWID);
        if (mRowId == null) {
            Bundle extras = getIntent().getExtras();
            mRowId = extras != null ? extras.getLong(NotesDbAdapter.KEY_ROWID)
                                    : null;
        }

        populateFields();    
    } 
    
    @Override
    protected void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        saveState();
        outState.putSerializable(NotesDbAdapter.KEY_ROWID, mRowId);
    }
    
    @Override
    protected void onPause() {
        super.onPause();
        saveState();
    }
    
    @Override
    protected void onResume() {
        super.onResume();
        populateFields();
    }
    
 @Override
 public boolean onCreateOptionsMenu(Menu menu) {
  // Inflate the menu; this adds items to the action bar if it is present.
  getMenuInflater().inflate(R.menu.noteedit_menu, menu);
  return true;  
 }
 
 @Override
 public boolean onOptionsItemSelected(MenuItem item) {
     switch (item.getItemId()) {
     case R.id.menu_about:
           
      /* Here is the introduce about myself */      
         AlertDialog.Builder dialog = new AlertDialog.Builder(NoteEdit.this);
         dialog.setTitle("About");
         dialog.setMessage("Hello! I'm Heng, the creator of this application. 
              This application is created for learning." +
              " Using it on trading or any others activity that is related 
              to business is strictly forbidden."
              +"If there is any bug is found please freely e-mail me. "+
               "\n\tedisonthk@gmail.com"
              );
         dialog.setPositiveButton("OK", new DialogInterface.OnClickListener() {
    
             @Override
             public void onClick(DialogInterface dialog, int which) {
              dialog.cancel();
     
             }
            });
            dialog.show();            
            return true;
     case R.id.menu_delete:
   if(note != null){
       note.close();
       note = null;
      }
      if(mRowId != null){
       mDbHelper.deleteNote(mRowId);
      }
      finish();
      
         return true;
     case R.id.menu_save:
      saveState();
      finish();      
     default:
      return super.onOptionsItemSelected(item);
     }
 }
    
    private void saveState() {
        String title = mTitleText.getText().toString();
        String body = mBodyText.getText().toString();

        if(mRowId == null){     
         long id = mDbHelper.createNote(title, body, curDate);
         if(id > 0){ mRowId = id; }
        }else{
         mDbHelper.updateNote(mRowId, title, body, curDate);
        }
    }    
  
    private void populateFields() {
        if (mRowId != null) {
            note = mDbHelper.fetchNote(mRowId);
            startManagingCursor(note);
            mTitleText.setText(note.getString(
                 note.getColumnIndexOrThrow(NotesDbAdapter.KEY_TITLE)));
            mBodyText.setText(note.getString(
                    note.getColumnIndexOrThrow(NotesDbAdapter.KEY_BODY)));
            curText = note.getString(
                    note.getColumnIndexOrThrow(NotesDbAdapter.KEY_BODY));
        }
    }
}

Here's the XML code for note edit:

XML
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
 android:layout_width="match_parent"
 android:layout_height="match_parent"
 android:background="#FFF9C8">
         
 <RelativeLayout
     android:id="@+id/toplayout"
     android:background="@drawable/notetop"
     android:layout_width="fill_parent"
     android:layout_height="wrap_content"
     android:layout_alignParentTop="true"
     android:paddingBottom="5dp"
     android:paddingLeft="5dp"
     android:paddingTop="5dp" >
  
  <TextView android:layout_width="wrap_content"
   android:layout_height="wrap_content"
   android:textSize="18sp" 
   android:text="@string/title"
   android:id="@+id/title_text1" />     
  <EditText android:id="@+id/title"
   android:layout_width="wrap_content"
   android:layout_height="wrap_content"
   android:textSize="18sp"    
      android:hint="@string/no_title"
      android:layout_toRightOf="@+id/title_text1" 
      android:background="@android:color/transparent"  
      android:layout_marginLeft="5dp" 
   android:singleLine="true"
   android:imeOptions="actionNext"/>
  <TextView
      android:id="@+id/notelist_date"
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:layout_alignParentRight="true"       
      android:paddingRight="10sp"       
      android:textSize="18sp" />  
 </RelativeLayout>

 <view
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:id="@+id/body"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
     android:layout_below="@+id/toplayout"
     class="com.example.note.NoteEdit$LineEditText"
     android:background="@android:color/transparent"
     android:capitalize="sentences"
     android:fadingEdge="vertical"
     android:gravity="top"
     android:padding="5dp"
     android:scrollbars="vertical"
     android:textSize="22sp" />

</RelativeLayout>

Here's the Java code for note adapters that is provided by Android developers. It's almost the same but I did some edit on it that I added the one more parameter for date.

Java
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.SQLException;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.util.Log;

/**
 * Simple notes database access helper class. Defines the basic CRUD operations
 * for the notepad example, and gives the ability to list all notes as well as
 * retrieve or modify a specific note.
 * 
 * This has been improved from the first version of this tutorial through the
 * addition of better error handling and also using returning a Cursor instead
 * of using a collection of inner classes (which is less scalable and not
 * recommended).
 */

/* It is not original code that notepad exercise provided.
 * 
 * I did some edit on this code by adding date parameter on it.  
 */

public class NotesDbAdapter {

    public static final String KEY_TITLE = "title";
    public static final String KEY_DATE = "date";
    public static final String KEY_BODY = "body";
    public static final String KEY_ROWID = "_id";

    private static final String TAG = "NotesDbAdapter";
    private DatabaseHelper mDbHelper;
    private SQLiteDatabase mDb;

    /**
     * Database creation sql statement
     */
    private static final String DATABASE_CREATE =
        "create table notes (_id integer primary key autoincrement, "
        + "title text not null, body text not null, date text not null);";

    private static final String DATABASE_NAME = "data";
    private static final String DATABASE_TABLE = "notes";
    private static final int DATABASE_VERSION = 2;

    private final Context mCtx;

    private static class DatabaseHelper extends SQLiteOpenHelper {

        DatabaseHelper(Context context) {
            super(context, DATABASE_NAME, null, DATABASE_VERSION);
        }

        @Override
        public void onCreate(SQLiteDatabase db) {

            db.execSQL(DATABASE_CREATE);
        }

        @Override
        public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
            Log.w(TAG, "Upgrading database from version " + oldVersion + " to "
                    + newVersion + ", which will destroy all old data");
            db.execSQL("DROP TABLE IF EXISTS notes");
            onCreate(db);
        }
    }

    /**
     * Constructor - takes the context to allow the database to be
     * opened/created
     * 
     * @param ctx the Context within which to work
     */
    public NotesDbAdapter(Context ctx) {
        this.mCtx = ctx;
    }

    /**
     * Open the notes database. If it cannot be opened, try to create a new
     * instance of the database. If it cannot be created, throw an exception to
     * signal the failure
     * 
     * @return this (self reference, allowing this to be chained in an
     *         initialization call)
     * @throws SQLException if the database could be neither opened or created
     */
    public NotesDbAdapter open() throws SQLException {
        mDbHelper = new DatabaseHelper(mCtx);
        mDb = mDbHelper.getWritableDatabase();
        return this;
    }

    public void close() {
        mDbHelper.close();
    }

    /**
     * Create a new note using the title and body provided. If the note is
     * successfully created return the new rowId for that note, otherwise return
     * a -1 to indicate failure.
     * 
     * @param title the title of the note
     * @param body the body of the note
     * @return rowId or -1 if failed
     */
    public long createNote(String title, String body, String date) {
        ContentValues initialValues = new ContentValues();
        initialValues.put(KEY_TITLE, title);
        initialValues.put(KEY_BODY, body);
        initialValues.put(KEY_DATE, date);

        return mDb.insert(DATABASE_TABLE, null, initialValues);
    }

    /**
     * Delete the note with the given rowId
     * 
     * @param rowId id of note to delete
     * @return true if deleted, false otherwise
     */
    public boolean deleteNote(long rowId) {

        return mDb.delete(DATABASE_TABLE, KEY_ROWID + "=" + rowId, null) > 0;
    }

    /**
     * Return a Cursor over the list of all notes in the database
     * 
     * @return Cursor over all notes
     */
    public Cursor fetchAllNotes() {

        return mDb.query(DATABASE_TABLE, new String[] {KEY_ROWID, KEY_TITLE,
                KEY_BODY,KEY_DATE}, null, null, null, null, null);
    }

    /**
     * Return a Cursor positioned at the note that matches the given rowId
     * 
     * @param rowId id of note to retrieve
     * @return Cursor positioned to matching note, if found
     * @throws SQLException if note could not be found/retrieved
     */
    public Cursor fetchNote(long rowId) throws SQLException {

        Cursor mCursor =

            mDb.query(true, DATABASE_TABLE, new String[] {KEY_ROWID,
                    KEY_TITLE, KEY_BODY,KEY_DATE}, KEY_ROWID + "=" + rowId, null,
                    null, null, null, null);
        if (mCursor != null) {
            mCursor.moveToFirst();
        }
        return mCursor;
    }

    /**
     * Update the note using the details provided. The note to be updated is
     * specified using the rowId, and it is altered to use the title and body
     * values passed in
     * 
     * @param rowId id of note to update
     * @param title value to set note title to
     * @param body value to set note body to
     * @return true if the note was successfully updated, false otherwise
     */
    public boolean updateNote(long rowId, String title, String body,String date) {
        ContentValues args = new ContentValues();
        args.put(KEY_TITLE, title);
        args.put(KEY_BODY, body);
        
        //This lines is added for personal reason
        args.put(KEY_DATE, date);
        
        //One more parameter is added for data
        return mDb.update(DATABASE_TABLE, args, KEY_ROWID + "=" + rowId, null) > 0;
    }
}

Here's the manisfest code for this project:

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

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

    <application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
        <activity
            android:name="com.example.note.NoteList"
            android:label="@string/app_name" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <activity android:name="com.example.note
.NoteEdit"
            android:label="@string/app_name"
            android:windowSoftInputMode="adjustUnspecified"/>
    </application>

</manifest>

The code above shown is not completely all of this project. There are some files I didn't put on here as I want to show the main files of this project.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Japan Japan
Hi! Thank you everyone who reading my article. My major is electronic and programming. Right now I am doing foreign study at Japan. I will like sharing to everyone with my works and if you do interesting with my works, please leave you comment on my blog. Any comments is welcoming.

Comments and Discussions

 
QuestionPlease provide me the APK Pin
Member 1330403711-Jul-17 21:30
Member 1330403711-Jul-17 21:30 
QuestionThank you so much!!! Pin
Member 1280693021-Oct-16 2:02
Member 1280693021-Oct-16 2:02 
QuestionSo helpful Pin
Renz Tuazon11-Oct-16 1:06
Renz Tuazon11-Oct-16 1:06 
QuestionAre there any copyright issues that are associated with this application? Pin
Member 122433737-Jan-16 12:16
Member 122433737-Jan-16 12:16 
AnswerGood and Helpfull Pin
Member 1178766023-Jun-15 16:16
Member 1178766023-Jun-15 16:16 
QuestionQuestion Pin
Member 1130299110-Dec-14 23:19
Member 1130299110-Dec-14 23:19 
Questionbug Pin
Member 6460703-Sep-14 3:03
Member 6460703-Sep-14 3:03 
SuggestionNice project, but I'm missing some explanations Pin
Andromeda Shun22-Aug-14 1:58
Andromeda Shun22-Aug-14 1:58 
GeneralRe: Nice project, but I'm missing some explanations Pin
Edison Heng22-Aug-14 14:15
Edison Heng22-Aug-14 14:15 
GeneralAwsome!! Pin
Member 1045954711-Dec-13 0:25
Member 1045954711-Dec-13 0:25 
GeneralThanks for your acknowledge Pin
Member 103212857-Oct-13 10:14
Member 103212857-Oct-13 10:14 
Questionview Pin
Swagger Yusuf13-Jun-13 11:58
Swagger Yusuf13-Jun-13 11:58 
GeneralMy vote of 5 Pin
lee skyhr3-Jun-13 2:50
lee skyhr3-Jun-13 2:50 
GeneralMy vote of 4 Pin
Member 816675911-May-13 3:22
Member 816675911-May-13 3:22 
GeneralSimple Notepad Pin
Member 945631511-Feb-13 19:59
Member 945631511-Feb-13 19:59 
Questionhi Pin
mirtahmid11-Jan-13 2:07
mirtahmid11-Jan-13 2:07 
AnswerRe: hi Pin
Edison Heng12-Jan-13 1:55
Edison Heng12-Jan-13 1:55 
GeneralRe: hi Pin
mirtahmid12-Jan-13 12:10
mirtahmid12-Jan-13 12:10 
GeneralRe: hi Pin
Edison Heng12-Jan-13 19:50
Edison Heng12-Jan-13 19:50 
You are welcome. I am new to here and I have to thanks to you hint too. Good luck
GeneralRe: hi Pin
mirtahmid12-Jan-13 20:40
mirtahmid12-Jan-13 20:40 
GeneralRe: hi Pin
jay_200815-Nov-13 16:52
jay_200815-Nov-13 16:52 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.