Objectives

We continue the implementation of Donation to including the welcome, sign up and login. Support these views with a User model, and validate the users on log in.

Exercise 1

Run the app and insert amounts of varying lengths (1, 222, 23, 2323). Note that the second column - payment method -may be displayed at different positions. If this happens, fix it.

Hint: each row is laid out by a row_donate.xml layout. The easiest way to fix this would be to experiment with they layout, and have the text fields aligned with the edges and not with each other.

Solution

This is a revised version of the row_donate.xml file:

<?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:id="@+id/row_amount"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentLeft="true"
        android:layout_alignParentTop="true"
        android:layout_marginLeft="48dp"
        android:layout_marginTop="20dp"
        android:text="@string/defaultAmount" />

    <TextView
        android:id="@+id/row_method"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignBaseline="@+id/row_amount"
        android:layout_alignBottom="@+id/row_amount"
        android:layout_alignParentRight="true"
        android:layout_marginRight="79dp"
        android:text="@string/defaultMethod" />

</RelativeLayout>

Exercise 2

When a donation is accepted, set the amount on screen to 0 (in both picker and text field).

Solution

Add these two lines at the end of Donate.donateButtonPressed()

    amountText.setText("");
    amountPicker.setValue(0);

Exercise 3

When you navigate from the Donate activity to reports, there will be no menu available. Bring in a menu, with two options 'Settings' and 'Donate' - Donate should bring you back to the donate screen.

Solution

Introduce a new string constant into strings.xml:

    <string name="menuDonate">Donate</string>

Then a new menu - report.xml -

<menu xmlns:android="http://schemas.android.com/apk/res/android" >

    <item
        android:id="@+id/action_settings"
        android:orderInCategory="100"
        android:showAsAction="never"
        android:title="@string/menuSettings"/>

    <item
        android:id="@+id/menuDonate"
        android:orderInCategory="100"
        android:showAsAction="never"
        android:title="@string/menuDonate"/>    

</menu>

The report activity will then require the following two methods.

  @Override
  public boolean onCreateOptionsMenu(Menu menu)
  {
    getMenuInflater().inflate(R.menu.report, menu);
    return true;
  }

  @Override
  public boolean onOptionsItemSelected(MenuItem item)
  {
    switch (item.getItemId())
    {
      case R.id.menuDonate : startActivity (new Intent(this, Donate.class));
                             break;
    }
    return true;
  }  

These imports are required:

import android.content.Intent;
import android.view.MenuItem;
import android.view.Menu;

Exercise 4

Introduce a new welcome screen - which should display a greeting + give the user 2 options (as simple buttons)

When Login is pressed, the app should take you directly to the Donate activity (for the moment).

Solution

This is the new layout:

Filename: activity_welcome.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/RelativeLayout"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >

    <Button
        android:id="@+id/welcomeLogin"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentTop="true"
        android:layout_centerHorizontal="true"
        android:layout_marginTop="102dp"
        android:text="@string/welcomeLogin" />

    <Button
        android:id="@+id/welcomeSignup"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerHorizontal="true"
        android:layout_centerVertical="true"
        android:text="@string/welcomeSignup" />

</RelativeLayout>

It requires these two strings in strings.xml:

    <string name="welcomeLogin">Login</string>
    <string name="welcomeSignup">Sign up</string>

Here is a minimal version of the activity:

package app.activities;

import app.donation.R;
import android.app.Activity;
import android.os.Bundle;

public class Welcome extends Activity
{
  @Override
  public void onCreate(Bundle savedInstanceState)
  {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_welcome);
  }
}

We would like this to be the first activity to appear when the app is launched. This is configured in AndroidManifest.xml.

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

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

    <application
        android:name="app.main.DonationApp"
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
        <activity
            android:name="app.activities.Donate"
            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="app.activities.Report"
            android:label="@string/donateTitle" >
        </activity>
    </application>

</manifest>

In the above, you can see that "Donate" is the MAIN activity, and we have no entry for Welcome yet. Here is a revision to do what we want:

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

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

    <application
        android:name="app.main.DonationApp"
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >

        <activity
            android:name="app.activities.Welcome"
            android:label="@string/donateTitle" >
           <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <activity
            android:name="app.activities.Donate"
            android:label="@string/app_name" >
        </activity>
        <activity
            android:name="app.activities.Report"
            android:label="@string/donateTitle" >
        </activity>
    </application>

</manifest>

We have three activities - with Welcome denoted as the Main via these attributes:

           <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>

Notice that we have 'donateTitle' as the Label for all activities. It is currently set to:

    <string name="donateTitle">Welcome Homer</string>

in strings. Perhaps we should change this now to:

    <string name="donateTitle">Donation App</string>

It may make sense to have a different label for each activity as you evolve the app.

Finally, we need an event handler for the Login button.

    <Button
        android:id="@+id/welcomeLogin"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentTop="true"
        android:layout_centerHorizontal="true"
        android:layout_marginTop="102dp"
        android:onClick="loginPressed"
        android:text="@string/welcomeLogin" />

Then bring in the method into Welcome activity:

  public void loginPressed (View view) 
  {
    Toast toast = Toast.makeText(this, "Login Pressed!", Toast.LENGTH_SHORT);
    toast.show();
  }

These import statements are necessary:

import android.view.View;
import android.widget.Toast;

Test all of this now.

Finally, replace the loginPressed method implementation with the code to actually start the donate activity:

   startActivity (new Intent(this, Donate.class));

Import the Intent class and delete the import for Toast which is no longer needed here:

import android.content.Intent;

Launch the app again, press Login: the Donation view should open.

This completes exercise 4.

Exercise 5 - Part 1 Layout

Introduce a Signup Activity, which should present the user with:

Pressing Register should take you directly to "Donate" activity. Pressing Register should take you directly to "Donate" activity. Also, refactor the Welcome screen such that the 'signup' button takes you to this screen.

Solution

In the Exercise 4 we created and activity by separately creating layout and a class, and building each from scratch. This time we will use the create activity wizard. In doing so, keep a close eye the files the wizard generates or modifies.

Select the app.activities package, and select "File->New->Other" ... and follow the following entries:

The last screen you should look at very carefully:

Once you press Finish - hunt down each of the files mentioned here and inspect them.

Open the layout designer, and design a screen (activity_signup.xml) to look like this:

In particular, that we do not have a 'label' for first name (like we would have in a web app), but rather set a 'Hint'. This can be set by right clicking on the edit field and selecting "Edit Hint" - something like this:

When placing the text field on the canvas, select "Person Name" from the 'Text Widgets' palette

This is the layout so far:

<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:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context=".Signup" >

    <TextView
        android:id="@+id/signupTitle"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentLeft="true"
        android:layout_alignParentTop="true"
        android:layout_marginLeft="32dp"
        android:layout_marginTop="28dp"
        android:text="@string/signupTitle"
        android:textAppearance="?android:attr/textAppearanceMedium" />

    <TextView
        android:id="@+id/signupSubtitle"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignLeft="@+id/signupTitle"
        android:layout_below="@+id/signupTitle"
        android:layout_marginLeft="55dp"
        android:layout_marginTop="30dp"
        android:text="@string/signupSubtitle"
        android:textAppearance="?android:attr/textAppearanceSmall" />

    <EditText
        android:id="@+id/firstName"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentLeft="true"
        android:layout_alignParentRight="true"
        android:layout_below="@+id/signupSubtitle"
        android:layout_marginTop="40dp"
        android:ems="10"
        android:hint="@string/signupFirstname"
        android:inputType="textPersonName" />

</RelativeLayout>

and strings.xml has these supporting definitions:

    <string name="title_activity_signup">Signup</string>
    <string name="signupTitle">Sign up for the Donation App</string>
    <string name="signupSubtitle">Enter details below</string>
    <string name="signupFirstname">First name</string>

Complete the layout now, select "Person Name", "Email" and "Password" from the text widgets pallette:

The email and password ids are exceptionally "Email" and "Password" (leading upper case letter). This is to avoid conflict with built in ids.

Set the 'Hints' as you go, and fix up the strings as we have been doing. This is the final version of the layout:

<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:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context=".Signup" >

    <TextView
        android:id="@+id/signupTitle"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentLeft="true"
        android:layout_alignParentTop="true"
        android:layout_marginLeft="32dp"
        android:layout_marginTop="28dp"
        android:text="@string/signupTitle"
        android:textAppearance="?android:attr/textAppearanceMedium" />

    <TextView
        android:id="@+id/signupSubtitle"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignLeft="@+id/signupTitle"
        android:layout_below="@+id/signupTitle"
        android:layout_marginLeft="55dp"
        android:layout_marginTop="30dp"
        android:text="@string/signupSubtitle"
        android:textAppearance="?android:attr/textAppearanceSmall" />

    <EditText
        android:id="@+id/firstName"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentLeft="true"
        android:layout_alignParentRight="true"
        android:layout_below="@+id/signupSubtitle"
        android:layout_marginTop="40dp"
        android:ems="10"
        android:hint="@string/signupFirstname"
        android:inputType="textPersonName"/>

        <requestFocus />

    <EditText
        android:id="@+id/lastName"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignLeft="@+id/firstName"
        android:layout_alignParentRight="true"
        android:layout_below="@+id/firstName"
        android:ems="10"
        android:hint="@string/signupLastName"
        android:inputType="textPersonName" >

    </EditText>

    <EditText
        android:id="@+id/Email"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignLeft="@+id/lastName"
        android:layout_alignParentRight="true"
        android:layout_below="@+id/lastName"
        android:ems="10"
        android:hint="@string/signupEmail"
        android:inputType="textEmailAddress" />

    <EditText
        android:id="@+id/Password"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignLeft="@+id/Email"
        android:layout_alignParentRight="true"
        android:layout_below="@+id/Email"
        android:ems="10"
        android:hint="@string/signupPassword"
        android:inputType="textPassword" />

    <Button
        android:id="@+id/register"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:layout_centerHorizontal="true"
        android:layout_marginBottom="28dp"
        android:onClick="registerPressed"
        android:text="@string/signupRegister" />

</RelativeLayout>

and the supporting Strings:

    ...
    <string name="title_activity_signup">Signup</string>
    <string name="signupTitle">Sign up for the Donation App</string>
    <string name="signupSubtitle">Enter details below</string>
    <string name="signupFirstname">First name</string>
    <string name="signupLastName">Last Name</string>
    <string name="signupEmail">Email</string>
    <string name="signupPassword">Password</string>
    <string name="signupRegister">Register</string>

Exercise 5 - Part 2 Activity Class

(continued)

Pressing Register should take you directly to "Donate" activity. Also, refactor the Welcome screen such that the 'signup' button takes you to this screen (the Donate activity).

Note: we have labelled the Register button as Signup in the Welcome activity.

Solution

The previous step generated this Activity class:

public class Signup extends Activity
{

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

  @Override
  public boolean onCreateOptionsMenu(Menu menu)
  {
    // Inflate the menu; this adds items to the action bar if it is present.
    getMenuInflater().inflate(R.menu.signup, menu);
    return true;
  }
}

This class may be in the wrong package - if so, move it (drag and drop) to 'app.activities' now.

We are not going to support a menu in this class - so we will delete the onCreateOptionsMenu method now.

Similarly, we can delete the generate menu - signup.xml - in the menu folder.

Now we can set our event handler in the activity_signup.xml layout:

    <Button
        android:id="@+id/signup"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:layout_centerHorizontal="true"
        android:layout_marginBottom="28dp"
        android:onClick="registerPressed"
        android:text="@string/signupRegister" />

and implement the event handler:

  public void registerPressed (View view) 
  {
    startActivity (new Intent(this, Donate.class));
  }

Finally (!) - we need to wire up the 'Signup' button from the welcome screen to take us to the signup activity.

Open the welcome layout, and introduce the click handler:

    <Button
        android:id="@+id/welcomeSignup"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerHorizontal="true"
        android:layout_centerVertical="true"
        android:onClick="signupPressed"
        android:text="@string/welcomeSignup" />

Then, in Welcome activity - implement the handler:

  public void signupPressed (View view) 
  {
    startActivity (new Intent(this, Signup.class));
  }

Run the app now, and verify that the navigation operates as expected. Also, try the 'back' button, and the menu button. Try various combination of these to see how it operates.

Exercise 6:

Introduce a Login activty, which should just look for

Pressing Login should take you directly to "Donate" activity.

Solution

Create new activity using the 'Wizard' - called "Login". Do not select "Login" form the wizard, keep the activity 'Blank'.

Edit the layout, and design a screen to look like this:

This is the layout that may be generated:

<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:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context=".Login" >

    <TextView
        android:id="@+id/loginTitle"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentLeft="true"
        android:layout_alignParentRight="true"
        android:layout_alignParentTop="true"
        android:layout_marginTop="18dp"
        android:text="@string/loginTitle"
        android:textAppearance="?android:attr/textAppearanceMedium" />

    <TextView
        android:id="@+id/loginSubtitle"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignLeft="@+id/loginTitle"
        android:layout_alignParentRight="true"
        android:layout_below="@+id/loginTitle"
        android:text="@string/loginSubtitle"
        android:textAppearance="?android:attr/textAppearanceSmall" />

    <EditText
        android:id="@+id/loginEmail"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignLeft="@+id/loginSubtitle"
        android:layout_alignRight="@+id/loginSubtitle"
        android:layout_below="@+id/loginSubtitle"
        android:layout_marginTop="17dp"
        android:ems="10"
        android:hint="@string/loginEmail"
        android:inputType="textEmailAddress" >

        <requestFocus />
    </EditText>

    <EditText
        android:id="@+id/loginPassword"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignLeft="@+id/loginEmail"
        android:layout_alignRight="@+id/loginEmail"
        android:layout_below="@+id/loginEmail"
        android:ems="10"
        android:hint="@string/loginPassword"
        android:inputType="textPassword" />

    <Button
        android:id="@+id/login"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerHorizontal="true"
        android:layout_centerVertical="true"
        android:text="@string/loginSignin" />

</RelativeLayout>

and the supporting strings:

    <string name="title_activity_login">Login</string>
    <string name="loginTitle">Login to Donation</string>
    <string name="loginSubtitle">You must be registered</string>
    <string name="loginSignin">Sign in</string>
    <string name="loginEmail">Email</string>
    <string name="loginPassword">Password</string>

There is how we would like the class to be implemented:

package app.activities;

import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import app.donation.R;

public class Login extends Activity
{
  @Override
  protected void onCreate(Bundle savedInstanceState)
  {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_login);
  }

  public void signinPressed (View view) 
  {
    startActivity (new Intent(this, Donate.class));
  }
}

Locate the button resource in the activity_login.xml resource:

    <Button
        android:id="@+id/login"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerHorizontal="true"
        android:layout_centerVertical="true"
        android:onClick="signinPressed"        
        android:text="@string/loginSignin" />

.. and introduce the onClick handler (shown above).

Now, redirect the welcome screen to take the user to this screen when 'Login' is pressed. Open Welcome.java:

  public void loginPressed (View view) 
  {
    startActivity (new Intent(this, Login.class));
  }

Now launch the app and verify that this route works as expected.

Exercise 7

Bring in a new menu option - 'logout'. It should take you to the welcome screen, and should be available from the donate and report activities.

Solution

Introduce a new string resource for the menu item in strings.xml:

    <string name="menuLogout">Logout</string>    

In both donate.xml and report.xml (the menus), bring in a new option:

      <item
        android:id="@+id/menuLogout"
        android:orderInCategory="100"
        android:showAsAction="never"
        android:title="@string/menuLogout"/>  

Run the app now, and verify that these options appear.

Then we need to implement the control flow. In the donate activity class:

  @Override
  public boolean onOptionsItemSelected(MenuItem item)
  {
    switch (item.getItemId())
    {
      case R.id.menuReport : startActivity (new Intent(this, Report.class));
                             break;
      case R.id.menuLogout : startActivity (new Intent(this, Welcome.class));
                             break;                             
    }
    return true;
  }

and Report:

  @Override
  public boolean onOptionsItemSelected(MenuItem item)
  {
    switch (item.getItemId())
    {
      case R.id.menuDonate : startActivity (new Intent(this, Donate.class));
                             break;
      case R.id.menuLogout : startActivity (new Intent(this, Welcome.class));
                             break;                               
    }
    return true;
  }  

Test this now.

Exercise 8

Introduce a 'User' into the models package to represent the user in the usual way. Maintain a list of Users in the DonationApp object. Whenever anyone registers, then create a new User object in this list.

Solution

Bring in this new class into models:

package app.models;

public class User 
{
  public String firstName;
  public String lastName;
  public String email;
  public String password;

  public User(String firstName, String lastName, String email, String password)
  {
    this.firstName = firstName;
    this.lastName = lastName;
    this.email = email;
    this.password = password;
  } 
}

In DonationApp class, incorporate a new collection of Users as a member of the class:

  public List <User>      users       = new ArrayList<User>();  

.. and a new method to add a user to this collection:

  public void newUser(User user)
  {
    users.add(user);
  }

Import required:

import app.models.User;

Now in the Signup activity - we can retrieve the fields from the widgets, create a new User object and store it in the DonationApp object:

  public void registerPressed (View view) 
  {
    TextView firstName = (TextView)  findViewById(R.id.firstName);
    TextView lastName  = (TextView)  findViewById(R.id.lastName);
    TextView email     = (TextView)  findViewById(R.id.Email);
    TextView password  = (TextView)  findViewById(R.id.Password);

    User user = new User (firstName.getText().toString(), lastName.getText().toString(), email.getText().toString(), password.getText().toString());

    DonationApp app = (DonationApp) getApplication();
    app.newUser(user);

    startActivity (new Intent(this, Welcome.class));
  }
}

Imports required:

import android.widget.TextView;
import app.models.User;
import app.main.DonationApp;

Delete any redundant import statements.

Test this now - There is one behavioural change from the last version - pressing 'Register' takes us back to the Welcome screen.

Exercise 9

Implement the Login activity, to now only let users in to Donate if they are registered (i.e. a matching email + password in the list of users maintained by DonationApp)

Solution

Here is a new method for DonationApp that will validate users :

  public boolean validUser (String email, String password)
  {
    for (User user : users)
    {
      if (user.email.equals(email) && user.password.equals(password))
      {
        return true;
      }
    }
    return false;
  }

Then we can check in Login activity that the entered details match a user:

  public void signinPressed (View view) 
  {
    DonationApp app = (DonationApp) getApplication();

    TextView email     = (TextView)  findViewById(R.id.loginEmail);
    TextView password  = (TextView)  findViewById(R.id.loginPassword);

    if (app.validUser(email.getText().toString(), password.getText().toString()))
    {
      startActivity (new Intent(this, Donate.class));
    }
    else
    {
      Toast toast = Toast.makeText(this, "Invalid Credentials", Toast.LENGTH_SHORT);
      toast.show();
    }
  }

Imports required:

import android.widget.TextView;
import android.widget.Toast;
import app.main.DonationApp;

This completes the series of exercises based on donation lab v2.

Challenges

Challenge 1:

Introduce a feature whereby the donor is explicitly associated with each donation.

Hint: Modify DonationApp class to keep a reference to 'current' user. Store a reference to this user in each Donation that is created

Challenge 2:

Change the Report activity to show the name of the doner with each donation

Challenge 3:

Introduce a new activity which will display, sorted, the top three doners and the amounts they contributed.