Development of an Android grading app for teachers 

Frank Böddeker

Master Thesis presentation

Agenda

meinUnterricht.de

Android

Apps in general

Tools

Implementation

Api

Bug tracking

Conclusion

meinUnterricht.de

  • Up/down load docs
  • Edit/create docs
  • Search for docs
  • Own desk

Education

  • Mobile apps

Android

  • Current version 4.4.4
  • Version 2.x, 3.x, 4.x  
  • Different devices
  • Different resolutions

Apps in general

  • Java
  • AndroidManifest.xml
    • Name
    • Launcher icon
  • Activities/Fragments
    • Intents
  • Ressources
    • Strings
    • Drawables
    • Layouts

Apps in general

  • xml structure
  • Different layout types
  • Elements
  • Every view has a layout

Layout files

<LinearLayout
        android:layout_width="fill_parent"
        android:layout_height="fill_parent">
        <LinearLayout style="@style/LoginFormContainer"
            android:orientation="vertical"
            android:layout_width="fill_parent"
            android:layout_height="fill_parent"
            android:background="#ffffff"
            android:layout_marginBottom="40dp"
            android:layout_marginTop="40dp"
            android:layout_marginLeft="30dp"
            android:layout_marginRight="30dp"
            android:paddingTop="30dp">
       
        ...
        </LinearLayout>
</LinearLayout>

Apps in general

  • Access resources
  • Generated class
  • findViewById()

R file

package de.meinunterricht.app;

public final class R {
    ...
    public static final class layout {
        public static final int activity_add_student=0x7f030018;
        public static final int activity_add_test=0x7f030019;
        public static final int activity_global=0x7f03001a;
        ...
    }
    ...
}

//R.layout.activity_add_student

Tools

  • UI mockup
  • Android elements
  • Linking
  • Notes

Balsamiq

Tools

Balsamiq

Tools

  • IntelliJ
  • Beta version
  • Layout editor
  • Gradle IDE integration
  • Wizard

AndroidStudio

Tools

  • Emulator
  • Virtual box
  • Fast
  • Terminal access

Genymotion

Tools

  • Dependencies
  • Maven
  • Create multiple versions
  • Unit tests
  • Lint

Gradle

Implementation

  • Displays one active screen
  • Changing between Activities with intents
  • setContentView(layout);
  • Save settings in onStop()

Activity

Implementation

  • Added to activities
  • Multi-pane views
  • findFragmentById()
  • findFragmentByTag()

Fragments

Implementation

  • Activity with wizard
  • Directly added to activity
  • Activity has one FrameLayout
  • Fragment as inner class

Activities/Fragments

//Activity onCreate()
if (savedInstanceState == null) {
   getFragmentManager().beginTransaction()
      .add(R.id.container, new PlaceholderFragment())
      .commit();
}
//Placeholder fragment onCreateView
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
                                 Bundle savedInstanceState) {
    rootView = inflater.inflate(R.layout.fragment_registration,
                                 container,
                                 false);
    …
    return rootView
}

Implementation

  • context.getApplicationContext()
  • Reachable from everywhere
  • Keeps user model
  • Singelton like

Application

public class ApplicationHandler extends Application {

    @Override
    public void onCreate() {
        super.onCreate();
    }
    
    public ApplicationHandler() {
        user = new User();
    }
}

Implementation

  • Stores data
  • Key value
    • ​Token
    • Positions
    • Client ID

Shared preferences

//Add something to Prefs
sharedPreferences = context.
             getSharedPreferences(TOKEN, 0);
SharedPreferences.Editor editor = sharedPreferences.edit();
editor.putString("token", accessToken);
editor.commit();

//Read something from Prefs
sharedPreferences = context.
             getSharedPreferences(TOKEN, 0);
sharedPreferences.getString("token", "empty");

Implementation

  • Extend with listactivity or as element in layout
  • Needs adapter
    • Custom adapter
    • Array Adapter
  • Two listviews not recommended
  • Change notifier
  • onListitemClick when extended

Listviews

<ListView
    android:id="@+id/student_pref_tests"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:clickable="false"
    />

Implementation

  • Just displaying a list of values
  • android.R != project R file
  • No custom rows

Array adapter

ArrayAdapter<String> adapter = new ArrayAdapter<String>(this,
              android.R.layout.simple_list_item_1, android.R.id.text1, values);

Implementation

  • Custom layout per row
  • All elements in one row
    • show/hide needed elements
  • List of elements is passed to the adapter

Custom array adapter

Implementation

One row

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="match_parent"
    android:clickable="false"
    android:focusable="false"
    android:focusableInTouchMode="false"
    android:id="@+id/one_row_linear_student_pref">

    <!-- Term -->
    <RelativeLayout android:id="@+id/pref_student_term_header"
        ...
    </RelativeLayout>

    <!-- ExamType -->
    <RelativeLayout android:id="@+id/pref_student_exam_head"
        ...
    </RelativeLayout>

    <!-- GradeRow -->
    <RelativeLayout style="@style/RowStyle"
        ...
    </RelativeLayout>

</LinearLayout>

Implementation

  • Call super() in constructor
  • Overwrite getView()
  • notifyDataSetChanged()
  • clear()

Custom Adapter

public StudentPrefAdapater(Context context, 
                           SchoolClass schoolClass, 
                           int gradingSystem) {
        super(context, 
              R.layout.activity_student_pref_row, 
              ((StudentPrefsActivity) context).getTests());
        ...
}

Implementation

  • Inflate layout in getView()
  • Getting layout elements with findViewById(id);
  • convertView() called for each row in the list

Custom Adapter getView()

//getView()
LayoutInflater inflater = (LayoutInflater) context
                    .getSystemService(Context.LAYOUT_INFLATER_SERVICE);

convertView = inflater.inflate(R.layout.activity_student_pref_row, parent, false);
...
TextView examGrade = (TextView) convertView.findViewById(R.id.pref_student_examgrade)

Implementation

  • findViewById() slows down performance
    • Find elements again
  • Static inner class to hold the view elements

Custom Adapter holder pattern

 if (convertView == null) {
    LayoutInflater inflater = (LayoutInflater) context
            .getSystemService(Context.LAYOUT_INFLATER_SERVICE);

    convertView = inflater.inflate(R.layout.row, parent, false);

    holder = new StudentPrefHolder();
    holder.setTermLayout((RelativeLayout) convertView.findViewById(R.id.head));

    convertView.setTag(holder);

} else {
    holder = (StudentPrefHolder) convertView.getTag();
}
...
static class StudentPrefHolder {
    private RelativeLayout termLayout;
}

Implementation

  • Picker for grades
  • DialogFragment
  • Override
    • onValueChange()
    • onDetach()
    • onCreateDialog()

Number picker

Implementation

  • Interface for communication
  • Calling method in activity
  • Setting the value in activity below

Dialog communication

//onDialogDetach interface in Activity
@Override
public void onDialogDetach(NumberPickerFragment dialog) {
    //the chosen value from the picker
    int numberPickerValue = dialog.getValueFromPicker();
    ...
}

Implementation

  • Context menu
  • Items in Actionbar
    • option items
    • case android.R.id.home:

Navigation

switch (item.getItemId()) {
    case R.id.context_menu_edit_pupil:
        changeToPupilInformations();
        return true;
    case R.id.context_menu_delete_pupil:
        deletePupil(classPosition, pupilPosition);
        return true;
    default:
        return false;
}

Implementation

  • Navigation drawer
  • Side navigation
    • Click or swipe
  • List of school classes

Navigation

<android.support.v4.widget.DrawerLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/drawer_layout"
android:layout_width="match_parent"
android:layout_height="match_parent">
    <!-- The main content view -->
    <include layout="@layout/tabview_global"/>

    <!-- The navigation drawer -->
    <include layout="@layout/navigation_class_overview"/>

</android.support.v4.widget.DrawerLayout>

Implementation

  • Tabs
  • Fragment in main Activity
    • two tabs for pupils and tests
  • Nested fragments

Navigation

studentOverviewFragment = (StudentOverviewFragment) tabFragment.
                                                    getChildFragmentManager().
                                                    findFragmentByTag("pupils");

        if (studentOverviewFragment != null) {
            studentOverviewFragment.updatePupilList(classPosition);
        }
}
FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
ft.replace(R.id.global_content_frame, tabFragment, "tabTag");
ft.commit();

Implementation

  • Separate project / file
  • Gradle build from console
    • apply-plugin: 'android-library'
  • Needs same Manifest information
  • ./gradlew clean && ./gradlew aR

Api

repositories {
    flatDir { dirs 'libs' }
}
dependencies {
    compile’:lib:1.0@aar’
}

Implementation

  • http REST Api
  • POST, PUT, DELETE, GET
  • Receive JSON object
  • JSON to local model structure
  • fromJSON and toJSON
  • MuApi.schoolClass.put(JSONObject document, String specifier)

Api

Implementation

  • Bugsense
    • Dashboard
    • Multiple platform
    • Gradle dependency
    • Exceptions
    • Own messages

Bug tracking

Implementation

  • Google dashboard
    • Exceptions
    • Devices
    • Android version
    • Statistics

Bug tracking

Conclusion

  • Nice project with a good use case
  • Missing features
    • Offline
    • Tablet
  • Grading feature in webapp
  • More "nice to have" features
  • Add library as dependency

Thank you

Kolloquium

By frankbo

Kolloquium

  • 834