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