Commit d9d785e7 authored by Roger Barton's avatar Roger Barton
Browse files

Added Check-in app project, reorganisation

parent 532100e2
Pipeline #4501 canceled with stages
in 15 seconds
...@@ -17,6 +17,10 @@ android { ...@@ -17,6 +17,10 @@ android {
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
} }
} }
lintOptions {
abortOnError false
}
} }
dependencies { dependencies {
...@@ -29,6 +33,7 @@ dependencies { ...@@ -29,6 +33,7 @@ dependencies {
implementation project(path: ':volley') implementation project(path: ':volley')
implementation 'com.google.zxing:core:3.2.1' implementation 'com.google.zxing:core:3.2.1'
implementation 'com.journeyapps:zxing-android-embedded:3.2.0@aar' implementation 'com.journeyapps:zxing-android-embedded:3.2.0@aar'
implementation 'com.google.android.gms:play-services-vision:15.0.2'
testImplementation 'junit:junit:4.12' testImplementation 'junit:junit:4.12'
androidTestImplementation 'com.android.support.test:runner:1.0.2' androidTestImplementation 'com.android.support.test:runner:1.0.2'
......
...@@ -17,19 +17,19 @@ ...@@ -17,19 +17,19 @@
android:label="@string/app_name" android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round" android:roundIcon="@mipmap/ic_launcher_round"
android:screenOrientation="portrait" android:screenOrientation="portrait"
android:name=".MyApplication" android:name=".core.MyApplication"
android:theme="@style/AppThemeLight"> android:theme="@style/AppThemeLight">
<activity <activity
android:name=".SettingsActivity" android:name=".core.SettingsActivity"
android:label="" android:label=""
android:configChanges="layoutDirection|locale" android:configChanges="layoutDirection|locale"
android:parentActivityName=".MainActivity"> android:parentActivityName=".core.MainActivity">
<meta-data <meta-data
android:name="android.support.PARENT_ACTIVITY" android:name="android.support.PARENT_ACTIVITY"
android:value="ch.amiv.android_app.MainActivity" /> android:value="ch.amiv.android_app.core.MainActivity" />
</activity> </activity>
<activity <activity
android:name=".MainActivity" android:name=".core.MainActivity"
android:configChanges="keyboardHidden|orientation|layoutDirection|screenSize|locale" android:configChanges="keyboardHidden|orientation|layoutDirection|screenSize|locale"
android:label="@string/app_name" android:label="@string/app_name"
android:theme="@style/AppThemeLight" android:theme="@style/AppThemeLight"
...@@ -42,28 +42,79 @@ ...@@ -42,28 +42,79 @@
</activity> </activity>
<activity <activity
android:name=".LoginActivity" android:name=".core.LoginActivity"
android:configChanges="keyboardHidden|orientation|layoutDirection|screenSize|locale" android:configChanges="keyboardHidden|orientation|layoutDirection|screenSize|locale"
android:label="" android:label=""
android:windowSoftInputMode="stateVisible"> android:windowSoftInputMode="stateVisible">
<meta-data <meta-data
android:name="android.support.PARENT_ACTIVITY" android:name="android.support.PARENT_ACTIVITY"
android:value="ch.amiv.android_app.MainActivity" /> android:value="ch.amiv.android_app.core.MainActivity" />
</activity> </activity>
<activity <activity
android:name=".EventDetailActivity" android:name=".core.EventDetailActivity"
android:configChanges="orientation|layoutDirection|locale" android:configChanges="orientation|layoutDirection|locale"
android:theme="@style/AppThemeLight"> android:theme="@style/AppThemeLight">
<meta-data <meta-data
android:name="android.support.PARENT_ACTIVITY" android:name="android.support.PARENT_ACTIVITY"
android:value="ch.amiv.android_app.MainActivity" /> android:value="ch.amiv.android_app.core.MainActivity" />
</activity> </activity>
<activity android:name=".BarcodeIdActivity" <activity android:name=".checkin.BarcodeIdActivity"
android:screenOrientation="portrait" android:screenOrientation="portrait"
android:configChanges="layoutDirection|locale" android:configChanges="layoutDirection|locale"
android:parentActivityName=".MainActivity"> android:parentActivityName=".core.MainActivity">
</activity> </activity>
<!-- Checkin -->
<activity
android:name=".checkin.MainActivity"
android:screenOrientation="portrait"
android:label="@string/app_name"
android:theme="@style/CheckinTheme">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name=".checkin.ScanActivity"
android:screenOrientation="portrait"
android:theme="@style/CheckinTheme">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value=".checkin.MainActivity" />
</activity>
<activity android:name=".checkin.SettingsActivity"
android:screenOrientation="portrait"
android:label="@string/app_name"
android:theme="@style/CheckinTheme">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value=".checkin.MainActivity" />
</activity>
<activity android:name=".checkin.MemberListActivity"
android:theme="@style/CheckinTheme"
android:label="@string/app_name">
<intent-filter>
<action android:name="android.intent.action.SEARCH" />
</intent-filter>
<meta-data android:name="android.app.searchable"
android:resource="@xml/checkin_searchable"/>
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value=".checkin.ScanActivity" />
</activity>
<activity android:name=".checkin.SearchMembersActivity"
android:theme="@style/CheckinTheme"
android:label="@string/app_name">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value=".checkin.MemberListActivity" />
</activity>
</application> </application>
</manifest> </manifest>
\ No newline at end of file
package ch.amiv.android_app; package ch.amiv.android_app.checkin;
import android.os.Bundle; import android.os.Bundle;
import android.support.design.widget.Snackbar; import android.support.design.widget.Snackbar;
...@@ -17,6 +17,10 @@ import com.google.zxing.common.BitMatrix; ...@@ -17,6 +17,10 @@ import com.google.zxing.common.BitMatrix;
import com.journeyapps.barcodescanner.BarcodeEncoder; import com.journeyapps.barcodescanner.BarcodeEncoder;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.nio.file.attribute.UserDefinedFileAttributeView;
import ch.amiv.android_app.R;
import ch.amiv.android_app.core.UserInfo;
public class BarcodeIdActivity extends AppCompatActivity { public class BarcodeIdActivity extends AppCompatActivity {
...@@ -28,7 +32,7 @@ public class BarcodeIdActivity extends AppCompatActivity { ...@@ -28,7 +32,7 @@ public class BarcodeIdActivity extends AppCompatActivity {
@Override @Override
protected void onCreate(Bundle savedInstanceState) { protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
setContentView(R.layout.activity_barcode_id); setContentView(R.layout.checkin_activity_barcode_id);
//Set up toolbar and back button //Set up toolbar and back button
Toolbar toolbar = findViewById(R.id.toolbar); Toolbar toolbar = findViewById(R.id.toolbar);
...@@ -72,14 +76,14 @@ public class BarcodeIdActivity extends AppCompatActivity { ...@@ -72,14 +76,14 @@ public class BarcodeIdActivity extends AppCompatActivity {
* Will generate a barcode bitmap and apply it to the barcodeImageview. Note: barcode will not fill whole constraint as the width of the bars is determined in pixels, to prevent distortion * Will generate a barcode bitmap and apply it to the barcodeImageview. Note: barcode will not fill whole constraint as the width of the bars is determined in pixels, to prevent distortion
*/ */
public void GenerateBarcode(){ public void GenerateBarcode(){
if(UserInfo.current == null || (UserInfo.current.nethz.isEmpty() && UserInfo.current.email.isEmpty())){ if(UserInfo.current == null || (UserInfo.current.legi.isEmpty() && UserInfo.current.nethz.isEmpty() && UserInfo.current.email.isEmpty())){
Snackbar.make(swipeRefreshLayout, R.string.not_logged_in, Snackbar.LENGTH_LONG).show(); Snackbar.make(swipeRefreshLayout, R.string.not_logged_in, Snackbar.LENGTH_LONG).show();
swipeRefreshLayout.setRefreshing(false); swipeRefreshLayout.setRefreshing(false);
return; return;
} }
//prioritise nethz as this is shorter //prioritise legi > nethz > email
String encode = UserInfo.current.nethz.isEmpty() ? UserInfo.current.email : UserInfo.current.nethz; String encode = UserInfo.current.legi.isEmpty() ? (UserInfo.current.nethz.isEmpty() ? UserInfo.current.email : UserInfo.current.nethz) : UserInfo.current.legi;
encode = encode.replace('@',' ').toUpperCase(); encode = encode.replace('@',' ').toUpperCase();
MultiFormatWriter multiFormatWriter = new MultiFormatWriter(); MultiFormatWriter multiFormatWriter = new MultiFormatWriter();
try { try {
......
package ch.amiv.android_app.checkin;
import android.app.Activity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.TextView;
import java.util.List;
import ch.amiv.android_app.R;
/**
* Created by Roger on 06-Feb-18.
*/
public class CustomListAdapter extends ArrayAdapter<Member> {
private final Activity context;
private final List<Member> members;
public CustomListAdapter(Activity context, List<Member> _members){
super(context, R.layout.checkin_list_item_member, _members);
this.context = context;
this.members = _members;
}
public View getView(int position, View view, ViewGroup parent) {
LayoutInflater inflater = context.getLayoutInflater();
View rowView = inflater.inflate(R.layout.checkin_list_item_member, null,true);
TextView nameField = rowView.findViewById(R.id.nameField);
TextView infoField = rowView.findViewById(R.id.infoField);
TextView checkinField = rowView.findViewById(R.id.checkinStatus);
TextView membershipField = rowView.findViewById(R.id.infoField2);
Member m = members.get(position);
nameField.setText(m.firstname + " " + m.lastname);
infoField.setText(m.legi);
checkinField.setText((m.checkedIn ? "In" : "Out"));
if(EventDatabase.instance != null && EventDatabase.instance.eventData.eventType == EventData.EventType.GV && m.membership.length() > 1)
membershipField.setText(m.membership.substring(0,1).toUpperCase() + m.membership.substring(1));
else
membershipField.setText("");
return rowView;
}
}
package ch.amiv.android_app.checkin;
import android.util.Log;
import java.util.ArrayList;
import java.util.List;
/**
* This stores all event data received by /get_event_data get request. It can be accessed by the static instance. The instance should be deleted when a new session is started
* Created by Roger on 28-Feb-18.
*/
public class EventData {
public String serverId = "0";
public enum EventType {NotSet, Event, PVK, GV, Counter, Unknown}
public EventType eventType = EventType.NotSet;
public enum CheckinType {InOut, Counter}
public CheckinType checkinType = CheckinType.InOut;
public String name = "";
public String description;
public int signupCount;
public int spots; //number of expected people/preregistered members
public String startTime = "";
public boolean Update(String _serverId, String _eventType, String _checkinType, String _name, String _description, int _signupCount, int _spots, String _startTime)
{
serverId = _serverId;
name = _name;
description = _description;
signupCount = _signupCount;
spots = _spots;
startTime = _startTime;
/*
if(_checkinType .equalsIgnoreCase("counter"))
checkinType = CheckinType.Counter;
else if(_checkinType.equalsIgnoreCase("in_out"))
checkinType = CheckinType.InOut;*/
return SetEventType(_eventType);
}
public boolean SetEventType(String s)
{
checkinType = CheckinType.InOut; //XXX set the checkin type in a better way, get from json or use a fkt to determine it
if(s.equalsIgnoreCase("AMIV Events")) //Matches the human readable string in the dropdown on the checkin login website
eventType = EventType.Event;
else if(s.equalsIgnoreCase("AMIV PVK")) //May need to be adjusted to what is given
eventType = EventType.PVK;
else if(s.equalsIgnoreCase("AMIV General Assemblies"))
eventType = EventType.GV;
else if(s.equalsIgnoreCase("AMIV Freebies")) {
eventType = EventType.Counter;
checkinType = CheckinType.Counter;
}
else {
eventType = EventType.Unknown;
Log.d("SetEventType()", "Parsing event type unsuccessful, will attempt to imply event type else restrict functionality.");
return false;
}
return true;
}
public List<KeyValuePair> GetInfosAsKeyValuePairs ()
{
List<KeyValuePair> list = new ArrayList<KeyValuePair>();
list.add(new KeyValuePair("Event", name));
list.add(new KeyValuePair("Event Type", eventType.toString()));
list.add(new KeyValuePair("Sign-ups", "" + signupCount));
list.add(new KeyValuePair("Description", description));
list.add(new KeyValuePair("Start Time", startTime));
return list;
}
}
package ch.amiv.android_app.checkin;
import android.util.Log;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
/**
* Created by Roger on 06-Feb-18.
* Contains all the data received by the server with GET /checkin_update_data in a android friendly format. The JSON file received by the server fills this.
* See README_API on the server side repo
*/
public class EventDatabase {
public static EventDatabase instance;
final List<Member> members = new ArrayList<Member>();
public EventData eventData = new EventData();
public List<KeyValuePair> stats = new ArrayList<KeyValuePair>();
public Comparator<Member> memberComparator;
public enum MemberComparator {None, Name, Membership, Status, Legi}
private MemberComparator currentSorting = MemberComparator.None;
private boolean invertSorting = false;
public EventDatabase()
{
if(instance != null)
Log.e("memberDatabase", "A Member Database already exists, cannot create another. Should delete old MemberDB if this is wanted");
instance = this;
}
public void UpdateMemberData(JSONArray _members)
{
members.clear();
for (int i = 0; i < _members.length(); i++) {
try {
Member m = new Member(_members.getJSONObject(i));
members.add(m);
} catch (JSONException e) {
e.printStackTrace();
}
}
SortMembers();
}
public void UpdateStats(JSONArray statSource, boolean hasEventInfos)
{
stats.clear();
try {
for (int i = 0; i < statSource.length(); i++) {
JSONObject j = statSource.getJSONObject(i);
stats.add(new KeyValuePair(j.getString("key"), j.get("value").toString()));
//also imply event type if it has not been parsed from the json
if ((!hasEventInfos || EventDatabase.instance.eventData.eventType == EventData.EventType.NotSet || EventDatabase.instance.eventData.eventType == EventData.EventType.Unknown)
&& j.getString("key").equalsIgnoreCase("Extraordinary Members"))
EventDatabase.instance.eventData.eventType = EventData.EventType.GV;
}
if (hasEventInfos && EventDatabase.instance.eventData.eventType == EventData.EventType.NotSet) //imply event type if we do not have the event infos
EventDatabase.instance.eventData.eventType = EventData.EventType.Event;
}
catch (JSONException e) {
Log.e("postrequest", "Error parsing received JsonObject in GetStats().");
e.printStackTrace();
}
}
public void SetMemberSortingType(Comparator<Member> comparator)
{
memberComparator = comparator;
SortMembers();
}
public void SetMemberSortingType(final MemberComparator sortingType)
{
if(sortingType == MemberComparator.None)
return;
if(currentSorting == sortingType)
invertSorting = !invertSorting;
else
invertSorting = false;
currentSorting = sortingType;
Comparator<Member> comparator;
comparator = new Comparator<Member>() {
@Override
public int compare(Member a, Member b) {
switch (sortingType)
{
case Name:
return a.firstname.compareTo(b.firstname);
case Membership:
return a.membership.compareTo(b.membership);
case Status:
if(eventData.checkinType == EventData.CheckinType.InOut)
return (b.checkedIn == a.checkedIn ? 0 : (a.checkedIn ? 1 : -1));
else if(eventData.checkinType == EventData.CheckinType.Counter)
return a.checkinCount.compareTo(b.checkinCount);
case Legi:
return a.legi.compareTo(b.legi);
}
return 0;
}
};
if(invertSorting && android.os.Build.VERSION.SDK_INT >= 24)
SetMemberSortingType(comparator.reversed());
else
SetMemberSortingType(comparator);
}
/**
* Way of sorting the array of members using a custom defined comparator
*/
private void SortMembers()
{
if(memberComparator == null)
return;
Collections.sort(members, memberComparator);
}
}
package ch.amiv.android_app.checkin;
/**
* Created by roger on 01/03/2018.
*/
public class KeyValuePair {
public String name;
public String value;
public KeyValuePair(String name, String value) {
this.name = name;
this.value = value;
}
}
package ch.amiv.android_app.checkin;
/**
* Author: Roger Barton, rbarton@ethz.ch
* Date Created: 2/12/17
*/
import android.Manifest;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.Bundle;
import android.os.Vibrator;
import android.support.v4.app.ActivityCompat;
import android.support.v4.content.ContextCompat;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.KeyEvent;
import android.view.View;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
import android.widget.EditText;
import android.widget.TextView;
import ch.amiv.android_app.R;
public class MainActivity extends AppCompatActivity {
public static String CurrentPin;
private boolean mWaitingOnServer = false;
private EditText mPinField;
private TextView mInvalidPinLabel;
public static Vibrator vibrator;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.checkin_activity_main);
InitialiseUI();
CheckPermissions();
}
@Override
public void onPause()
{
super.onPause();
if(vibrator != null)
vibrator.cancel();
}
private void InitialiseUI()
{
mPinField = findViewById(R.id.PinField);
mInvalidPinLabel = findViewById(R.id.InvalidPinLabel);
vibrator = (Vibrator) getSystemService(Context.VIBRATOR_SERVICE);
mPinField.setOnKeyListener(new View.OnKeyListener() {
public boolean onKey(View view, int keyCode, KeyEvent keyevent) {
if ((keyevent.getAction() == KeyEvent.ACTION_DOWN) && (keyCode == KeyEvent.KEYCODE_ENTER)) {
SubmitPin(view);
return true;
}
return false;
}
});
View logo = findViewById(R.id.LogoImage);
if(logo != null) {
Animation animation = AnimationUtils.loadAnimation(this, R.anim.item_anim_pop);
animation.setDuration(150);
logo.startAnimation(animation);
}
}
private void CheckPermissions()
{
if (ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) { //Get permission for camera
if (ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.CAMERA)) {
//Add popup