Commit 47dd78f9 authored by Roger Barton's avatar Roger Barton
Browse files

Merge branch 'master' into checkin

parents d9d785e7 240c8a86
......@@ -21,3 +21,5 @@ captures
\.idea/codeStyles/Project\.xml
\.idea/
app/release/
image: registry.gitlab.com/showcheap/android-ci:react-native
before_script:
- sdkmanager "platforms;android-27"
- sdkmanager "build-tools;27.0.3"
- export GRADLE_USER_HOME=`pwd`/.gradle
- chmod +x ./gradlew
- npm install
cache:
key: "$CI_COMMIT_REF_NAME"
paths:
- .gradle/
stages:
- test
- build
test:
stage: test
script:
- ./gradlew check
build:
stage: build
script:
- ./gradlew assembleRelease
artifacts:
expire_in: 1 week
paths:
- app/build/outputs/
image: registry.gitlab.com/showcheap/android-ci:react-native
variables:
GIT_SUBMODULE_STRATEGY: recursive
before_script:
- echo $PLAYSTORE_KEY > app/google-play-key.json
- echo $SIGNING_KEYSTORE > app/keystore.properties
- sed -i 's/\s\+/\n/g' app/keystore.properties
# Note: Gitlab CI variables show newlines as a space, this will render the storePath as incorrect->Throws error that signingConfig was not found
- sdkmanager "platforms;android-27"
- sdkmanager "build-tools;27.0.3"
- export GRADLE_USER_HOME=`pwd`/.gradle
- chmod +x ./gradlew
- npm install
cache:
key: "$CI_COMMIT_REF_NAME"
paths:
- .gradle/
stages:
- test
- build
- deploy
test:
stage: test
when: manual
script:
- ./gradlew check
build:
stage: build
script:
- ./gradlew assembleRelease
artifacts:
paths:
- app/build/outputs/
deploy:
stage: deploy
when: manual
dependencies:
- build
script:
- ./gradlew publishRelease
[submodule "volley"]
path = volley
url = https://github.com/google/volley.git
url = https://github.com/amiv-eth/volley.git
amiv-android-app1
### AMIV App - Android
This is the centralised amiv app for the android platform.
The idea is to have an app which contains all social things like events etc and to provide a platform for creating micro-apps, for example a barcode generator to use as a legi.
## To Start Developing
1. Install Android Studio, if android studio asks you to install extra stuff once opened, install it.
Gradle, the build system, will have to build first before you can edit properly.
2.
## To add your own micro-app
1. Install Android Studio and get set up. Not so easy as it sounds.
2. Create a java package under java/ch/amiv/android_app for your own java code
3. Name your resource files with a suitable prefix to keep the folders organised, especially the layout folder.
4. to be continued...
"$schema":"http://json-schema.org/draft-04/schema#"
"additionalProperties":false
"title":"Additional Fields"
"type":"object"
"properties":{
"SBB_Abo":
{
"type":"string"
"enum":["None", "GA", "Halbtax", "Gleis 7"]
}
"Food":
{
"type":"string"
"enum":["Omnivor", "Vegi", "Vegan", "Other"]
}
"Special Food Requirements":
{
"type":"string"
}
}
"required":["SBB_Abo", "Food"]
apply plugin: 'com.android.application'
apply plugin: 'com.github.triplet.play'
// Load keystore
def keystorePropertiesFile = file("keystore.properties")
def keystoreProperties = new Properties()
keystoreProperties.load(new FileInputStream(keystorePropertiesFile))
android {
compileSdkVersion 27
buildToolsVersion '27.0.3'
playAccountConfigs {
defaultAccountConfig {
jsonFile = file('google-play-key.json')
}
}
signingConfigs {
release {
storeFile file(keystoreProperties['storeFile'])
storePassword keystoreProperties['storePassword']
keyAlias keystoreProperties['keyAlias']
keyPassword keystoreProperties['keyPassword']
}
}
defaultConfig {
applicationId "ch.amiv.android_app"
minSdkVersion 23
......@@ -11,8 +34,10 @@ android {
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
vectorDrawables.useSupportLibrary = true
}
buildTypes {
release {
signingConfig signingConfigs.release
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
......@@ -27,15 +52,22 @@ dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation 'com.android.support:appcompat-v7:27.1.1'
implementation 'com.android.support:design:27.1.1'
implementation 'com.android.support.constraint:constraint-layout:1.1.0'
implementation 'com.android.support.constraint:constraint-layout:1.1.2'
implementation 'com.android.support:support-v4:27.1.1'
//implementation 'com.mcxiaoke.volley:library:1.0.19'
implementation project(path: ':volley')
implementation 'com.google.zxing:core:3.2.1'
implementation 'com.journeyapps:zxing-android-embedded:3.2.0@aar'
implementation 'com.google.android.gms:play-services-vision:15.0.2'
implementation 'com.google.code.gson:gson:2.8.2'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'com.android.support.test:runner:1.0.2'
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
}
play {
track = 'production'
uploadImages = true
errorOnSizeLimit = true
}
\ No newline at end of file
storeFile=testkeystore.jks
storePassword=000000
keyAlias=release
keyPassword=000000
\ No newline at end of file
......@@ -10,111 +10,143 @@
<uses-permission android:name="android.permission.GET_ACCOUNTS" />
<uses-permission android:name="android.permission.READ_PROFILE" />
<uses-permission android:name="android.permission.READ_CONTACTS" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<application
android:name=".core.MyApplication"
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:screenOrientation="portrait"
android:name=".core.MyApplication"
android:theme="@style/AppThemeLight">
<activity
android:name=".core.SettingsActivity"
android:label=""
android:configChanges="layoutDirection|locale"
android:parentActivityName=".core.MainActivity">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value="ch.amiv.android_app.core.MainActivity" />
</activity>
<activity
android:name=".core.MainActivity"
android:configChanges="keyboardHidden|orientation|layoutDirection|screenSize|locale"
android:name=".core.SplashActivity"
android:theme="@style/SplashTheme"
android:label="@string/app_name"
android:theme="@style/AppThemeLight"
android:windowSoftInputMode="adjustNothing">
android:noHistory="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name=".core.MainActivity"
android:configChanges="keyboardHidden|orientation|layoutDirection|screenSize|locale"
android:theme="@style/AppThemeLight"
android:windowSoftInputMode="adjustNothing">
</activity>
<activity
android:name=".core.LoginActivity"
android:configChanges="keyboardHidden|orientation|layoutDirection|screenSize|locale"
android:label="">
<!--android:windowSoftInputMode="stateVisible" use this to make the keyboard show when the activity is started-->
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value="ch.amiv.android_app.core.MainActivity" />
</activity>
<activity
android:name=".core.IntroActivity"
android:screenOrientation="portrait"
android:configChanges="locale" />
<activity
android:name=".core.SettingsActivity"
android:configChanges="layoutDirection|locale"
android:label=""
android:windowSoftInputMode="stateVisible">
android:parentActivityName=".core.MainActivity">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value="ch.amiv.android_app.core.MainActivity" />
</activity>
<activity
android:name=".core.EventDetailActivity"
android:name=".events.EventDetailActivity"
android:configChanges="orientation|layoutDirection|locale"
android:theme="@style/AppThemeLight">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value="ch.amiv.android_app.core.MainActivity" />
</activity>
<activity android:name=".checkin.BarcodeIdActivity"
android:screenOrientation="portrait"
android:configChanges="layoutDirection|locale"
android:parentActivityName=".core.MainActivity">
<activity
android:name=".jobs.JobDetailActivity"
android:configChanges="orientation|layoutDirection|locale"
android:theme="@style/AppThemeLight">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value="ch.amiv.android_app.core.MainActivity" />
</activity>
<activity
android:name=".checkin.BarcodeIdActivity"
android:configChanges="layoutDirection|locale"
android:parentActivityName=".core.MainActivity"
android:screenOrientation="portrait" />
<!-- Checkin -->
<activity
android:name=".checkin.MainActivity"
android:label="@string/app_name_checkin"
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>
android:theme="@style/AmivTheme" />
<activity android:name=".checkin.ScanActivity"
<activity
android:name=".checkin.ScanActivity"
android:label="@string/app_name_checkin"
android:screenOrientation="portrait"
android:theme="@style/CheckinTheme">
android:theme="@style/AmivTheme">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value=".checkin.MainActivity" />
</activity>
<activity android:name=".checkin.SettingsActivity"
<activity
android:name=".checkin.SettingsActivity"
android:label="@string/app_name_checkin"
android:screenOrientation="portrait"
android:label="@string/app_name"
android:theme="@style/CheckinTheme">
android:theme="@style/AmivTheme">
<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">
<activity
android:name=".checkin.MemberListActivity"
android:label="@string/app_name_checkin"
android:theme="@style/AmivTheme">
<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.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">
<activity
android:name=".checkin.SearchMembersActivity"
android:label="@string/app_name_checkin"
android:theme="@style/AmivTheme">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value=".checkin.MemberListActivity" />
</activity>
</application>
<!-- Demo -->
<activity
android:name=".demo.MainActivity"
android:configChanges="layoutDirection|locale"
android:parentActivityName=".core.MainActivity"
android:screenOrientation="portrait" />
</application>
</manifest>
\ No newline at end of file
package ch.amiv.android_app.checkin;
import android.content.Context;
import android.os.Bundle;
import android.support.design.widget.Snackbar;
import android.support.v4.content.res.ResourcesCompat;
......@@ -8,6 +9,10 @@ import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.util.Log;
import android.view.View;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
import android.view.animation.LinearInterpolator;
import android.view.animation.RotateAnimation;
import android.widget.ImageView;
import com.google.zxing.BarcodeFormat;
......@@ -17,7 +22,6 @@ import com.google.zxing.common.BitMatrix;
import com.journeyapps.barcodescanner.BarcodeEncoder;
import java.lang.reflect.Field;
import java.nio.file.attribute.UserDefinedFileAttributeView;
import ch.amiv.android_app.R;
import ch.amiv.android_app.core.UserInfo;
......@@ -48,23 +52,43 @@ public class BarcodeIdActivity extends AppCompatActivity {
}
});
//Setup swipe down to refresh
InitSwipeRefreshUI();
}
//Setup swipe down to refresh, adds the amiv logo and rotate animation
private void InitSwipeRefreshUI()
{
swipeRefreshLayout = findViewById(R.id.swipeRefresh);
swipeRefreshLayout.setRefreshing(true);
//swipeRefreshLayout.setRefreshing(true);
swipeRefreshLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() { //This sets what function is called when we swipe down to refresh
@Override
public void onRefresh() {
GenerateBarcode();
try {
Field f = swipeRefreshLayout.getClass().getDeclaredField("mCircleView");
f.setAccessible(true);
ImageView img = (ImageView)f.get(swipeRefreshLayout);
RotateAnimation rotate = new RotateAnimation(0, 360, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
rotate.setRepeatMode(Animation.INFINITE);
rotate.setDuration(1000);
rotate.setInterpolator(new LinearInterpolator());
img.startAnimation(rotate);
} catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
});
//Change the image on the swipe to refresh icon. Unfortunately have not found a way to make the icon rotate, may have to create overriden class
try {
Field f = swipeRefreshLayout.getClass().getDeclaredField("mCircleView");
f.setAccessible(true);
ImageView img = (ImageView)f.get(swipeRefreshLayout);
img.setBackground(ResourcesCompat.getDrawable(getResources(), R.drawable.ic_amiv_logo_icon_scaled, null));
swipeRefreshLayout.setColorSchemeResources(R.color.refresh_progress_1, R.color.refresh_progress_2, R.color.refresh_progress_3);
img.setImageDrawable(ResourcesCompat.getDrawable(getResources(), R.drawable.ic_amiv_logo_icon_scaled, null));
} catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
......
......@@ -38,7 +38,7 @@ public class CustomListAdapter extends ArrayAdapter<Member> {
Member m = members.get(position);
nameField.setText(m.firstname + " " + m.lastname);
infoField.setText(m.legi);
infoField.setText(m.GetLegiFormatted());
checkinField.setText((m.checkedIn ? "In" : "Out"));
if(EventDatabase.instance != null && EventDatabase.instance.eventData.eventType == EventData.EventType.GV && m.membership.length() > 1)
......
......@@ -25,6 +25,8 @@ import android.widget.TextView;
import ch.amiv.android_app.R;
import ch.amiv.android_app.core.Settings;
public class MainActivity extends AppCompatActivity {
public static String CurrentPin;
private boolean mWaitingOnServer = false;
......@@ -32,8 +34,6 @@ public class MainActivity extends AppCompatActivity {
private EditText mPinField;
private TextView mInvalidPinLabel;
public static Vibrator vibrator;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
......@@ -47,15 +47,13 @@ public class MainActivity extends AppCompatActivity {
public void onPause()
{
super.onPause();
if(vibrator != null)
vibrator.cancel();
Settings.CancelVibrate();
}
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) {
......@@ -67,7 +65,7 @@ public class MainActivity extends AppCompatActivity {
}
});
View logo = findViewById(R.id.LogoImage);
View logo = findViewById(R.id.logoImage);
if(logo != null) {
Animation animation = AnimationUtils.loadAnimation(this, R.anim.item_anim_pop);
animation.setDuration(150);
......@@ -88,13 +86,11 @@ public class MainActivity extends AppCompatActivity {
}
/**
* Submit a pin for an event to the server and act on response accondingly, ie open scanActivity if valid, or request pin entry again
* @param view
* Submit a pin for an event to the server and act on response accordingly, ie open scanActivity if valid, or request pin entry again
*/
public void SubmitPin(View view)
{
if(vibrator != null)
vibrator.vibrate(50);
Settings.Vibrate(Settings.VibrateTime.SHORT, getApplicationContext());
View button = findViewById(R.id.SubmitPin);
if(button != null)
button.startAnimation(AnimationUtils.loadAnimation(this, R.anim.item_anim_pop));
......
......@@ -14,6 +14,7 @@ public class Member {
public String firstname;
public String lastname;
public String legi;
private String formattedLegi;
public String membership; //as in membership type: ordinary extraordinary honorary
public String nethz;
public String checkinCount; //how often the person has been checked in
......@@ -46,4 +47,28 @@ public class Member {
nethz = _nethz;
checkinCount= _checkinCount;
}
private static final char legiFormatSeperator = '-';
private static final char legiStandardLength = 8;
/**
* @return Legi formatted for reading
*/
public String GetLegiFormatted (){
if(!formattedLegi.isEmpty()) //cache formatted legi for faster access, e.g. when searching through list
return formattedLegi;
if(legi == null || legi.isEmpty())
formattedLegi = "-";
else if(legi.length() != legiStandardLength)
formattedLegi = legi;
else {
StringBuilder s = new StringBuilder();
s.append(legi);
s.insert(5, legiFormatSeperator);
s.insert(2, legiFormatSeperator);
formattedLegi = s.toString();
}
return formattedLegi;
}
}
......@@ -17,13 +17,13 @@ import ch.amiv.android_app.R;
/**
* This activity is for displaying the list of signed in members, similar to what is seen in the checkin website in the other amiv checkin project.
* Mostly handles updating the data by fetching from the server and then updating the listview. Note the list view is customised, ie the individual item, this is what the CustomListAdapter class and list_item_memberber.xml are for
* Mostly handles updating the data by fetching from the server and then updating the listview. Note the list view is customised, ie the individual item, this is what the CustomListAdapter class and list_item_member.xml are for
*/
public class MemberListActivity extends AppCompatActivity {
RecyclerView mRecylerView;
RecyclerView.Adapter mRecylcerAdaper;
RecyclerView mRecyclerView;
RecyclerView.Adapter mRecyclerAdapter;
RecyclerView.LayoutManager mRecyclerLayoutAdapter;
private final Handler handler = new Handler(); //similar as in scanActivity, to keep refreshing the data
......@@ -71,19 +71,19 @@ public class MemberListActivity extends AppCompatActivity {
}
else if(id == R.id.menuSort_Membership) {
EventDatabase.instance.SetMemberSortingType(EventDatabase.MemberComparator.Membership);
mRecylcerAdaper.notifyDataSetChanged();
mRecyclerAdapter.notifyDataSetChanged();
}
else if(id == R.id.menuSort_Status) {
EventDatabase.instance.SetMemberSortingType(EventDatabase.MemberComparator.Status);
mRecylcerAdaper.notifyDataSetChanged();
mRecyclerAdapter.notifyDataSetChanged();
}
else if(id == R.id.menuSort_Name) {
EventDatabase.instance.SetMemberSortingType(EventDatabase.MemberComparator.Name);
mRecylcerAdaper.notifyDataSetChanged();
mRecyclerAdapter.notifyDataSetChanged();
}
else if(id == R.id.menuSort_Legi) {
EventDatabase.instance.SetMemberSortingType(EventDatabase.MemberComparator.Legi);
mRecylcerAdaper.notifyDataSetChanged();
mRecyclerAdapter.notifyDataSetChanged();
}
return super.onOptionsItemSelected(item);
......@@ -92,20 +92,20 @@ public class MemberListActivity extends AppCompatActivity {
private void InitialiseListView()
{
//=====Recycler View====
mRecylerView = findViewById(R.id.recyclerView);
mRecyc