From: pk910 Date: Wed, 27 Jan 2016 10:29:18 +0000 (+0100) Subject: Grundaufbau der App X-Git-Url: http://git.pk910.de/?p=DHBWCampusApp.git;a=commitdiff_plain;h=a0f644715b43af1e4acf2513b972c3f980efdaca Grundaufbau der App --- a0f644715b43af1e4acf2513b972c3f980efdaca diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..c6cbe56 --- /dev/null +++ b/.gitignore @@ -0,0 +1,8 @@ +*.iml +.gradle +/local.properties +/.idea/workspace.xml +/.idea/libraries +.DS_Store +/build +/captures diff --git a/.idea/.name b/.idea/.name new file mode 100644 index 0000000..fc882fd --- /dev/null +++ b/.idea/.name @@ -0,0 +1 @@ +DHBW Campus App \ No newline at end of file diff --git a/.idea/compiler.xml b/.idea/compiler.xml new file mode 100644 index 0000000..9a8b7e5 --- /dev/null +++ b/.idea/compiler.xml @@ -0,0 +1,22 @@ + + + + + \ No newline at end of file diff --git a/.idea/copyright/profiles_settings.xml b/.idea/copyright/profiles_settings.xml new file mode 100644 index 0000000..e7bedf3 --- /dev/null +++ b/.idea/copyright/profiles_settings.xml @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/.idea/encodings.xml b/.idea/encodings.xml new file mode 100644 index 0000000..97626ba --- /dev/null +++ b/.idea/encodings.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/.idea/gradle.xml b/.idea/gradle.xml new file mode 100644 index 0000000..d390b65 --- /dev/null +++ b/.idea/gradle.xml @@ -0,0 +1,19 @@ + + + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..6a1e020 --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,46 @@ + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..a1be715 --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/.idea/runConfigurations.xml b/.idea/runConfigurations.xml new file mode 100644 index 0000000..7f68460 --- /dev/null +++ b/.idea/runConfigurations.xml @@ -0,0 +1,12 @@ + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..94a25f7 --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/app/.gitignore b/app/.gitignore new file mode 100644 index 0000000..796b96d --- /dev/null +++ b/app/.gitignore @@ -0,0 +1 @@ +/build diff --git a/app/build.gradle b/app/build.gradle new file mode 100644 index 0000000..6414d6d --- /dev/null +++ b/app/build.gradle @@ -0,0 +1,60 @@ +apply plugin: 'com.android.application' + +android { + compileSdkVersion 23 + buildToolsVersion "23.0.2" + + defaultConfig { + applicationId "de.dhbwloe.campusapp" + minSdkVersion 16 + targetSdkVersion 23 + versionCode 1 + versionName "1.0" + } + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' + } + } + packagingOptions { + exclude 'META-INF/DEPENDENCIES.txt' + exclude 'META-INF/LICENSE.txt' + exclude 'META-INF/NOTICE.txt' + exclude 'META-INF/NOTICE' + exclude 'META-INF/LICENSE' + exclude 'META-INF/DEPENDENCIES' + exclude 'META-INF/notice.txt' + exclude 'META-INF/license.txt' + exclude 'META-INF/dependencies.txt' + exclude 'META-INF/LGPL2.1' + } +} + +dependencies { + compile fileTree(dir: 'libs', include: ['*.jar']) + testCompile 'junit:junit:4.12' + compile 'org.mnode.ical4j:ical4j:1.0.5' + compile 'backport-util-concurrent:backport-util-concurrent:3.1' + compile 'commons-codec:commons-codec:1.8' + compile 'commons-lang:commons-lang:2.6' + compile 'com.loopj.android:android-async-http:1.4.9' + compile 'org.jsoup:jsoup:1.8.3' + compile 'org.apache.commons:commons-lang3:3.1' + compile 'com.android.support:appcompat-v7:23.1.1' + compile 'com.android.support:design:23.1.1' + compile 'com.android.support:support-v4:23.1.1' +} + + + + + + + + + + + + + diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro new file mode 100644 index 0000000..2b76c9d --- /dev/null +++ b/app/proguard-rules.pro @@ -0,0 +1,17 @@ +# Add project specific ProGuard rules here. +# By default, the flags in this file are appended to flags specified +# in D:\android-sdk/tools/proguard/proguard-android.txt +# You can edit the include path and order by changing the proguardFiles +# directive in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# Add any project specific keep options here: + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} diff --git a/app/src/androidTest/java/de/dhbwloe/campusapp/ApplicationTest.java b/app/src/androidTest/java/de/dhbwloe/campusapp/ApplicationTest.java new file mode 100644 index 0000000..ce43036 --- /dev/null +++ b/app/src/androidTest/java/de/dhbwloe/campusapp/ApplicationTest.java @@ -0,0 +1,13 @@ +package de.dhbwloe.campusapp; + +import android.app.Application; +import android.test.ApplicationTestCase; + +/** + * Testing Fundamentals + */ +public class ApplicationTest extends ApplicationTestCase { + public ApplicationTest() { + super(Application.class); + } +} \ No newline at end of file diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml new file mode 100644 index 0000000..89c3c04 --- /dev/null +++ b/app/src/main/AndroidManifest.xml @@ -0,0 +1,40 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/ic_close_white-web.png b/app/src/main/ic_close_white-web.png new file mode 100644 index 0000000..5b55c69 Binary files /dev/null and b/app/src/main/ic_close_white-web.png differ diff --git a/app/src/main/ic_search-web.png b/app/src/main/ic_search-web.png new file mode 100644 index 0000000..a31172c Binary files /dev/null and b/app/src/main/ic_search-web.png differ diff --git a/app/src/main/java/com/codebutler/farebot/Utils.java b/app/src/main/java/com/codebutler/farebot/Utils.java new file mode 100644 index 0000000..c77b93c --- /dev/null +++ b/app/src/main/java/com/codebutler/farebot/Utils.java @@ -0,0 +1,268 @@ +/* + * Utils.java + * + * Copyright (C) 2011 Eric Butler + * + * Authors: + * Eric Butler + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.codebutler.farebot; + +import android.app.Activity; +import android.app.AlertDialog; +import android.content.DialogInterface; +import android.util.Log; +import android.view.WindowManager; + +import com.codebutler.farebot.card.desfire.DesfireException; +import com.codebutler.farebot.card.desfire.DesfireFileSettings; +import com.codebutler.farebot.card.desfire.DesfireProtocol; + +import org.w3c.dom.Node; + +import java.io.StringWriter; +import java.util.List; + +import javax.xml.transform.OutputKeys; +import javax.xml.transform.Result; +import javax.xml.transform.Source; +import javax.xml.transform.Transformer; +import javax.xml.transform.TransformerFactory; +import javax.xml.transform.dom.DOMSource; +import javax.xml.transform.stream.StreamResult; + +public class Utils { + + private static final String TAG = Utils.class.getName(); + + public static void showError (final Activity activity, Exception ex) { + Log.e(activity.getClass().getName(), ex.getMessage(), ex); + new AlertDialog.Builder(activity) + .setMessage(Utils.getErrorMessage(ex)) + .show(); + } + + public static void showErrorAndFinish (final Activity activity, Exception ex) { + try { + Log.e(activity.getClass().getName(), Utils.getErrorMessage(ex)); + ex.printStackTrace(); + + new AlertDialog.Builder(activity) + .setMessage(Utils.getErrorMessage(ex)) + .setCancelable(false) + .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { + public void onClick(DialogInterface arg0, int arg1) { + activity.finish(); + } + }) + .show(); + } catch (WindowManager.BadTokenException unused) { + /* Ignore... happens if the activity was destroyed */ + } + } + + public static String getHexString (byte[] b) throws Exception { + String result = ""; + for (int i=0; i < b.length; i++) { + result += Integer.toString( ( b[i] & 0xff ) + 0x100, 16).substring( 1 ); + } + return result; + } + + public static String getHexString (byte[] b, String defaultResult) { + try { + return getHexString(b); + } catch (Exception ex) { + return defaultResult; + } + } + + public static byte[] hexStringToByteArray (String s) { + if ((s.length() % 2) != 0) { + throw new IllegalArgumentException("Bad input string: " + s); + } + + int len = s.length(); + byte[] data = new byte[len / 2]; + for (int i = 0; i < len; i += 2) { + data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4) + + Character.digit(s.charAt(i+1), 16)); + } + return data; + } + + /* + public static byte[] intToByteArray(int value) { + return new byte[] { + (byte)(value >>> 24), + (byte)(value >>> 16), + (byte)(value >>> 8), + (byte)value}; + } + */ + + public static int byteArrayToInt(byte[] b) { + return byteArrayToInt(b, 0); + } + + public static int byteArrayToInt(byte[] b, int offset) { + return byteArrayToInt(b, offset, b.length); + } + + public static int byteArrayToInt(byte[] b, int offset, int length) { + return (int) byteArrayToLong(b, offset, length); + } + + public static long byteArrayToLong(byte[] b, int offset, int length) { + if (b.length < length) + throw new IllegalArgumentException("length must be less than or equal to b.length"); + + long value = 0; + for (int i = 0; i < length; i++) { + int shift = (length - 1 - i) * 8; + value += (b[i + offset] & 0x000000FF) << shift; + } + return value; + } + + public static byte[] byteArraySlice(byte[] b, int offset, int length) { + byte[] ret = new byte[length]; + for (int i = 0; i < length; i++) + ret[i] = b[offset+i]; + return ret; + } + + public static String xmlNodeToString (Node node) throws Exception { + // The amount of code required to do simple things in Java is incredible. + Source source = new DOMSource(node); + StringWriter stringWriter = new StringWriter(); + Result result = new StreamResult(stringWriter); + TransformerFactory factory = TransformerFactory.newInstance(); + Transformer transformer = factory.newTransformer(); + transformer.setOutputProperty(OutputKeys.INDENT, "yes"); + transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "2"); + transformer.setURIResolver(null); + transformer.transform(source, result); + return stringWriter.getBuffer().toString(); + } + + public static String getErrorMessage (Throwable ex) { + String errorMessage = ex.getLocalizedMessage(); + if (errorMessage == null) + errorMessage = ex.getMessage(); + if (errorMessage == null) + errorMessage = ex.toString(); + + if (ex.getCause() != null) { + String causeMessage = ex.getCause().getLocalizedMessage(); + if (causeMessage == null) + causeMessage = ex.getCause().getMessage(); + if (causeMessage == null) + causeMessage = ex.getCause().toString(); + + if (causeMessage != null) + errorMessage += ": " + causeMessage; + } + + return errorMessage; + } + + + public static T findInList(List list, Matcher matcher) { + for (T item : list) { + if (matcher.matches(item)) { + return item; + } + } + return null; + } + + public static interface Matcher { + public boolean matches(T t); + } + + public static int convertBCDtoInteger(byte data) { + return (((data & (char)0xF0) >> 4) * 10) + ((data & (char)0x0F)); + } + + public static int getBitsFromInteger(int buffer, int iStartBit, int iLength) { + return (buffer >> (iStartBit)) & ((char)0xFF >> (8 - iLength)); + } + + /* Based on function from mfocGUI by 'Huuf' (http://www.huuf.info/OV/) */ + public static int getBitsFromBuffer(byte[] buffer, int iStartBit, int iLength) { + int iEndBit = iStartBit + iLength - 1; + int iSByte = iStartBit / 8; + int iSBit = iStartBit % 8; + int iEByte = iEndBit / 8; + int iEBit = iEndBit % 8; + + if (iSByte == iEByte) { + return (int)(((char)buffer[iEByte] >> (7 - iEBit)) & ((char)0xFF >> (8 - iLength))); + } else { + int uRet = (((char)buffer[iSByte] & (char)((char)0xFF >> iSBit)) << (((iEByte - iSByte - 1) * 8) + (iEBit + 1))); + + for (int i = iSByte + 1; i < iEByte; i++) { + uRet |= (((char)buffer[i] & (char)0xFF) << (((iEByte - i - 1) * 8) + (iEBit + 1))); + } + + uRet |= (((char)buffer[iEByte] & (char)0xFF)) >> (7 - iEBit); + + return uRet; + } + } + + + public static DesfireFileSettings selectAppFile(DesfireProtocol tag, int appID, int fileID) { + try { + tag.selectApp(appID); + } catch (DesfireException e) { + Log.w(TAG,"App not found"); + return null; + } + try { + return tag.getFileSettings(fileID); + } catch (DesfireException e) { + Log.w(TAG,"File not found"); + return null; + } + } + + public static boolean arrayContains(int[] arr, int item) { + for (int i: arr) + if (i==item) + return true; + return false; + } + + public static boolean containsAppFile(DesfireProtocol tag, int appID, int fileID) { + try { + tag.selectApp(appID); + } catch (DesfireException e) { + Log.w(TAG,"App not found"); + Log.w(TAG, e); + return false; + } + try { + return arrayContains(tag.getFileList(),fileID); + } catch (DesfireException e) { + Log.w(TAG,"File not found"); + Log.w(TAG, e); + return false; + } + } +} diff --git a/app/src/main/java/com/codebutler/farebot/card/desfire/DesfireApplication.java b/app/src/main/java/com/codebutler/farebot/card/desfire/DesfireApplication.java new file mode 100644 index 0000000..bda03a6 --- /dev/null +++ b/app/src/main/java/com/codebutler/farebot/card/desfire/DesfireApplication.java @@ -0,0 +1,77 @@ +/* + * DesfireApplication.java + * + * Copyright (C) 2011 Eric Butler + * + * Authors: + * Eric Butler + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.codebutler.farebot.card.desfire; + +import android.os.Parcel; +import android.os.Parcelable; + +public class DesfireApplication implements Parcelable { + private int mId; + private DesfireFile[] mFiles; + + public DesfireApplication (int id, DesfireFile[] files) { + mId = id; + mFiles = files; + } + + public int getId () { + return mId; + } + + public DesfireFile[] getFiles () { + return mFiles; + } + + public DesfireFile getFile (int fileId) { + for (DesfireFile file : mFiles) { + if (file.getId() == fileId) + return file; + } + return null; + } + + public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { + public DesfireApplication createFromParcel(Parcel source) { + int id = source.readInt(); + + DesfireFile[] files = new DesfireFile[source.readInt()]; + source.readTypedArray(files, DesfireFile.CREATOR); + + return new DesfireApplication(id, files); + } + + public DesfireApplication[] newArray (int size) { + return new DesfireApplication[size]; + } + }; + + public void writeToParcel (Parcel parcel, int flags) { + parcel.writeInt(mId); + parcel.writeInt(mFiles.length); + parcel.writeTypedArray(mFiles, flags); + } + + public int describeContents () { + return 0; + } +} \ No newline at end of file diff --git a/app/src/main/java/com/codebutler/farebot/card/desfire/DesfireException.java b/app/src/main/java/com/codebutler/farebot/card/desfire/DesfireException.java new file mode 100644 index 0000000..b737d38 --- /dev/null +++ b/app/src/main/java/com/codebutler/farebot/card/desfire/DesfireException.java @@ -0,0 +1,13 @@ +package com.codebutler.farebot.card.desfire; + +/** + * Created by Jakob Wenzel on 16.11.13. + */ +public class DesfireException extends Exception { + public DesfireException(String message) { + super(message); + } + public DesfireException(Throwable cause) { + super(cause); + } +} diff --git a/app/src/main/java/com/codebutler/farebot/card/desfire/DesfireFile.java b/app/src/main/java/com/codebutler/farebot/card/desfire/DesfireFile.java new file mode 100644 index 0000000..80ae5fc --- /dev/null +++ b/app/src/main/java/com/codebutler/farebot/card/desfire/DesfireFile.java @@ -0,0 +1,135 @@ +/* + * DesfireFile.java + * + * Copyright (C) 2011 Eric Butler + * + * Authors: + * Eric Butler + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.codebutler.farebot.card.desfire; + +import org.apache.commons.lang3.ArrayUtils; + +import android.os.Parcel; +import android.os.Parcelable; + +import com.codebutler.farebot.card.desfire.DesfireFileSettings.RecordDesfireFileSettings; + +public class DesfireFile implements Parcelable { + private int mId; + private DesfireFileSettings mSettings; + private byte[] mData; + + public static DesfireFile create (int fileId, DesfireFileSettings fileSettings, byte[] fileData) { + if (fileSettings instanceof RecordDesfireFileSettings) + return new RecordDesfireFile(fileId, fileSettings, fileData); + else + return new DesfireFile(fileId, fileSettings, fileData); + } + + private DesfireFile (int fileId, DesfireFileSettings fileSettings, byte[] fileData) { + mId = fileId; + mSettings = fileSettings; + mData = fileData; + } + + public DesfireFileSettings getFileSettings () { + return mSettings; + } + + public int getId () { + return mId; + } + + public byte[] getData () { + return mData; + } + + public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { + public DesfireFile createFromParcel(Parcel source) { + int fileId = source.readInt(); + + boolean isError = (source.readInt() == 1); + + if (!isError) { + DesfireFileSettings fileSettings = (DesfireFileSettings) source.readParcelable(DesfireFileSettings.class.getClassLoader()); + int dataLength = source.readInt(); + byte[] fileData = new byte[dataLength]; + source.readByteArray(fileData); + + return DesfireFile.create(fileId, fileSettings, fileData); + } else { + return new InvalidDesfireFile(fileId, source.readString()); + } + } + + public DesfireFile[] newArray (int size) { + return new DesfireFile[size]; + } + }; + + public void writeToParcel (Parcel parcel, int flags) { + parcel.writeInt(mId); + if (this instanceof InvalidDesfireFile) { + parcel.writeInt(1); + parcel.writeString(((InvalidDesfireFile)this).getErrorMessage()); + } else { + parcel.writeInt(0); + parcel.writeParcelable(mSettings, 0); + parcel.writeInt(mData.length); + parcel.writeByteArray(mData); + } + } + + public int describeContents () { + return 0; + } + + public static class RecordDesfireFile extends DesfireFile { + private DesfireRecord[] mRecords; + + private RecordDesfireFile (int fileId, DesfireFileSettings fileSettings, byte[] fileData) { + super(fileId, fileSettings, fileData); + + RecordDesfireFileSettings settings = (RecordDesfireFileSettings) fileSettings; + + DesfireRecord[] records = new DesfireRecord[settings.curRecords]; + for (int i = 0; i < settings.curRecords; i++) { + int offset = settings.recordSize * i; + records[i] = new DesfireRecord(ArrayUtils.subarray(getData(), offset, offset + settings.recordSize)); + } + mRecords = records; + } + + public DesfireRecord[] getRecords () { + return mRecords; + } + } + + public static class InvalidDesfireFile extends DesfireFile { + private String mErrorMessage; + + public InvalidDesfireFile (int fileId, String errorMessage) { + super(fileId, null, new byte[0]); + mErrorMessage = errorMessage; + } + + public String getErrorMessage () { + return mErrorMessage; + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/codebutler/farebot/card/desfire/DesfireFileSettings.java b/app/src/main/java/com/codebutler/farebot/card/desfire/DesfireFileSettings.java new file mode 100644 index 0000000..17220b7 --- /dev/null +++ b/app/src/main/java/com/codebutler/farebot/card/desfire/DesfireFileSettings.java @@ -0,0 +1,241 @@ +/* + * DesfireFileSettings.java + * + * Copyright (C) 2011 Eric Butler + * + * Authors: + * Eric Butler + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.codebutler.farebot.card.desfire; + +import java.io.ByteArrayInputStream; + +import org.apache.commons.lang3.ArrayUtils; + +import android.os.Parcel; +import android.os.Parcelable; + +import com.codebutler.farebot.Utils; + +public abstract class DesfireFileSettings implements Parcelable { + public final byte fileType; + public final byte commSetting; + public final byte[] accessRights; + + /* DesfireFile Types */ + static final byte STANDARD_DATA_FILE = (byte) 0x00; + static final byte BACKUP_DATA_FILE = (byte) 0x01; + static final byte VALUE_FILE = (byte) 0x02; + static final byte LINEAR_RECORD_FILE = (byte) 0x03; + static final byte CYCLIC_RECORD_FILE = (byte) 0x04; + + public static DesfireFileSettings Create (byte[] data) throws DesfireException { + byte fileType = (byte) data[0]; + + ByteArrayInputStream stream = new ByteArrayInputStream(data); + + if (fileType == STANDARD_DATA_FILE || fileType == BACKUP_DATA_FILE) + return new StandardDesfireFileSettings(stream); + else if (fileType == LINEAR_RECORD_FILE || fileType == CYCLIC_RECORD_FILE) + return new RecordDesfireFileSettings(stream); + else if (fileType == VALUE_FILE) + return new ValueDesfireFileSettings(stream); + else + throw new DesfireException("Unknown file type: " + Integer.toHexString(fileType)); + } + + private DesfireFileSettings (ByteArrayInputStream stream) { + fileType = (byte) stream.read(); + commSetting = (byte) stream.read(); + + accessRights = new byte[2]; + stream.read(accessRights, 0, accessRights.length); + } + + private DesfireFileSettings (byte fileType, byte commSetting, byte[] accessRights) { + this.fileType = fileType; + this.commSetting = commSetting; + this.accessRights = accessRights; + } + + public String getFileTypeName () { + switch (fileType) { + case STANDARD_DATA_FILE: + return "Standard"; + case BACKUP_DATA_FILE: + return "Backup"; + case VALUE_FILE: + return "Value"; + case LINEAR_RECORD_FILE: + return "Linear Record"; + case CYCLIC_RECORD_FILE: + return "Cyclic Record"; + default: + return "Unknown"; + } + } + + public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { + public DesfireFileSettings createFromParcel(Parcel source) { + byte fileType = source.readByte(); + byte commSetting = source.readByte(); + byte[] accessRights = new byte[source.readInt()]; + source.readByteArray(accessRights); + + if (fileType == STANDARD_DATA_FILE || fileType == BACKUP_DATA_FILE) { + int fileSize = source.readInt(); + return new StandardDesfireFileSettings(fileType, commSetting, accessRights, fileSize); + } else if (fileType == LINEAR_RECORD_FILE || fileType == CYCLIC_RECORD_FILE) { + int recordSize = source.readInt(); + int maxRecords = source.readInt(); + int curRecords = source.readInt(); + return new RecordDesfireFileSettings(fileType, commSetting, accessRights, recordSize, maxRecords, curRecords); + } else { + return new UnsupportedDesfireFileSettings(fileType); + } + } + + public DesfireFileSettings[] newArray(int size) { + return new DesfireFileSettings[size]; + } + }; + + public void writeToParcel (Parcel parcel, int flags) { + parcel.writeByte(fileType); + parcel.writeByte(commSetting); + parcel.writeInt(accessRights.length); + parcel.writeByteArray(accessRights); + } + + public int describeContents () { + return 0; + } + + public static class StandardDesfireFileSettings extends DesfireFileSettings { + public final int fileSize; + + private StandardDesfireFileSettings (ByteArrayInputStream stream) { + super(stream); + byte[] buf = new byte[3]; + stream.read(buf, 0, buf.length); + ArrayUtils.reverse(buf); + fileSize = Utils.byteArrayToInt(buf); + } + + StandardDesfireFileSettings (byte fileType, byte commSetting, byte[] accessRights, int fileSize) { + super(fileType, commSetting, accessRights); + this.fileSize = fileSize; + } + + @Override + public void writeToParcel (Parcel parcel, int flags) { + super.writeToParcel(parcel, flags); + parcel.writeInt(fileSize); + } + } + + public static class RecordDesfireFileSettings extends DesfireFileSettings { + public final int recordSize; + public final int maxRecords; + public final int curRecords; + + public RecordDesfireFileSettings(ByteArrayInputStream stream) { + super(stream); + + byte[] buf = new byte[3]; + stream.read(buf, 0, buf.length); + ArrayUtils.reverse(buf); + recordSize = Utils.byteArrayToInt(buf); + + buf = new byte[3]; + stream.read(buf, 0, buf.length); + ArrayUtils.reverse(buf); + maxRecords = Utils.byteArrayToInt(buf); + + buf = new byte[3]; + stream.read(buf, 0, buf.length); + ArrayUtils.reverse(buf); + curRecords = Utils.byteArrayToInt(buf); + } + + RecordDesfireFileSettings (byte fileType, byte commSetting, byte[] accessRights, int recordSize, int maxRecords, int curRecords) { + super(fileType, commSetting, accessRights); + this.recordSize = recordSize; + this.maxRecords = maxRecords; + this.curRecords = curRecords; + } + + @Override + public void writeToParcel (Parcel parcel, int flags) { + super.writeToParcel(parcel, flags); + parcel.writeInt(recordSize); + parcel.writeInt(maxRecords); + parcel.writeInt(curRecords); + } + } + + + + + public static class ValueDesfireFileSettings extends DesfireFileSettings { + public final int lowerLimit; + public final int upperLimit; + public final int value; + public final byte limitedCreditEnabled; + + public ValueDesfireFileSettings(ByteArrayInputStream stream) { + super(stream); + + byte[] buf = new byte[4]; + stream.read(buf, 0, buf.length); + ArrayUtils.reverse(buf); + lowerLimit = Utils.byteArrayToInt(buf); + + buf = new byte[4]; + stream.read(buf, 0, buf.length); + ArrayUtils.reverse(buf); + upperLimit = Utils.byteArrayToInt(buf); + + buf = new byte[4]; + stream.read(buf, 0, buf.length); + ArrayUtils.reverse(buf); + value = Utils.byteArrayToInt(buf); + + + buf = new byte[1]; + stream.read(buf, 0, buf.length); + limitedCreditEnabled = buf[0]; + + //http://www.skyetek.com/docs/m2/desfire.pdf + //http://neteril.org/files/M075031_desfire.pdf + } + + @Override + public void writeToParcel (Parcel parcel, int flags) { + super.writeToParcel(parcel, flags); + parcel.writeInt(lowerLimit); + parcel.writeInt(upperLimit); + parcel.writeInt(value); + parcel.writeByte(limitedCreditEnabled); + } + } + public static class UnsupportedDesfireFileSettings extends DesfireFileSettings { + public UnsupportedDesfireFileSettings(byte fileType) { + super(fileType, Byte.MIN_VALUE, new byte[0]); + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/codebutler/farebot/card/desfire/DesfireManufacturingData.java b/app/src/main/java/com/codebutler/farebot/card/desfire/DesfireManufacturingData.java new file mode 100644 index 0000000..becc763 --- /dev/null +++ b/app/src/main/java/com/codebutler/farebot/card/desfire/DesfireManufacturingData.java @@ -0,0 +1,173 @@ +/* + * DesfireManufacturingData.java + * + * Copyright (C) 2011 Eric Butler + * + * Authors: + * Eric Butler + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.codebutler.farebot.card.desfire; + +import android.os.Parcel; +import android.os.Parcelable; +import com.codebutler.farebot.Utils; +import org.w3c.dom.Element; + +import java.io.ByteArrayInputStream; + +public class DesfireManufacturingData implements Parcelable { + public final int hwVendorID; + public final int hwType; + public final int hwSubType; + public final int hwMajorVersion; + public final int hwMinorVersion; + public final int hwStorageSize; + public final int hwProtocol; + + public final int swVendorID; + public final int swType; + public final int swSubType; + public final int swMajorVersion; + public final int swMinorVersion; + public final int swStorageSize; + public final int swProtocol; + + public final int uid; + public final int batchNo; + public final int weekProd; + public final int yearProd; + + public DesfireManufacturingData (byte[] data) { + ByteArrayInputStream stream = new ByteArrayInputStream(data); + hwVendorID = stream.read(); + hwType = stream.read(); + hwSubType = stream.read(); + hwMajorVersion = stream.read(); + hwMinorVersion = stream.read(); + hwStorageSize = stream.read(); + hwProtocol = stream.read(); + + swVendorID = stream.read(); + swType = stream.read(); + swSubType = stream.read(); + swMajorVersion = stream.read(); + swMinorVersion = stream.read(); + swStorageSize = stream.read(); + swProtocol = stream.read(); + + // FIXME: This has fewer digits than what's contained in EXTRA_ID, why? + byte[] buf = new byte[7]; + stream.read(buf, 0, buf.length); + uid = Utils.byteArrayToInt(buf); + + // FIXME: This is returning a negative number. Probably is unsigned. + buf = new byte[5]; + stream.read(buf, 0, buf.length); + batchNo = Utils.byteArrayToInt(buf); + + // FIXME: These numbers aren't making sense. + weekProd = stream.read(); + yearProd = stream.read(); + } + + public static DesfireManufacturingData fromXml (Element element) { + return new DesfireManufacturingData(element); + } + + private DesfireManufacturingData (Element element) { + hwVendorID = Integer.parseInt(element.getElementsByTagName("hw-vendor-id").item(0).getTextContent()); + hwType = Integer.parseInt(element.getElementsByTagName("hw-type").item(0).getTextContent()); + hwSubType = Integer.parseInt(element.getElementsByTagName("hw-sub-type").item(0).getTextContent()); + hwMajorVersion = Integer.parseInt(element.getElementsByTagName("hw-major-version").item(0).getTextContent()); + hwMinorVersion = Integer.parseInt(element.getElementsByTagName("hw-minor-version").item(0).getTextContent()); + hwStorageSize = Integer.parseInt(element.getElementsByTagName("hw-storage-size").item(0).getTextContent()); + hwProtocol = Integer.parseInt(element.getElementsByTagName("hw-protocol").item(0).getTextContent()); + + swVendorID = Integer.parseInt(element.getElementsByTagName("sw-vendor-id").item(0).getTextContent()); + swType = Integer.parseInt(element.getElementsByTagName("sw-type").item(0).getTextContent()); + swSubType = Integer.parseInt(element.getElementsByTagName("sw-sub-type").item(0).getTextContent()); + swMajorVersion = Integer.parseInt(element.getElementsByTagName("sw-major-version").item(0).getTextContent()); + swMinorVersion = Integer.parseInt(element.getElementsByTagName("sw-minor-version").item(0).getTextContent()); + swStorageSize = Integer.parseInt(element.getElementsByTagName("sw-storage-size").item(0).getTextContent()); + swProtocol = Integer.parseInt(element.getElementsByTagName("sw-protocol").item(0).getTextContent()); + + uid = Integer.parseInt(element.getElementsByTagName("uid").item(0).getTextContent()); + batchNo = Integer.parseInt(element.getElementsByTagName("batch-no").item(0).getTextContent()); + weekProd = Integer.parseInt(element.getElementsByTagName("week-prod").item(0).getTextContent()); + yearProd = Integer.parseInt(element.getElementsByTagName("year-prod").item(0).getTextContent()); + } + + private DesfireManufacturingData (Parcel parcel) { + hwVendorID = parcel.readInt(); + hwType = parcel.readInt(); + hwSubType = parcel.readInt(); + hwMajorVersion = parcel.readInt(); + hwMinorVersion = parcel.readInt(); + hwStorageSize = parcel.readInt(); + hwProtocol = parcel.readInt(); + + swVendorID = parcel.readInt(); + swType = parcel.readInt(); + swSubType = parcel.readInt(); + swMajorVersion = parcel.readInt(); + swMinorVersion = parcel.readInt(); + swStorageSize = parcel.readInt(); + swProtocol = parcel.readInt(); + + uid = parcel.readInt(); + batchNo = parcel.readInt(); + weekProd = parcel.readInt(); + yearProd = parcel.readInt(); + } + + public void writeToParcel(Parcel parcel, int flags) { + parcel.writeInt(hwVendorID); + parcel.writeInt(hwType); + parcel.writeInt(hwSubType); + parcel.writeInt(hwMajorVersion); + parcel.writeInt(hwMinorVersion); + parcel.writeInt(hwStorageSize); + parcel.writeInt(hwProtocol); + + parcel.writeInt(swVendorID); + parcel.writeInt(swType); + parcel.writeInt(swSubType); + parcel.writeInt(swMajorVersion); + parcel.writeInt(swMinorVersion); + parcel.writeInt(swStorageSize); + parcel.writeInt(swProtocol); + + parcel.writeInt(uid); + parcel.writeInt(batchNo); + parcel.writeInt(weekProd); + parcel.writeInt(yearProd); + } + + public int describeContents() { + return 0; + } + + public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { + public DesfireManufacturingData createFromParcel(Parcel source) { + return new DesfireManufacturingData(source); + } + + public DesfireManufacturingData[] newArray(int size) { + return new DesfireManufacturingData[size]; + } + }; +} \ No newline at end of file diff --git a/app/src/main/java/com/codebutler/farebot/card/desfire/DesfireProtocol.java b/app/src/main/java/com/codebutler/farebot/card/desfire/DesfireProtocol.java new file mode 100644 index 0000000..28404d7 --- /dev/null +++ b/app/src/main/java/com/codebutler/farebot/card/desfire/DesfireProtocol.java @@ -0,0 +1,188 @@ +/* + * DesfireProtocol.java + * + * Copyright (C) 2011 Eric Butler + * + * Authors: + * Eric Butler + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.codebutler.farebot.card.desfire; + +import android.nfc.tech.IsoDep; +import com.codebutler.farebot.Utils; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; + +import org.apache.commons.lang3.ArrayUtils; + +public class DesfireProtocol { + /* Commands */ + static final byte GET_MANUFACTURING_DATA = (byte) 0x60; + static final byte GET_APPLICATION_DIRECTORY = (byte) 0x6A; + static final byte GET_ADDITIONAL_FRAME = (byte) 0xAF; + static final byte SELECT_APPLICATION = (byte) 0x5A; + static final byte READ_DATA = (byte) 0xBD; + static final byte READ_RECORD = (byte) 0xBB; + static final byte READ_VALUE = (byte) 0x6C; + static final byte GET_FILES = (byte) 0x6F; + static final byte GET_FILE_SETTINGS = (byte) 0xF5; + + /* Status codes */ + static final byte OPERATION_OK = (byte) 0x00; + static final byte PERMISSION_DENIED = (byte) 0x9D; + static final byte ADDITIONAL_FRAME = (byte) 0xAF; + + private IsoDep mTagTech; + + public DesfireProtocol(IsoDep tagTech) { + mTagTech = tagTech; + } + + public DesfireManufacturingData getManufacturingData() throws DesfireException { + byte[] respBuffer = sendRequest(GET_MANUFACTURING_DATA); + + if (respBuffer.length != 28) + throw new DesfireException("Invalid response"); + + return new DesfireManufacturingData(respBuffer); + } + + public int[] getAppList() throws DesfireException { + byte[] appDirBuf = sendRequest(GET_APPLICATION_DIRECTORY); + + int[] appIds = new int[appDirBuf.length / 3]; + + for (int app = 0; app < appDirBuf.length; app += 3) { + byte[] appId = new byte[3]; + System.arraycopy(appDirBuf, app, appId, 0, 3); + + appIds[app / 3] = Utils.byteArrayToInt(appId); + } + + return appIds; + } + + public void selectApp (int appId) throws DesfireException { + byte[] appIdBuff = new byte[3]; + appIdBuff[0] = (byte) ((appId & 0xFF0000) >> 16); + appIdBuff[1] = (byte) ((appId & 0xFF00) >> 8); + appIdBuff[2] = (byte) (appId & 0xFF); + + sendRequest(SELECT_APPLICATION, appIdBuff); + } + + public int[] getFileList() throws DesfireException { + byte[] buf = sendRequest(GET_FILES); + int[] fileIds = new int[buf.length]; + for (int x = 0; x < buf.length; x++) { + fileIds[x] = (int)buf[x]; + } + return fileIds; + } + + public DesfireFileSettings getFileSettings (int fileNo) throws DesfireException { + byte[] data = new byte[0]; + data = sendRequest(GET_FILE_SETTINGS, new byte[] { (byte) fileNo }); + return DesfireFileSettings.Create(data); + } + + public byte[] readFile (int fileNo) throws DesfireException { + return sendRequest(READ_DATA, new byte[] { + (byte) fileNo, + (byte) 0x0, (byte) 0x0, (byte) 0x0, + (byte) 0x0, (byte) 0x0, (byte) 0x0 + }); + } + + public byte[] readRecord (int fileNum) throws DesfireException { + return sendRequest(READ_RECORD, new byte[]{ + (byte) fileNum, + (byte) 0x0, (byte) 0x0, (byte) 0x0, + (byte) 0x0, (byte) 0x0, (byte) 0x0 + }); + } + + public int readValue(int fileNum) throws DesfireException { + byte[] buf = sendRequest(READ_VALUE, new byte[]{ + (byte) fileNum + }); + ArrayUtils.reverse(buf); + return Utils.byteArrayToInt(buf); + } + + + private byte[] sendRequest (byte command) throws DesfireException { + return sendRequest(command, null); + } + + private byte[] sendRequest (byte command, byte[] parameters) throws DesfireException { + ByteArrayOutputStream output = new ByteArrayOutputStream(); + + byte[] recvBuffer = new byte[0]; + try { + recvBuffer = mTagTech.transceive(wrapMessage(command, parameters)); + } catch (IOException e) { + throw new DesfireException(e); + } + + while (true) { + if (recvBuffer[recvBuffer.length - 2] != (byte) 0x91) + throw new DesfireException("Invalid response"); + + output.write(recvBuffer, 0, recvBuffer.length - 2); + + byte status = recvBuffer[recvBuffer.length - 1]; + if (status == OPERATION_OK) { + break; + } else if (status == ADDITIONAL_FRAME) { + try { + recvBuffer = mTagTech.transceive(wrapMessage(GET_ADDITIONAL_FRAME, null)); + } catch (IOException e) { + throw new DesfireException(e); + } + } else if (status == PERMISSION_DENIED) { + throw new DesfireException("Permission denied"); + } else { + throw new DesfireException("Unknown status code: " + Integer.toHexString(status & 0xFF)); + } + } + + return output.toByteArray(); + } + + private byte[] wrapMessage (byte command, byte[] parameters) throws DesfireException { + ByteArrayOutputStream stream = new ByteArrayOutputStream(); + + stream.write((byte) 0x90); + stream.write(command); + stream.write((byte) 0x00); + stream.write((byte) 0x00); + if (parameters != null) { + stream.write((byte) parameters.length); + try { + stream.write(parameters); + } catch (IOException e) { + throw new DesfireException(e); + } + } + stream.write((byte) 0x00); + + return stream.toByteArray(); + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/codebutler/farebot/card/desfire/DesfireRecord.java b/app/src/main/java/com/codebutler/farebot/card/desfire/DesfireRecord.java new file mode 100644 index 0000000..2dfeda4 --- /dev/null +++ b/app/src/main/java/com/codebutler/farebot/card/desfire/DesfireRecord.java @@ -0,0 +1,35 @@ +/* + * DesfireRecord.java + * + * Copyright (C) 2011 Eric Butler + * + * Authors: + * Eric Butler + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.codebutler.farebot.card.desfire; + +public class DesfireRecord { + private byte[] mData; + + public DesfireRecord (byte[] data) { + mData = data; + } + + public byte[] getData () { + return mData; + } +} \ No newline at end of file diff --git a/app/src/main/java/de/dhbwloe/campusapp/AppCompatPreferenceActivity.java b/app/src/main/java/de/dhbwloe/campusapp/AppCompatPreferenceActivity.java new file mode 100644 index 0000000..3423ee5 --- /dev/null +++ b/app/src/main/java/de/dhbwloe/campusapp/AppCompatPreferenceActivity.java @@ -0,0 +1,109 @@ +package de.dhbwloe.campusapp; + +import android.content.res.Configuration; +import android.os.Bundle; +import android.preference.PreferenceActivity; +import android.support.annotation.LayoutRes; +import android.support.annotation.Nullable; +import android.support.v7.app.ActionBar; +import android.support.v7.app.AppCompatDelegate; +import android.support.v7.widget.Toolbar; +import android.view.MenuInflater; +import android.view.View; +import android.view.ViewGroup; + +/** + * A {@link android.preference.PreferenceActivity} which implements and proxies the necessary calls + * to be used with AppCompat. + */ +public abstract class AppCompatPreferenceActivity extends PreferenceActivity { + + private AppCompatDelegate mDelegate; + + @Override + protected void onCreate(Bundle savedInstanceState) { + getDelegate().installViewFactory(); + getDelegate().onCreate(savedInstanceState); + super.onCreate(savedInstanceState); + } + + @Override + protected void onPostCreate(Bundle savedInstanceState) { + super.onPostCreate(savedInstanceState); + getDelegate().onPostCreate(savedInstanceState); + } + + public ActionBar getSupportActionBar() { + return getDelegate().getSupportActionBar(); + } + + public void setSupportActionBar(@Nullable Toolbar toolbar) { + getDelegate().setSupportActionBar(toolbar); + } + + @Override + public MenuInflater getMenuInflater() { + return getDelegate().getMenuInflater(); + } + + @Override + public void setContentView(@LayoutRes int layoutResID) { + getDelegate().setContentView(layoutResID); + } + + @Override + public void setContentView(View view) { + getDelegate().setContentView(view); + } + + @Override + public void setContentView(View view, ViewGroup.LayoutParams params) { + getDelegate().setContentView(view, params); + } + + @Override + public void addContentView(View view, ViewGroup.LayoutParams params) { + getDelegate().addContentView(view, params); + } + + @Override + protected void onPostResume() { + super.onPostResume(); + getDelegate().onPostResume(); + } + + @Override + protected void onTitleChanged(CharSequence title, int color) { + super.onTitleChanged(title, color); + getDelegate().setTitle(title); + } + + @Override + public void onConfigurationChanged(Configuration newConfig) { + super.onConfigurationChanged(newConfig); + getDelegate().onConfigurationChanged(newConfig); + } + + @Override + protected void onStop() { + super.onStop(); + getDelegate().onStop(); + } + + @Override + protected void onDestroy() { + super.onDestroy(); + getDelegate().onDestroy(); + } + + public void invalidateOptionsMenu() { + getDelegate().invalidateOptionsMenu(); + } + + private AppCompatDelegate getDelegate() { + if (mDelegate == null) { + mDelegate = AppCompatDelegate.create(this, null); + } + return mDelegate; + } +} diff --git a/app/src/main/java/de/dhbwloe/campusapp/CampusApp.java b/app/src/main/java/de/dhbwloe/campusapp/CampusApp.java new file mode 100644 index 0000000..e9200ac --- /dev/null +++ b/app/src/main/java/de/dhbwloe/campusapp/CampusApp.java @@ -0,0 +1,296 @@ +package de.dhbwloe.campusapp; + +import android.content.Context; +import android.content.Intent; +import android.nfc.NfcAdapter; +import android.os.Bundle; +import android.support.design.widget.FloatingActionButton; +import android.support.design.widget.Snackbar; +import android.support.v4.app.FragmentActivity; +import android.util.Log; +import android.view.KeyEvent; +import android.view.View; +import android.support.design.widget.NavigationView; +import android.support.v4.view.GravityCompat; +import android.support.v4.widget.DrawerLayout; +import android.support.v7.app.ActionBarDrawerToggle; +import android.support.v7.app.AppCompatActivity; +import android.support.v7.widget.Toolbar; +import android.view.Menu; +import android.view.MenuItem; +import android.view.inputmethod.EditorInfo; +import android.view.inputmethod.InputMethodManager; +import android.widget.EditText; +import android.widget.ImageView; +import android.widget.LinearLayout; +import android.widget.TextView; + +import java.util.Date; + +import de.dhbwloe.campusapp.nfcreader.NfcCardListener; + +public class CampusApp extends FragmentActivity { + + private boolean bSearchActive = false; + private CampusAppContext AppContext = null; + + /* + * Dev Info: + * + * Die App besteht aus einer einzigen Activity, auf welcher dynamisch Fragmente platziert werden. + * Das Menü, sowie die Headerleiste befinden sich auf der Activity, sind dementsprechend immer verfügbar. + * + * Zur laufzeit der App wird eine einzige Instanz der Klasse CampusAppContext angelegt. + * Dieser Kontext dient als einstiegspunkt für sämmtliche global verfügbaren verwaltungs tools. + * Dazu gehören: + * AppContext.getNavigationManager() NavigationManager Verwaltet die angezeigten Fragmente und deren Navigation + * .navigatePage("Dashboard") to navigate to the Dashboard + * ... to be continued ... + */ + + @Override + protected void onCreate(Bundle savedInstanceState) { + Log.i("CampusApp", "Event: onCreate"); + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_campus_app); + + AppContext = CampusAppContext.getInstance(); + if(AppContext == null) + AppContext = new CampusAppContext(this, R.id.fragment_container); + else + AppContext.setMainActivity(this); + + boolean instantRestore = false; + if(savedInstanceState != null) { + long lastrun = savedInstanceState.getLong("lastrun"); + if(((new Date()).getTime()/1000) - lastrun < 30) { + instantRestore = true; + + AppContext.setTitle(savedInstanceState.getString("activetitle")); + + } + } + + prepareMainUi(); + if(instantRestore) // orientation change + loadMainUi(); + else + // don't add navigation to SlashScreen, or even from SplashScreen to Dashboard to the history log! + // it would be very strange to return to the SplashScreen or an "empty" page on return ;) + AppContext.getNavigationManager().navigatePage("SplashScreen", null, false); + + } + + @Override + public void onSaveInstanceState(Bundle savedInstanceState) { + Log.i("CampusApp", "Event: onSaveInstanceState"); + // Save instance state during "restarts" due to orientation changes. + // We don't want to see the splash screen everytime the orientation changes ;) + savedInstanceState.putLong("lastrun", (new Date()).getTime() / 1000); + savedInstanceState.putString("activepage", AppContext.getNavigationManager().getCurrentPageName()); + TextView titleView = (TextView) findViewById(R.id.title); + savedInstanceState.putString("activetitle", titleView.getText().toString()); + + // Always call the superclass so it can save the view hierarchy state + super.onSaveInstanceState(savedInstanceState); + } + + public void prepareMainUi() { + ImageView btnOpenSearch = (ImageView) findViewById(R.id.search_button); + btnOpenSearch.setVisibility(View.GONE); + } + + public void loadMainUi() { + ImageView btnOpenSearch = (ImageView) findViewById(R.id.search_button); + + btnOpenSearch.setVisibility(View.VISIBLE); + setupActionBar(); + setupSearchTriggers(); + + AppContext.getNfcCardListener().startNfcListener(); + } + + private void setupActionBar() { + Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar); + DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout); + ActionBarDrawerToggle toggle = new ActionBarDrawerToggle(this, drawer, toolbar, R.string.navigation_drawer_open, R.string.navigation_drawer_close); + drawer.setDrawerListener(toggle); + toggle.syncState(); + + NavigationView navigationView = (NavigationView) findViewById(R.id.nav_view); + navigationView.setNavigationItemSelectedListener(new NavigationView.OnNavigationItemSelectedListener() { + @SuppressWarnings("StatementWithEmptyBody") + @Override + public boolean onNavigationItemSelected(MenuItem item) { + // Handle navigation view item clicks here. + int id = item.getItemId(); + + switch (id) { // Navigation Items from res/menu/activity_campus_app_drawer.xml + case R.id.nav_dashboard: + AppContext.getNavigationManager().navigatePage("Dashboard"); + break; + case R.id.nav_vorlesungsplan: + AppContext.getNavigationManager().navigatePage("Vorlesungsplan"); + break; + case R.id.nav_mensa: + AppContext.getNavigationManager().navigatePage("Mensa"); + break; + case R.id.nav_news: + AppContext.getNavigationManager().navigatePage("News"); + break; + case R.id.nav_settings: + Intent settings = new Intent(AppContext.getMainActivity(), SettingsActivity.class); + AppContext.getMainActivity().startActivity(settings); + break; + case R.id.nav_impressum: + AppContext.getNavigationManager().navigatePage("Impressum"); + break; + default: + return false; + } + + DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout); + drawer.closeDrawer(GravityCompat.START); + return true; + } + }); + } + + private void setupSearchTriggers() { + ImageView btnOpenSearch = (ImageView) findViewById(R.id.search_button); + ImageView btnCloseSearch = (ImageView) findViewById(R.id.search_clear); + EditText edtSearchInput = (EditText) findViewById(R.id.search_input); + TextView txtTitle = (TextView) findViewById(R.id.title); + + btnOpenSearch.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + triggerSearchPanel(true); + } + }); + edtSearchInput.setOnFocusChangeListener(new View.OnFocusChangeListener() { + @Override + public void onFocusChange(View v, boolean hasFocus) { + EditText edtSearchInput = (EditText) v; + if (!hasFocus && edtSearchInput.getText().length() == 0) { + triggerSearchPanel(false); + } + } + }); + edtSearchInput.setOnEditorActionListener(new TextView.OnEditorActionListener() { + @Override + public boolean onEditorAction(TextView v, int actionId, KeyEvent event) { + if (actionId == EditorInfo.IME_ACTION_SEARCH) { + EditText edtSearchInput = (EditText) v; + triggerSearchAction(edtSearchInput.getText().toString()); + triggerSearchPanel(false); + return true; + } + return false; + } + }); + btnCloseSearch.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + EditText edtSearchInput = (EditText) findViewById(R.id.search_input); + edtSearchInput.setText(""); + triggerSearchPanel(false); + } + }); + txtTitle.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + triggerSearchPanel(true); + } + }); + + } + + private void triggerSearchPanel(boolean show) { + if(bSearchActive == show) + return; + bSearchActive = show; + LinearLayout layTitleContainer = (LinearLayout) findViewById(R.id.title_container); + LinearLayout laySearchContainer = (LinearLayout) findViewById(R.id.search_container); + EditText edtSearchInput = (EditText) findViewById(R.id.search_input); + + InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE); + if(show) { + layTitleContainer.setVisibility(View.GONE); + laySearchContainer.setVisibility(View.VISIBLE); + edtSearchInput.requestFocus(); + + imm.showSoftInput(edtSearchInput, InputMethodManager.SHOW_IMPLICIT); + } else { + layTitleContainer.setVisibility(View.VISIBLE); + laySearchContainer.setVisibility(View.GONE); + + View view = this.getCurrentFocus(); + if (view != null) { + imm.hideSoftInputFromWindow(view.getWindowToken(), 0); + } + } + } + + private void triggerSearchAction(String query) { + Bundle bundle = new Bundle(); + bundle.putString("query", query); + + AppContext.getNavigationManager().navigatePage("AppSearch", bundle); + } + + @Override + public void onBackPressed() { + DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout); + if (drawer.isDrawerOpen(GravityCompat.START)) { + drawer.closeDrawer(GravityCompat.START); + } else if(!AppContext.getNavigationManager().back()) { + super.onBackPressed(); // trigger system action if internal navigation manager returns false for back() + } + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + // Inflate the menu; this adds items to the action bar if it is present. + getMenuInflater().inflate(R.menu.campus_app, menu); + return true; + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + // Handle action bar item clicks here. The action bar will + // automatically handle clicks on the Home/Up button, so long + // as you specify a parent activity in AndroidManifest.xml. + int id = item.getItemId(); + + //noinspection SimplifiableIfStatement + if (id == R.id.action_settings) { + return true; + } + + return super.onOptionsItemSelected(item); + } + + /* nfc listener related callbacks */ + @Override + public void onResume() { + super.onResume(); + Log.i("CampusApp", "onResume event"); + AppContext.getNfcCardListener().resumeForefrontDispatcher(); + } + + @Override + public void onPause() { + super.onPause(); + Log.i("CampusApp", "Event: onPause"); + AppContext.getNfcCardListener().pauseForefrontDispatcher(); + } + + @Override + public void onNewIntent(Intent intent) { + if (NfcAdapter.ACTION_TECH_DISCOVERED.equals(intent.getAction())) { + AppContext.getNfcCardListener().handleNfcEvent(intent); + } + } + +} diff --git a/app/src/main/java/de/dhbwloe/campusapp/CampusAppContext.java b/app/src/main/java/de/dhbwloe/campusapp/CampusAppContext.java new file mode 100644 index 0000000..4f42cb5 --- /dev/null +++ b/app/src/main/java/de/dhbwloe/campusapp/CampusAppContext.java @@ -0,0 +1,159 @@ +package de.dhbwloe.campusapp; + +import android.app.Activity; +import android.os.Bundle; +import android.util.Log; +import android.widget.TextView; + +import java.lang.reflect.Method; + +import de.dhbwloe.campusapp.database.DatabaseManager; +import de.dhbwloe.campusapp.fragments.AppSearch; +import de.dhbwloe.campusapp.fragments.Dashboard; +import de.dhbwloe.campusapp.fragments.FirstRun; +import de.dhbwloe.campusapp.fragments.Impressum; +import de.dhbwloe.campusapp.fragments.Mensa; +import de.dhbwloe.campusapp.fragments.MensaCard; +import de.dhbwloe.campusapp.fragments.MensaWochenplan; +import de.dhbwloe.campusapp.fragments.News; +import de.dhbwloe.campusapp.fragments.PopupFragment; +import de.dhbwloe.campusapp.fragments.SplashScreen; +import de.dhbwloe.campusapp.fragments.Vorlesungsplan; +import de.dhbwloe.campusapp.fragments.WebBrowser; +import de.dhbwloe.campusapp.fragments.WifiSettings; +import de.dhbwloe.campusapp.nfcreader.NfcCardInterface; +import de.dhbwloe.campusapp.nfcreader.NfcCardListener; +import de.dhbwloe.campusapp.database.NfcCardData; +import de.dhbwloe.campusapp.search.SearchIndices; + +/** CampusAppContext + * A context, that is pushed to each fragment. + * This context will be persistent during app execution. + */ +public class CampusAppContext { + private class AppPage { + String name; + Class fragment; + int fragementType; + + public AppPage(String name, Class fragment) { + this.name = name; + this.fragment = fragment; + } + + public AppPage(String name, Class fragment, int type) { + this.name = name; + this.fragment = fragment; + this.fragementType = type; + } + } + private final AppPage[] PAGES = { + new AppPage("SplashScreen", SplashScreen.class), + new AppPage("Dashboard", Dashboard.class), + new AppPage("AppSearch", AppSearch.class), + new AppPage("Vorlesungsplan", Vorlesungsplan.class), + new AppPage("Mensa", Mensa.class), + new AppPage("MensaCard", MensaCard.class, 3), + new AppPage("News", News.class), + new AppPage("WifiSettings", WifiSettings.class), + new AppPage("FirstRun", FirstRun.class), + new AppPage("Impressum", Impressum.class), + new AppPage("WebBrowser", WebBrowser.class), + new AppPage("WebBrowserPopup", WebBrowser.class, 3) + }; + + private static CampusAppContext instance; + public static CampusAppContext getInstance() { + Log.i("AppContext", "Request new context instance"); + return instance; + } + + private Activity oMainActivity; + private NavigationManager oNavigationManager; + private DatabaseManager oDatabaseManager; + private NfcCardListener oNfcCardListener; + + public CampusAppContext(CampusApp mainActivity, int fragmentContainerId) { + final CampusAppContext AppContext = this; + instance = this; + oMainActivity = mainActivity; + oNavigationManager = new NavigationManager(this, fragmentContainerId); + oDatabaseManager = new DatabaseManager(this); + oNfcCardListener = new NfcCardListener(this); + + for(int i = 0; i < PAGES.length; i++) + oNavigationManager.registerPage(PAGES[i].name, PAGES[i].fragment, PAGES[i].fragementType); + + oNfcCardListener.registerNfcCardInterface(new NfcCardInterface() { + @Override + public void onNfcReaderStateChanged(boolean state) { + + } + + @Override + public void onNfcReaderReceived(NfcCardData data) { + if (data != null) + AppContext.onNfcCardDataReceived(data); + } + }); + } + + public Activity getMainActivity() { + return oMainActivity; + } + public void setMainActivity(Activity activity) { + oMainActivity = activity; + } + + public NavigationManager getNavigationManager() { + return oNavigationManager; + } + + public void setTitle(String title) { + PopupFragment popup = oNavigationManager.getDialog(); + if(popup != null) { + popup.getDialog().setTitle(title); + } else { + TextView titleView = (TextView)oMainActivity.findViewById(R.id.title); + titleView.setText(title); + } + } + + public DatabaseManager getDatabaseManager() { + return oDatabaseManager; + } + + public void addDefaultSearchIndexes() { + for(int i = 0; i < PAGES.length; i++) { + try { + Method m = PAGES[i].fragment.getMethod("GetSearchIndices"); + Object result = m.invoke(null); + SearchIndices[] indices = (SearchIndices[]) result; + addSearchIndices(indices); + } catch (Exception e) { + } + } + } + + public void addSearchIndices(SearchIndices[] indices) { + oDatabaseManager.addSearchIndices(indices); + } + + public NfcCardListener getNfcCardListener() { + return oNfcCardListener; + } + + private void onNfcCardDataReceived(NfcCardData data) { + Bundle bundle = new Bundle(); + bundle.putDouble("balance", data.getBalance() / 100.0); + bundle.putString("data", data.getCardData()); + + String pagename = oNavigationManager.getCurrentPageName(); + if(pagename != null && pagename.equalsIgnoreCase("MensaCard")) { + MensaCard fragment = (MensaCard) oNavigationManager.getCurrentFragment(); + fragment.showNfcCardData(bundle); + } else + oNavigationManager.navigatePage("MensaCard", bundle); + } + +} diff --git a/app/src/main/java/de/dhbwloe/campusapp/CampusAppFragment.java b/app/src/main/java/de/dhbwloe/campusapp/CampusAppFragment.java new file mode 100644 index 0000000..e0aead8 --- /dev/null +++ b/app/src/main/java/de/dhbwloe/campusapp/CampusAppFragment.java @@ -0,0 +1,26 @@ +package de.dhbwloe.campusapp; + +import android.os.Bundle; +import android.support.v4.app.Fragment; +import android.view.View; + +import de.dhbwloe.campusapp.search.SearchIndices; + +/** + * Created by pk910 on 19.01.2016. + */ +public abstract class CampusAppFragment extends Fragment { + protected CampusAppContext AppContext; + protected View oFragmentView; + + @Override + public void onCreate(Bundle savedInstanceState) { + AppContext = CampusAppContext.getInstance(); + super.onCreate(savedInstanceState); + } + + public static SearchIndices[] GetSearchIndices() { + return new SearchIndices[0]; + } + +} diff --git a/app/src/main/java/de/dhbwloe/campusapp/NavigationManager.java b/app/src/main/java/de/dhbwloe/campusapp/NavigationManager.java new file mode 100644 index 0000000..33d2f1c --- /dev/null +++ b/app/src/main/java/de/dhbwloe/campusapp/NavigationManager.java @@ -0,0 +1,183 @@ +package de.dhbwloe.campusapp; + +import android.os.Bundle; +import android.support.v4.app.Fragment; +import android.support.v4.app.FragmentActivity; +import android.support.v4.app.FragmentTransaction; +import android.view.Gravity; +import android.view.View; +import android.widget.LinearLayout; +import android.widget.PopupWindow; + +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +import java.util.ArrayList; + +import de.dhbwloe.campusapp.fragments.PopupFragment; + +/** + * Created by pk910 on 19.01.2016. + */ +public class NavigationManager { + private class NavPage { + String name; + Class fragmentClass; + int fragmentType; // 0 = normal; 1 = fullscreen; 2 = popup + }; + + private CampusAppContext AppContext; + private NavPage oCurrentPage; + private NavPage oParentPage; + private Fragment oCurrentFragment; + private Fragment oParentFragment; + private int iFragmentContainerId; + private ArrayList lNavigationPages = new ArrayList(); + + public NavigationManager(CampusAppContext context, int fragmentContainer) { + AppContext = context; + iFragmentContainerId = fragmentContainer; + oCurrentPage = null; + } + + public void registerPage(String name, Class fragment, int fragmentType) { + NavPage page = new NavPage(); + page.name = name; + page.fragmentClass = (Class) fragment; + page.fragmentType = fragmentType; + lNavigationPages.add(page); + } + + public String getCurrentPageName() { + if(oCurrentPage == null) + return null; + return oCurrentPage.name; + } + + public CampusAppFragment getCurrentFragment() { + if(oCurrentPage == null) + return null; + if(oCurrentPage.fragmentType != 3) + return (CampusAppFragment)oCurrentFragment; + return null; + } + + public void navigatePage(String name) { + navigatePage(name, null, true); + } + + public void navigatePage(String name, Bundle args) { + navigatePage(name, args, true); + } + + public void navigatePage(String name, Bundle args, boolean history) { + + NavPage page = getPageByName(name); + if(page == null) + return; + Fragment fragment; + + if(page.fragmentType == 3) { + PopupFragment popupFragment = new PopupFragment(); + if(args == null) + args = new Bundle(); + args.putString("target", "#"+page.name); + fragment = popupFragment; + } else { + fragment = getFragmentOfPage(page); + } + + fragment.setArguments(args); + + FragmentActivity fragmentActivity = (FragmentActivity) AppContext.getMainActivity(); + FragmentTransaction transaction = fragmentActivity.getSupportFragmentManager().beginTransaction(); + + if (oCurrentPage != null && oCurrentPage.fragmentType == 3) { + transaction.remove(oCurrentFragment); + oCurrentPage = oParentPage; + oCurrentFragment = oParentFragment; + } + if (page.fragmentType == 3) { + transaction.add(fragment, "popup"); + oParentPage = oCurrentPage; + oParentFragment = oCurrentFragment; + history = false; + } else if (oCurrentPage != null) { + transaction.replace(iFragmentContainerId, fragment); + } else { + transaction.add(iFragmentContainerId, fragment); + } + if (history) + transaction.addToBackStack(null); + + oCurrentPage = page; + oCurrentFragment = fragment; + + transaction.commit(); + } + + private NavPage getPageByName(String name) { + NavPage page = null; + for(int i = 0; i < lNavigationPages.size(); i++) { + if(lNavigationPages.get(i).name.equalsIgnoreCase(name)) { + page = lNavigationPages.get(i); + break; + } + } + if(page == null) + return null; + + return page; + } + + private CampusAppFragment getFragmentOfPage(NavPage page) { + Class fragmentClass = page.fragmentClass; + Constructor fragmentConstructor; + CampusAppFragment fragment; + try { + fragmentConstructor = fragmentClass.asSubclass(fragmentClass).getConstructor(); + fragment = (CampusAppFragment)fragmentConstructor.newInstance(new Object[]{}); + } catch (Exception e) { + return null; + } + return fragment; + } + + public CampusAppFragment getPageFragment(String name) { + NavPage page = getPageByName(name); + if(page == null) + return null; + return getFragmentOfPage(page); + } + + public PopupFragment getDialog() { + if(oCurrentPage != null && oCurrentPage.fragmentType == 3) { + PopupFragment fragment = (PopupFragment) oCurrentFragment; + return fragment; + } + return null; + } + + public boolean closeDialog() { + if(oCurrentPage != null && oCurrentPage.fragmentType == 3) { + PopupFragment fragment = (PopupFragment) oCurrentFragment; + fragment.destroyView(); + + FragmentActivity fragmentActivity = (FragmentActivity) AppContext.getMainActivity(); + FragmentTransaction transaction = fragmentActivity.getSupportFragmentManager().beginTransaction(); + + transaction.remove(oCurrentFragment); + + oCurrentPage = oParentPage; + oCurrentFragment = oParentFragment; + + transaction.commit(); + return true; + } + return false; + } + + public boolean back() { + return closeDialog(); + } + +} diff --git a/app/src/main/java/de/dhbwloe/campusapp/SettingsActivity.java b/app/src/main/java/de/dhbwloe/campusapp/SettingsActivity.java new file mode 100644 index 0000000..86aedcc --- /dev/null +++ b/app/src/main/java/de/dhbwloe/campusapp/SettingsActivity.java @@ -0,0 +1,267 @@ +package de.dhbwloe.campusapp; + + +import android.annotation.TargetApi; +import android.content.Context; +import android.content.Intent; +import android.content.res.Configuration; +import android.media.Ringtone; +import android.media.RingtoneManager; +import android.net.Uri; +import android.os.Build; +import android.os.Bundle; +import android.preference.ListPreference; +import android.preference.Preference; +import android.preference.PreferenceActivity; +import android.support.v7.app.ActionBar; +import android.preference.PreferenceFragment; +import android.preference.PreferenceManager; +import android.preference.RingtonePreference; +import android.text.TextUtils; +import android.view.MenuItem; +import android.support.v4.app.NavUtils; + +import java.util.List; + +/** + * A {@link PreferenceActivity} that presents a set of application settings. On + * handset devices, settings are presented as a single list. On tablets, + * settings are split by category, with category headers shown to the left of + * the list of settings. + *

+ * See + * Android Design: Settings for design guidelines and the Settings + * API Guide for more information on developing a Settings UI. + */ +public class SettingsActivity extends AppCompatPreferenceActivity { + /** + * A preference value change listener that updates the preference's summary + * to reflect its new value. + */ + private static Preference.OnPreferenceChangeListener sBindPreferenceSummaryToValueListener = new Preference.OnPreferenceChangeListener() { + @Override + public boolean onPreferenceChange(Preference preference, Object value) { + String stringValue = value.toString(); + + if (preference instanceof ListPreference) { + // For list preferences, look up the correct display value in + // the preference's 'entries' list. + ListPreference listPreference = (ListPreference) preference; + int index = listPreference.findIndexOfValue(stringValue); + + // Set the summary to reflect the new value. + preference.setSummary( + index >= 0 + ? listPreference.getEntries()[index] + : null); + + } else if (preference instanceof RingtonePreference) { + // For ringtone preferences, look up the correct display value + // using RingtoneManager. + if (TextUtils.isEmpty(stringValue)) { + // Empty values correspond to 'silent' (no ringtone). + preference.setSummary(R.string.pref_ringtone_silent); + + } else { + Ringtone ringtone = RingtoneManager.getRingtone( + preference.getContext(), Uri.parse(stringValue)); + + if (ringtone == null) { + // Clear the summary if there was a lookup error. + preference.setSummary(null); + } else { + // Set the summary to reflect the new ringtone display + // name. + String name = ringtone.getTitle(preference.getContext()); + preference.setSummary(name); + } + } + + } else { + // For all other preferences, set the summary to the value's + // simple string representation. + preference.setSummary(stringValue); + } + return true; + } + }; + + /** + * Helper method to determine if the device has an extra-large screen. For + * example, 10" tablets are extra-large. + */ + private static boolean isXLargeTablet(Context context) { + return (context.getResources().getConfiguration().screenLayout + & Configuration.SCREENLAYOUT_SIZE_MASK) >= Configuration.SCREENLAYOUT_SIZE_XLARGE; + } + + /** + * Binds a preference's summary to its value. More specifically, when the + * preference's value is changed, its summary (line of text below the + * preference title) is updated to reflect the value. The summary is also + * immediately updated upon calling this method. The exact display format is + * dependent on the type of preference. + * + * @see #sBindPreferenceSummaryToValueListener + */ + private static void bindPreferenceSummaryToValue(Preference preference) { + // Set the listener to watch for value changes. + preference.setOnPreferenceChangeListener(sBindPreferenceSummaryToValueListener); + + // Trigger the listener immediately with the preference's + // current value. + sBindPreferenceSummaryToValueListener.onPreferenceChange(preference, + PreferenceManager + .getDefaultSharedPreferences(preference.getContext()) + .getString(preference.getKey(), "")); + } + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setupActionBar(); + } + + /** + * Set up the {@link android.app.ActionBar}, if the API is available. + */ + private void setupActionBar() { + ActionBar actionBar = getSupportActionBar(); + if (actionBar != null) { + // Show the Up button in the action bar. + actionBar.setDisplayHomeAsUpEnabled(true); + } + } + + @Override + public boolean onMenuItemSelected(int featureId, MenuItem item) { + int id = item.getItemId(); + if (id == android.R.id.home) { + if (!super.onMenuItemSelected(featureId, item)) { + NavUtils.navigateUpFromSameTask(this); + } + return true; + } + return super.onMenuItemSelected(featureId, item); + } + + /** + * {@inheritDoc} + */ + @Override + public boolean onIsMultiPane() { + return isXLargeTablet(this); + } + + /** + * {@inheritDoc} + */ + @Override + @TargetApi(Build.VERSION_CODES.HONEYCOMB) + public void onBuildHeaders(List

target) { + loadHeadersFromResource(R.xml.pref_headers, target); + } + + /** + * This method stops fragment injection in malicious applications. + * Make sure to deny any unknown fragments here. + */ + protected boolean isValidFragment(String fragmentName) { + return PreferenceFragment.class.getName().equals(fragmentName) + || GeneralPreferenceFragment.class.getName().equals(fragmentName) + || DataSyncPreferenceFragment.class.getName().equals(fragmentName) + || NotificationPreferenceFragment.class.getName().equals(fragmentName); + } + + /** + * This fragment shows general preferences only. It is used when the + * activity is showing a two-pane settings UI. + */ + @TargetApi(Build.VERSION_CODES.HONEYCOMB) + public static class GeneralPreferenceFragment extends PreferenceFragment { + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + addPreferencesFromResource(R.xml.pref_general); + setHasOptionsMenu(true); + + // Bind the summaries of EditText/List/Dialog/Ringtone preferences + // to their values. When their values change, their summaries are + // updated to reflect the new value, per the Android Design + // guidelines. + bindPreferenceSummaryToValue(findPreference("example_text")); + bindPreferenceSummaryToValue(findPreference("example_list")); + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + int id = item.getItemId(); + if (id == android.R.id.home) { + startActivity(new Intent(getActivity(), SettingsActivity.class)); + return true; + } + return super.onOptionsItemSelected(item); + } + } + + /** + * This fragment shows notification preferences only. It is used when the + * activity is showing a two-pane settings UI. + */ + @TargetApi(Build.VERSION_CODES.HONEYCOMB) + public static class NotificationPreferenceFragment extends PreferenceFragment { + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + addPreferencesFromResource(R.xml.pref_notification); + setHasOptionsMenu(true); + + // Bind the summaries of EditText/List/Dialog/Ringtone preferences + // to their values. When their values change, their summaries are + // updated to reflect the new value, per the Android Design + // guidelines. + bindPreferenceSummaryToValue(findPreference("notifications_new_message_ringtone")); + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + int id = item.getItemId(); + if (id == android.R.id.home) { + startActivity(new Intent(getActivity(), SettingsActivity.class)); + return true; + } + return super.onOptionsItemSelected(item); + } + } + + /** + * This fragment shows data and sync preferences only. It is used when the + * activity is showing a two-pane settings UI. + */ + @TargetApi(Build.VERSION_CODES.HONEYCOMB) + public static class DataSyncPreferenceFragment extends PreferenceFragment { + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + addPreferencesFromResource(R.xml.pref_data_sync); + setHasOptionsMenu(true); + + // Bind the summaries of EditText/List/Dialog/Ringtone preferences + // to their values. When their values change, their summaries are + // updated to reflect the new value, per the Android Design + // guidelines. + bindPreferenceSummaryToValue(findPreference("sync_frequency")); + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + int id = item.getItemId(); + if (id == android.R.id.home) { + startActivity(new Intent(getActivity(), SettingsActivity.class)); + return true; + } + return super.onOptionsItemSelected(item); + } + } +} diff --git a/app/src/main/java/de/dhbwloe/campusapp/Tools.java b/app/src/main/java/de/dhbwloe/campusapp/Tools.java new file mode 100644 index 0000000..02c8d7a --- /dev/null +++ b/app/src/main/java/de/dhbwloe/campusapp/Tools.java @@ -0,0 +1,54 @@ +package de.dhbwloe.campusapp; + +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; + +import javax.crypto.Cipher; +import javax.crypto.spec.SecretKeySpec; + +/** + * Created by pk910 on 25.01.2016. + */ +public class Tools { + + public static final String md5(final String s) { + final String MD5 = "MD5"; + try { + // Create MD5 Hash + MessageDigest digest = java.security.MessageDigest.getInstance(MD5); + digest.update(s.getBytes()); + byte messageDigest[] = digest.digest(); + + // Create Hex String + StringBuilder hexString = new StringBuilder(); + for (byte aMessageDigest : messageDigest) { + String h = Integer.toHexString(0xFF & aMessageDigest); + while (h.length() < 2) + h = "0" + h; + hexString.append(h); + } + return hexString.toString(); + + } catch (NoSuchAlgorithmException e) { + e.printStackTrace(); + } + return ""; + } + + public static byte[] encrypt(byte[] raw, byte[] clear) throws Exception { + SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES"); + Cipher cipher = Cipher.getInstance("AES"); + cipher.init(Cipher.ENCRYPT_MODE, skeySpec); + byte[] encrypted = cipher.doFinal(clear); + return encrypted; + } + + public static byte[] decrypt(byte[] raw, byte[] encrypted) throws Exception { + SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES"); + Cipher cipher = Cipher.getInstance("AES"); + cipher.init(Cipher.DECRYPT_MODE, skeySpec); + byte[] decrypted = cipher.doFinal(encrypted); + return decrypted; + } + +} diff --git a/app/src/main/java/de/dhbwloe/campusapp/database/DatabaseManager.java b/app/src/main/java/de/dhbwloe/campusapp/database/DatabaseManager.java new file mode 100644 index 0000000..43a5fd5 --- /dev/null +++ b/app/src/main/java/de/dhbwloe/campusapp/database/DatabaseManager.java @@ -0,0 +1,402 @@ +package de.dhbwloe.campusapp.database; + +import android.app.Activity; +import android.content.ContentValues; +import android.database.Cursor; +import android.database.sqlite.SQLiteDatabase; +import android.util.Log; + +import net.fortuna.ical4j.model.DateList; +import net.fortuna.ical4j.model.DateTime; +import net.fortuna.ical4j.model.Period; +import net.fortuna.ical4j.model.Recur; +import net.fortuna.ical4j.model.parameter.Value; +import net.fortuna.ical4j.model.property.RRule; + +import java.lang.reflect.Array; +import java.text.ParseException; +import java.util.ArrayList; +import java.util.Date; +import java.util.ListIterator; + +import de.dhbwloe.campusapp.CampusAppContext; +import de.dhbwloe.campusapp.mensaplan.MensaTagesplan; +import de.dhbwloe.campusapp.news.NewsItem; +import de.dhbwloe.campusapp.search.SearchIndices; +import de.dhbwloe.campusapp.vorlesungen.CourseEvent; +import de.dhbwloe.campusapp.vorlesungen.CourseGroup; + +/** + * Created by pk910 on 19.01.2016. + */ +public class DatabaseManager { + private static final String DATABASE_NAME = "DHBWLoe.CampusApp.DEV"; + private static final int DATABASE_VERSION = 1; + private CampusAppContext AppContext; + private SQLiteDatabase database; + private NewsDatabaseHelper newsDBHelper; + private MensaplanDatabaseHelper mensaplanDBHelper; + private VorlesungsplanDatabaseHelper vorlesungsplanDBHelper; + + + public DatabaseManager(CampusAppContext context) { + AppContext = context; + } + + public void initializeDatabase() { + database = AppContext.getMainActivity().openOrCreateDatabase(DATABASE_NAME, Activity.MODE_PRIVATE, null); + database.execSQL("CREATE TABLE IF NOT EXISTS Version(Version INT);"); + + Cursor resultSet = database.rawQuery("Select * from Version", null); + int version; + if(resultSet.moveToFirst()) { + version = resultSet.getInt(0); + } else { + version = 0; + database.execSQL("INSERT INTO Version (Version) VALUES (0);"); + } + + resultSet.close(); + if(version < DATABASE_VERSION) + upgradeTables(version, DATABASE_VERSION); + + + } + + private void upgradeTables(int oldVersion, int newVersion) { + if(oldVersion == 0 && newVersion > 0) { + database.execSQL("CREATE TABLE IF NOT EXISTS RuntimeCache " + + "(" + + "Reference TEXT, " + + "Value TEXT, " + + "LastUpdate INT, " + + "PRIMARY KEY (Reference)" + + ");"); + database.execSQL("CREATE TABLE IF NOT EXISTS SearchIndex " + + "(" + + "KeyName TEXT, " + + "SearchText TEXT, " + + "SearchTitle TEXT," + + "Description TEXT, " + + "StaticEntry INT, " + + "UpdateTime INT, " + + "TargetPage TEXT);"); + database.execSQL("CREATE TABLE IF NOT EXISTS CourseCalendar " + + "(" + + "Id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, " + + "CourseName TEXT, " + + "UniqueId TEXT, " + + "SequenceId INT, " + + "EventFrom INT, " + + "EventTo INT, " + + "EventTitle TEXT, " + + "EventLocation TEXT, " + + "EventStatus TEXT," + + "RecurRule TEXT," + + "ExcludeDates TEXT," + + "CourseGroupId INT," + + "UNIQUE (CourseName, UniqueId)" + + ");"); + database.execSQL("CREATE TABLE IF NOT EXISTS CourseCalendarEvent " + + "(" + + "EventId INT, " + + "EventFrom INT, " + + "EventTo INT, " + + "PRIMARY KEY (EventId, EventFrom, EventTo)" + + ");"); + database.execSQL("CREATE INDEX CourseCalendarEventIdx ON CourseCalendarEvent (EventFrom, EventTo);"); + database.execSQL("CREATE TABLE IF NOT EXISTS NfcCardStore " + + "(" + + "CardId INT, " + + "UpdateTime INT," + + "CardData TEXT, " + + "CardBalance INT, " + + "CardLastTransaction INT, " + + "PRIMARY KEY (CardId, UpdateTime)" + + ");"); + database.execSQL("CREATE TABLE IF NOT EXISTS CourseCalendarGroup " + + "(" + + "GroupId INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, " + + "CourseName INT," + + "GroupName TEXT, " + + "LastUpdate INT, " + + "UNIQUE (GroupName)" + + ");"); + database.execSQL("CREATE INDEX CourseCalendarGroupIdx ON CourseCalendarGroup (CourseName, GroupName);"); + database.execSQL("CREATE TABLE IF NOT EXISTS MensaPlan " + + "(" + + "PlanDate INT, " + + "MenuName TEXT, " + + "ChkSum INT, " + + "Name TEXT, " + + "NameHtml TEXT, " + + "Additional TEXT, " + + "Notes TEXT, " + + "PriceStudents INT, " + + "PriceEmployees INT, " + + "PriceGuests INT, " + + "PriceSchool INT, " + + "PRIMARY KEY (PlanDate, MenuName)" + + ");"); + database.execSQL("CREATE TABLE IF NOT EXISTS News " + + "(" + + "Id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, " + + "Source TEXT, " + + "Time INT, " + + "UniqueId TEXT, " + + "ChkSum INT, " + + "Title TEXT, " + + "Summary TEXT, " + + "Content TEXT, " + + "Link TEXT, " + + "Categories TEXT, " + + "UNIQUE(Source, UniqueId) " + + ");"); + database.execSQL("CREATE INDEX NewsIdx ON News (Source, Time);"); + } + if(oldVersion < 2 && newVersion >= 2) { + // Version 2 + + } + + database.execSQL("UPDATE Version SET Version = "+Integer.toString(newVersion)); + } + + public void addSearchIndices(SearchIndices[] indices) { + for(int i = 0; i < indices.length; i++) { + String[] whereArgs = new String[] { + indices[i].getKeyName() + }; + Cursor resultSet = database.rawQuery("SELECT UpdateTime FROM SearchIndex WHERE KeyName = ?", whereArgs); + if(resultSet.moveToFirst()) { + long updateTime = resultSet.getLong(0); + if (updateTime < indices[i].getUpdateTime()) { + // remove + database.rawQuery("DELETE FROM SearchIndex WHERE KeyName = ?", whereArgs); + } else + continue; + } + resultSet.close(); + // add new + try { + ContentValues indexValues = new ContentValues(); + indexValues.put("KeyName", indices[i].getKeyName()); + indexValues.put("SearchText", indices[i].getKeyWords()); + indexValues.put("SearchTitle", indices[i].getTitle()); + indexValues.put("Description", indices[i].getDescription()); + indexValues.put("StaticEntry", indices[i].getIsStatic() ? 1 : 0); + indexValues.put("UpdateTime", indices[i].getUpdateTime()); + indexValues.put("TargetPage", indices[i].getTarget()); + + database.insertOrThrow("SearchIndex", null, indexValues); + } catch(Exception e) { + e.printStackTrace(); + } + } + } + + public SearchIndices[] performSearchRequest(String query, int maxResults) { + String[] whereArgs = new String[] { + "%" + query + "%" + }; + Cursor resultSet = database.rawQuery("SELECT KeyName, SearchTitle, Description, StaticEntry, UpdateTime, TargetPage, SearchText FROM SearchIndex WHERE SearchText LIKE ? ORDER BY StaticEntry DESC, UpdateTime DESC", whereArgs); + ArrayList indices = new ArrayList(); + if(resultSet.moveToFirst()) { + int[] columnIndexes = { + resultSet.getColumnIndex("KeyName"), + resultSet.getColumnIndex("SearchTitle"), + resultSet.getColumnIndex("Description"), + resultSet.getColumnIndex("StaticEntry"), + resultSet.getColumnIndex("UpdateTime"), + resultSet.getColumnIndex("TargetPage"), + resultSet.getColumnIndex("SearchText") + }; + do { + SearchIndices cIndices = new SearchIndices(resultSet.getString(columnIndexes[0]), (resultSet.getInt(columnIndexes[3]) == 1)); + cIndices.setUpdateTime(resultSet.getLong(columnIndexes[4])); + cIndices.setTarget(resultSet.getString(columnIndexes[5])); + cIndices.addKeyWord(resultSet.getString(columnIndexes[6])); + cIndices.setTitle(resultSet.getString(columnIndexes[1])); + cIndices.setDescription(resultSet.getString(columnIndexes[2])); + indices.add(cIndices); + } while (resultSet.moveToNext() && indices.size() < maxResults); + } + resultSet.close(); + + SearchIndices[] indicesArr = new SearchIndices[indices.size()]; + indicesArr = indices.toArray(indicesArr); + return indicesArr; + } + + public void setRuntimeCache(String name, String value) { + long now = (new Date()).getTime() / 1000; + String[] whereArgs = new String[] { + name + }; + Cursor resultSet = database.rawQuery("SELECT Value FROM RuntimeCache WHERE Reference = ?", whereArgs); + if(resultSet.moveToFirst()) { + if(resultSet.getString(0).equalsIgnoreCase(value)) + return; + try { + ContentValues updateValues = new ContentValues(); + updateValues.put("Value", value); + updateValues.put("LastUpdate", now); + + database.update("RuntimeCache", updateValues, "Reference = ?", whereArgs); + } catch(Exception e) { + e.printStackTrace(); + } + } else { + try { + ContentValues indexValues = new ContentValues(); + indexValues.put("Reference", name); + indexValues.put("Value", value); + indexValues.put("LastUpdate", now); + + database.insertOrThrow("RuntimeCache", null, indexValues); + } catch(Exception e) { + e.printStackTrace(); + } + } + resultSet.close(); + } + + public String getRuntimeCache(String name) { + String value = null; + String[] whereArgs = new String[] { + name + }; + Cursor resultSet = database.rawQuery("SELECT Value FROM RuntimeCache WHERE Reference = ?", whereArgs); + if(resultSet.moveToFirst()) { + value = resultSet.getString(0); + } + resultSet.close(); + return value; + } + + public void addNfcCardData(NfcCardData nfcCardData) { + String[] whereArgs = new String[] { + Integer.toString(nfcCardData.getUniqueId()), + Long.toString(nfcCardData.getLastUpdate()) + }; + Cursor resultSet = database.rawQuery("SELECT CardData FROM NfcCardStore WHERE CardId = ? AND UpdateTime = ?", whereArgs); + if(resultSet.moveToFirst()) { + if(resultSet.getString(0).equalsIgnoreCase(nfcCardData.getCardData())) + return; + try { + ContentValues updateValues = new ContentValues(); + updateValues.put("CardData", nfcCardData.getCardData()); + updateValues.put("CardBalance", nfcCardData.getBalance()); + updateValues.put("CardLastTransaction", nfcCardData.getLastTransaction()); + + database.update("NfcCardStore", updateValues, "CardId = ? AND UpdateTime = ?", whereArgs); + } catch(Exception e) { + e.printStackTrace(); + } + } else { + try { + ContentValues indexValues = new ContentValues(); + indexValues.put("CardId", nfcCardData.getUniqueId()); + indexValues.put("UpdateTime", nfcCardData.getLastUpdate()); + indexValues.put("CardData", nfcCardData.getCardData()); + indexValues.put("CardBalance", nfcCardData.getBalance()); + indexValues.put("CardLastTransaction", nfcCardData.getLastTransaction()); + + database.insertOrThrow("NfcCardStore", null, indexValues); + } catch(Exception e) { + e.printStackTrace(); + } + } + resultSet.close(); + } + + public NfcCardData[] getNfcCardData(int lastLimit) { + String value = null; + Cursor resultSet; + if(lastLimit > 0) { + String[] whereArgs = { + Integer.toString(lastLimit) + }; + resultSet = database.rawQuery("SELECT CardId,UpdateTime,CardData,CardBalance,CardLastTransaction FROM NfcCardStore ORDER BY UpdateTime DESC LIMIT ?", whereArgs); + } else + resultSet = database.rawQuery("SELECT CardId,UpdateTime,CardData,CardBalance,CardLastTransaction FROM NfcCardStore ORDER BY UpdateTime DESC", null); + ArrayList nfcCardDatas = new ArrayList(); + if(resultSet.moveToFirst()) { + do { + NfcCardData nfcCardData = new NfcCardData(resultSet.getInt(0), resultSet.getLong(1), resultSet.getString(2), resultSet.getInt(3), resultSet.getInt(4)); + nfcCardDatas.add(nfcCardData); + } while (resultSet.moveToNext()); + } + resultSet.close(); + NfcCardData[] resultsArr = new NfcCardData[nfcCardDatas.size()]; + resultsArr = nfcCardDatas.toArray(resultsArr); + return resultsArr; + } + + public void updateCourseCalendar(CourseEvent event) { + if(vorlesungsplanDBHelper == null) + vorlesungsplanDBHelper = new VorlesungsplanDatabaseHelper(AppContext, database); + vorlesungsplanDBHelper.updateCourseCalendar(event); + } + + public CourseEvent[] getCourseCalendarEvents(String coursename, long timeFrom, long timeTo) { + if(vorlesungsplanDBHelper == null) + vorlesungsplanDBHelper = new VorlesungsplanDatabaseHelper(AppContext, database); + return vorlesungsplanDBHelper.getCourseCalendarEvents(coursename, timeFrom, timeTo); + } + + public CourseGroup getCourseGroup(int courseGroupId) { + if(vorlesungsplanDBHelper == null) + vorlesungsplanDBHelper = new VorlesungsplanDatabaseHelper(AppContext, database); + return vorlesungsplanDBHelper.getCourseGroup(courseGroupId); + } + + public CourseGroup getCourseGroup(String coursename, String groupname) { + if(vorlesungsplanDBHelper == null) + vorlesungsplanDBHelper = new VorlesungsplanDatabaseHelper(AppContext, database); + return vorlesungsplanDBHelper.getCourseGroup(coursename, groupname); + } + + public CourseGroup addCourseGroup(String coursename, String groupname) { + if(vorlesungsplanDBHelper == null) + vorlesungsplanDBHelper = new VorlesungsplanDatabaseHelper(AppContext, database); + return vorlesungsplanDBHelper.addCourseGroup(coursename, groupname); + } + + public void updateMensaTagesplan(MensaTagesplan plan) { + if(mensaplanDBHelper == null) + mensaplanDBHelper = new MensaplanDatabaseHelper(AppContext, database); + mensaplanDBHelper.updateMensaTagesplan(plan); + } + + public MensaTagesplan[] getMensaTagesplan(long timeFrom, long timeTo) { + if(mensaplanDBHelper == null) + mensaplanDBHelper = new MensaplanDatabaseHelper(AppContext, database); + return mensaplanDBHelper.getMensaTagesplan(timeFrom, timeTo); + } + + public long[] getDaysWithPlanData(long timeFrom, long timeTo) { + if(mensaplanDBHelper == null) + mensaplanDBHelper = new MensaplanDatabaseHelper(AppContext, database); + return mensaplanDBHelper.getDaysWithPlanData(timeFrom, timeTo); + } + + public long[] getWeeksWithPlanData(long timeFrom, long timeTo) { + if(mensaplanDBHelper == null) + mensaplanDBHelper = new MensaplanDatabaseHelper(AppContext, database); + return mensaplanDBHelper.getWeeksWithPlanData(timeFrom, timeTo); + } + + public void updateNewsItem(NewsItem news) { + if(newsDBHelper == null) + newsDBHelper = new NewsDatabaseHelper(AppContext, database); + newsDBHelper.updateNewsItem(news); + } + + public NewsItem[] getNewsItems(String source, long timeFrom, long timeTo) { + if(newsDBHelper == null) + newsDBHelper = new NewsDatabaseHelper(AppContext, database); + return newsDBHelper.getNewsItems(source, timeFrom, timeTo); + } + +} diff --git a/app/src/main/java/de/dhbwloe/campusapp/database/MensaplanDatabaseHelper.java b/app/src/main/java/de/dhbwloe/campusapp/database/MensaplanDatabaseHelper.java new file mode 100644 index 0000000..a8a6fd4 --- /dev/null +++ b/app/src/main/java/de/dhbwloe/campusapp/database/MensaplanDatabaseHelper.java @@ -0,0 +1,170 @@ +package de.dhbwloe.campusapp.database; + +import android.content.ContentValues; +import android.database.Cursor; +import android.database.sqlite.SQLiteDatabase; + +import java.util.ArrayList; + +import de.dhbwloe.campusapp.CampusAppContext; +import de.dhbwloe.campusapp.mensaplan.MensaTagesplan; + +/** + * Created by pk910 on 24.01.2016. + */ +public class MensaplanDatabaseHelper { + private CampusAppContext AppContext; + private SQLiteDatabase database; + + public MensaplanDatabaseHelper(CampusAppContext context, SQLiteDatabase database) { + this.database = database; + AppContext = context; + } + + public void updateMensaTagesplan(MensaTagesplan plan) { + boolean isExisting = false; + String[] whereArgs = new String[] { + Long.toString(plan.getPlanDate()), + plan.getMenuName() + }; + Cursor resultSet = database.rawQuery("SELECT ChkSum FROM MensaPlan WHERE PlanDate = ? AND MenuName = ?", whereArgs); + if(resultSet.moveToFirst()) { + long chksum = resultSet.getLong(0); + if(plan.getChkSum() == chksum) + return; // nothing to update + isExisting = true; + } + resultSet.close(); + + if(isExisting) { + try { + ContentValues updateValues = new ContentValues(); + updateValues.put("ChkSum", plan.getChkSum()); + updateValues.put("Name", plan.getName()); + updateValues.put("NameHtml", plan.getNameHtml()); + updateValues.put("Additional", plan.getAdditional()); + updateValues.put("Notes", plan.getNotes()); + int plainPrice[] = plan.getPlainPrice(); + updateValues.put("PriceStudents", plainPrice[0]); + updateValues.put("PriceEmployees", plainPrice[1]); + updateValues.put("PriceGuests", plainPrice[2]); + updateValues.put("PriceSchool", plainPrice[3]); + + database.update("MensaPlan", updateValues, "PlanDate = ? AND MenuName = ?", whereArgs); + } catch(Exception e) { + e.printStackTrace(); + } + } else { + try { + ContentValues indexValues = new ContentValues(); + indexValues.put("PlanDate", plan.getPlanDate()); + indexValues.put("MenuName", plan.getMenuName()); + + indexValues.put("ChkSum", plan.getChkSum()); + indexValues.put("Name", plan.getName()); + indexValues.put("NameHtml", plan.getNameHtml()); + indexValues.put("Additional", plan.getAdditional()); + indexValues.put("Notes", plan.getNotes()); + int plainPrice[] = plan.getPlainPrice(); + indexValues.put("PriceStudents", plainPrice[0]); + indexValues.put("PriceEmployees", plainPrice[1]); + indexValues.put("PriceGuests", plainPrice[2]); + indexValues.put("PriceSchool", plainPrice[3]); + + database.insertOrThrow("MensaPlan", null, indexValues); + plan.setIsNew(); + } catch(Exception e) { + e.printStackTrace(); + } + } + + } + + public MensaTagesplan[] getMensaTagesplan(long timeFrom, long timeTo) { + String[] whereArgs = new String[] { + Long.toString(timeFrom), + Long.toString(timeTo) + }; + Cursor resultSet = database.rawQuery("SELECT PlanDate,MenuName,ChkSum,Name,NameHtml,Additional,Notes,PriceStudents,PriceEmployees,PriceGuests,PriceSchool FROM MensaPlan WHERE PlanDate >= ? AND PlanDate <= ?", whereArgs); + ArrayList results = new ArrayList(); + if(resultSet.moveToFirst()) { + int[] columnIndexes = { + resultSet.getColumnIndex("PlanDate"), + resultSet.getColumnIndex("MenuName"), + resultSet.getColumnIndex("ChkSum"), + resultSet.getColumnIndex("Name"), + resultSet.getColumnIndex("NameHtml"), + resultSet.getColumnIndex("Additional"), + resultSet.getColumnIndex("Notes"), + resultSet.getColumnIndex("PriceStudents"), + resultSet.getColumnIndex("PriceEmployees"), + resultSet.getColumnIndex("PriceGuests"), + resultSet.getColumnIndex("PriceSchool") + }; + do { + MensaTagesplan plan = new MensaTagesplan( + resultSet.getLong(columnIndexes[0]), resultSet.getString(columnIndexes[1]), + resultSet.getLong(columnIndexes[2]), resultSet.getString(columnIndexes[3]), + resultSet.getString(columnIndexes[4]), resultSet.getString(columnIndexes[5]), + resultSet.getString(columnIndexes[6]), + resultSet.getInt(columnIndexes[7]), resultSet.getInt(columnIndexes[8]), + resultSet.getInt(columnIndexes[9]), resultSet.getInt(columnIndexes[10]) + ); + + results.add(plan); + } while (resultSet.moveToNext()); + } + resultSet.close(); + + MensaTagesplan[] resultsArr = new MensaTagesplan[results.size()]; + resultsArr = results.toArray(resultsArr); + return resultsArr; + } + + public long[] getDaysWithPlanData(long timeFrom, long timeTo) { + String[] whereArgs = new String[] { + Long.toString(timeFrom), + Long.toString(timeTo) + }; + Cursor resultSet = database.rawQuery("SELECT PlanDate FROM MensaPlan WHERE PlanDate >= ? AND PlanDate <= ? GROUP BY PlanDate", whereArgs); + ArrayList results = new ArrayList(); + if(resultSet.moveToFirst()) { + do { + long date = resultSet.getLong(0); + + results.add(date); + } while (resultSet.moveToNext()); + } + resultSet.close(); + + long[] resultsArr = new long[results.size()]; + for(int i = 0; i < resultsArr.length; i++) { + resultsArr[i] = results.get(i); + } + return resultsArr; + } + + public long[] getWeeksWithPlanData(long timeFrom, long timeTo) { + String[] whereArgs = new String[] { + Long.toString(timeFrom), + Long.toString(timeTo) + }; + Cursor resultSet = database.rawQuery("SELECT MIN(PlanDate) AS PlanDate, strftime(\"%W\", PlanDate, \"unixepoch\") AS PlanWeek FROM MensaPlan WHERE PlanDate >= ? AND PlanDate <= ? GROUP BY PlanWeek", whereArgs); + ArrayList results = new ArrayList(); + if(resultSet.moveToFirst()) { + do { + long date = resultSet.getLong(0); + + results.add(date); + } while (resultSet.moveToNext()); + } + resultSet.close(); + + long[] resultsArr = new long[results.size()]; + for(int i = 0; i < resultsArr.length; i++) { + resultsArr[i] = results.get(i); + } + return resultsArr; + } + +} diff --git a/app/src/main/java/de/dhbwloe/campusapp/database/NewsDatabaseHelper.java b/app/src/main/java/de/dhbwloe/campusapp/database/NewsDatabaseHelper.java new file mode 100644 index 0000000..e71066d --- /dev/null +++ b/app/src/main/java/de/dhbwloe/campusapp/database/NewsDatabaseHelper.java @@ -0,0 +1,117 @@ +package de.dhbwloe.campusapp.database; + +import android.content.ContentValues; +import android.database.Cursor; +import android.database.sqlite.SQLiteDatabase; + +import java.util.ArrayList; + +import de.dhbwloe.campusapp.CampusAppContext; +import de.dhbwloe.campusapp.news.NewsItem; + +/** + * Created by pk910 on 24.01.2016. + */ +public class NewsDatabaseHelper { + private CampusAppContext AppContext; + private SQLiteDatabase database; + + public NewsDatabaseHelper(CampusAppContext context, SQLiteDatabase database) { + this.database = database; + AppContext = context; + } + + public void updateNewsItem(NewsItem news) { + boolean isExisting = false; + String[] whereArgs = new String[] { + news.getSource(), + news.getUniqueId() + }; + Cursor resultSet = database.rawQuery("SELECT ChkSum FROM News WHERE Source = ? AND UniqueId = ?", whereArgs); + if(resultSet.moveToFirst()) { + long chksum = resultSet.getLong(0); + if (news.getChkSum() == chksum) + return; // nothing to update + isExisting = true; + } + resultSet.close(); + + if(isExisting) { + try { + ContentValues updateValues = new ContentValues(); + updateValues.put("ChkSum", news.getChkSum()); + updateValues.put("Title", news.getTitle()); + updateValues.put("Time", news.getTime()); + updateValues.put("Summary", news.getSummary()); + updateValues.put("Content", news.getContent()); + updateValues.put("Link", news.getLink()); + updateValues.put("Categories", news.getCategories()); + + database.update("News", updateValues, "Source = ? AND UniqueId = ?", whereArgs); + } catch(Exception e) { + e.printStackTrace(); + } + } else { + try { + ContentValues indexValues = new ContentValues(); + indexValues.put("Source", news.getSource()); + indexValues.put("UniqueId", news.getUniqueId()); + + indexValues.put("ChkSum", news.getChkSum()); + indexValues.put("Title", news.getTitle()); + indexValues.put("Time", news.getTime()); + indexValues.put("Summary", news.getSummary()); + indexValues.put("Content", news.getContent()); + indexValues.put("Link", news.getLink()); + indexValues.put("Categories", news.getCategories()); + + long newId = database.insertOrThrow("News", null, indexValues); + news.setIsNew((int) newId); + } catch(Exception e) { + e.printStackTrace(); + } + } + + } + + public NewsItem[] getNewsItems(String source, long timeFrom, long timeTo) { + String[] whereArgs = new String[] { + source, + Long.toString(timeFrom), + Long.toString(timeTo) + }; + Cursor resultSet = database.rawQuery("SELECT Id, Source, Time, UniqueId, ChkSum, Title, Summary, Content, Link, Categories FROM News WHERE Source = ? AND Time >= ? AND Time <= ?", whereArgs); + ArrayList results = new ArrayList(); + if(resultSet.moveToFirst()) { + int[] columnIndexes = { + resultSet.getColumnIndex("Id"), + resultSet.getColumnIndex("Source"), + resultSet.getColumnIndex("Time"), + resultSet.getColumnIndex("UniqueId"), + resultSet.getColumnIndex("ChkSum"), + resultSet.getColumnIndex("Title"), + resultSet.getColumnIndex("Summary"), + resultSet.getColumnIndex("Content"), + resultSet.getColumnIndex("Link"), + resultSet.getColumnIndex("Categories") + }; + do { + NewsItem news = new NewsItem( + resultSet.getInt(columnIndexes[0]), resultSet.getString(columnIndexes[1]), + resultSet.getLong(columnIndexes[2]), resultSet.getString(columnIndexes[3]), + resultSet.getLong(columnIndexes[4]), resultSet.getString(columnIndexes[5]), + resultSet.getString(columnIndexes[6]), resultSet.getString(columnIndexes[7]), + resultSet.getString(columnIndexes[8]), resultSet.getString(columnIndexes[9]) + ); + + results.add(news); + } while (resultSet.moveToNext()); + } + resultSet.close(); + + NewsItem[] resultsArr = new NewsItem[results.size()]; + resultsArr = results.toArray(resultsArr); + return resultsArr; + } + +} diff --git a/app/src/main/java/de/dhbwloe/campusapp/database/NfcCardData.java b/app/src/main/java/de/dhbwloe/campusapp/database/NfcCardData.java new file mode 100644 index 0000000..08a6e2d --- /dev/null +++ b/app/src/main/java/de/dhbwloe/campusapp/database/NfcCardData.java @@ -0,0 +1,51 @@ +package de.dhbwloe.campusapp.database; + +import java.util.Date; + +/** + * Created by pk910 on 21.01.2016. + */ +public class NfcCardData { + private int iUniqueId; + private long iLastUpdate; + private String sCardData; + private int iBalance; + private int iLastTransaction; + + public NfcCardData(de.dhbwloe.campusapp.nfcreader.cardreader.NfcCardData carddata) { + iUniqueId = carddata.getUniqueid(); + iLastUpdate = (new Date()).getTime()/1000; + sCardData = carddata.getCompactCardDataSummary(); + iBalance = (int)(carddata.getBalanceData() * 100); + iLastTransaction = (int)(carddata.getLastTransaction() * 100); + } + + public NfcCardData(int uid, long lastupdate, String carddata, int balance, int transaction) { + iUniqueId = uid; + iLastUpdate = lastupdate; + sCardData = carddata; + iBalance = balance; + iLastTransaction = transaction; + } + + public int getUniqueId() { + return iUniqueId; + } + + public String getCardData() { + return sCardData; + } + + public long getLastUpdate() { + return iLastUpdate; + } + + public int getBalance() { + return iBalance; + } + + public int getLastTransaction() { + return iLastTransaction; + } + +} diff --git a/app/src/main/java/de/dhbwloe/campusapp/database/VorlesungsplanDatabaseHelper.java b/app/src/main/java/de/dhbwloe/campusapp/database/VorlesungsplanDatabaseHelper.java new file mode 100644 index 0000000..e38b87d --- /dev/null +++ b/app/src/main/java/de/dhbwloe/campusapp/database/VorlesungsplanDatabaseHelper.java @@ -0,0 +1,245 @@ +package de.dhbwloe.campusapp.database; + +import android.content.ContentValues; +import android.database.Cursor; +import android.database.sqlite.SQLiteDatabase; +import android.util.Log; + +import net.fortuna.ical4j.model.DateList; +import net.fortuna.ical4j.model.DateTime; +import net.fortuna.ical4j.model.Period; +import net.fortuna.ical4j.model.Recur; +import net.fortuna.ical4j.model.parameter.Value; +import net.fortuna.ical4j.model.property.RRule; + +import java.text.ParseException; +import java.util.ArrayList; +import java.util.Date; + +import de.dhbwloe.campusapp.CampusAppContext; +import de.dhbwloe.campusapp.vorlesungen.CourseEvent; +import de.dhbwloe.campusapp.vorlesungen.CourseGroup; + +/** + * Created by pk910 on 24.01.2016. + */ +public class VorlesungsplanDatabaseHelper { + private CampusAppContext AppContext; + private SQLiteDatabase database; + + public VorlesungsplanDatabaseHelper(CampusAppContext context, SQLiteDatabase database) { + this.database = database; + AppContext = context; + } + + + public void updateCourseCalendar(CourseEvent event) { + boolean isExisting = false; + String[] whereArgs = new String[] { + event.getCourseName(), + event.getUniqueId() + }; + Cursor resultSet = database.rawQuery("SELECT SequenceId FROM CourseCalendar WHERE CourseName = ? AND UniqueId = ?", whereArgs); + if(resultSet.moveToFirst()) { + int sequence = resultSet.getInt(0); + if(event.getSequenceId() <= sequence) + return; + isExisting = true; + } + resultSet.close(); + + if(isExisting) { + try { + ContentValues updateValues = new ContentValues(); + updateValues.put("SequenceId", event.getSequenceId()); + updateValues.put("EventFrom", event.getEventFrom()); + updateValues.put("EventTo", event.getEventTo()); + updateValues.put("EventTitle", event.getEventTitle()); + updateValues.put("EventLocation", event.getEventLocation()); + updateValues.put("EventStatus", event.getEventStatus()); + updateValues.put("RecurRule", event.getRecurRule()); + updateValues.put("ExcludeDates", event.getExcludeDates()); + if(event.getCourseGroup() != null) + updateValues.put("CourseGroupId", event.getCourseGroup().getGroupId()); + + database.update("CourseCalendar", updateValues, "CourseName = ? AND UniqueId = ?", whereArgs); + updateCourseCalendarEventTable(event, true); + } catch(Exception e) { + e.printStackTrace(); + } + } else { + try { + ContentValues indexValues = new ContentValues(); + indexValues.put("CourseName", event.getCourseName()); + indexValues.put("UniqueId", event.getUniqueId()); + indexValues.put("SequenceId", event.getSequenceId()); + indexValues.put("EventFrom", event.getEventFrom()); + indexValues.put("EventTo", event.getEventTo()); + indexValues.put("EventTitle", event.getEventTitle()); + indexValues.put("EventLocation", event.getEventLocation()); + indexValues.put("EventStatus", event.getEventStatus()); + indexValues.put("RecurRule", event.getRecurRule()); + indexValues.put("ExcludeDates", event.getExcludeDates()); + if(event.getCourseGroup() != null) + indexValues.put("CourseGroupId", event.getCourseGroup().getGroupId()); + + long id = database.insertOrThrow("CourseCalendar", null, indexValues); + event.setEventId((int) id); + updateCourseCalendarEventTable(event, false); + } catch(Exception e) { + e.printStackTrace(); + } + } + + } + + private void updateCourseCalendarEventTable(CourseEvent event, boolean clear) { + if(clear) { + String[] whereArgs = { + Integer.toString(event.getEventId()) + }; + database.rawQuery("DELETE FROM CourseCalendarEvent WHERE EventId = ?", whereArgs); + } + + String rrule = event.getRecurRule(); + String exdates = event.getExcludeDates(); + + if(rrule != null && rrule.length() > 0) { + try { + Log.i("DBM", "RRule: " + rrule); + RRule rule = new RRule(rrule); + Recur recur = rule.getRecur(); + Date startDate = new Date((event.getEventFrom())*1000); + Date endDate = new Date(startDate.getTime() + (86400 * 365)*1000); + + DateTime startDateTime = new DateTime(startDate); + DateList dates = recur.getDates(startDateTime, new Period(startDateTime, new DateTime(endDate)), Value.DATE); + + Log.i("DBM", "Update events table for " + event.getUniqueId()); + Log.i("DBM", "events: " +dates.size()); + + for(int i = 0; i < dates.size(); i++) { + net.fortuna.ical4j.model.Date date = (net.fortuna.ical4j.model.Date) dates.get(i); + long eventStartTime = date.getTime() / 1000; + long eventEndTime = (eventStartTime + (event.getEventTo() - event.getEventFrom())) / 1000; + + try { + ContentValues indexValues = new ContentValues(); + indexValues.put("EventId", event.getEventId()); + indexValues.put("EventFrom", eventStartTime); + indexValues.put("EventTo", eventEndTime); + database.insertOrThrow("CourseCalendarEvent", null, indexValues); + } catch(Exception e) { + e.printStackTrace(); + } + } + + } catch (ParseException e) { + } + } else { + try { + ContentValues indexValues = new ContentValues(); + indexValues.put("EventId", event.getEventId()); + indexValues.put("EventFrom", event.getEventFrom()); + indexValues.put("EventTo", event.getEventTo()); + database.insertOrThrow("CourseCalendarEvent", null, indexValues); + } catch(Exception e) { + e.printStackTrace(); + } + } + + } + + public CourseEvent[] getCourseCalendarEvents(String coursename, long timeFrom, long timeTo) { + String[] whereArgs = new String[] { + coursename, + Long.toString(timeFrom), + Long.toString(timeTo) + }; + Cursor resultSet = database.rawQuery("SELECT Id, CourseName, UniqueId, SequenceId, EventFrom, EventTo, EventTitle, EventLocation, EventStatus, RecurRule, ExcludeDates, CourseGroupId FROM CourseCalendar WHERE CourseName = ? AND EventTo >= ? AND EventFrom <= ?", whereArgs); + ArrayList events = new ArrayList(); + if(resultSet.moveToFirst()) { + int[] columnIndexes = { + resultSet.getColumnIndex("Id"), + resultSet.getColumnIndex("CourseName"), + resultSet.getColumnIndex("UniqueId"), + resultSet.getColumnIndex("SequenceId"), + resultSet.getColumnIndex("EventFrom"), + resultSet.getColumnIndex("EventTo"), + resultSet.getColumnIndex("EventTitle"), + resultSet.getColumnIndex("EventLocation"), + resultSet.getColumnIndex("EventStatus"), + resultSet.getColumnIndex("RecurRule"), + resultSet.getColumnIndex("ExcludeDates"), + resultSet.getColumnIndex("CourseGroupId") + }; + do { + int groupId = resultSet.getInt(columnIndexes[11]); + CourseGroup group; + if(groupId > 0) + group = CourseGroup.GetCourseGroupById(AppContext.getDatabaseManager(), groupId); + else + group = null; + CourseEvent event = new CourseEvent(resultSet.getInt(columnIndexes[0]), resultSet.getString(columnIndexes[1]), resultSet.getString(columnIndexes[2]), resultSet.getInt(columnIndexes[3]), + resultSet.getLong(columnIndexes[4]), resultSet.getLong(columnIndexes[5]), resultSet.getString(columnIndexes[6]), resultSet.getString(columnIndexes[7]), + resultSet.getString(columnIndexes[8]), resultSet.getString(columnIndexes[9]), resultSet.getString(columnIndexes[10]), group); + + events.add(event); + } while (resultSet.moveToNext()); + } + resultSet.close(); + + CourseEvent[] eventsArr = new CourseEvent[events.size()]; + eventsArr = events.toArray(eventsArr); + return eventsArr; + } + + public CourseGroup getCourseGroup(int courseGroupId) { + CourseGroup coursegroup = null; + String[] whereArgs = new String[] { + Integer.toString(courseGroupId) + }; + Cursor resultSet = database.rawQuery("SELECT CourseName, GroupName, LastUpdate FROM CourseCalendarGroup WHERE GroupId = ?", whereArgs); + if(resultSet.moveToFirst()) { + coursegroup = new CourseGroup(courseGroupId, resultSet.getString(0), resultSet.getString(1)); + } + resultSet.close(); + return coursegroup; + } + + public CourseGroup getCourseGroup(String coursename, String groupname) { + CourseGroup coursegroup = null; + String[] whereArgs = new String[] { + coursename, + groupname + }; + Cursor resultSet = database.rawQuery("SELECT GroupId, CourseName, GroupName, LastUpdate FROM CourseCalendarGroup WHERE CourseName = ? AND GroupName = ?", whereArgs); + if(resultSet.moveToFirst()) { + coursegroup = new CourseGroup(resultSet.getInt(0), resultSet.getString(1), resultSet.getString(2)); + } + resultSet.close(); + return coursegroup; + } + + public CourseGroup addCourseGroup(String coursename, String groupname) { + long now = (new Date()).getTime() / 1000; + int id = 0; + try { + ContentValues indexValues = new ContentValues(); + indexValues.put("CourseName",coursename); + indexValues.put("GroupName", groupname); + indexValues.put("LastUpdate", now); + + id = (int) database.insertOrThrow("CourseCalendarGroup", null, indexValues); + } catch(Exception e) { + e.printStackTrace(); + } + + if(id > 0) { + CourseGroup newGroup = new CourseGroup(id, coursename, groupname); + return newGroup; + } else + return null; + } + +} diff --git a/app/src/main/java/de/dhbwloe/campusapp/fragments/AppSearch.java b/app/src/main/java/de/dhbwloe/campusapp/fragments/AppSearch.java new file mode 100644 index 0000000..3ca9e02 --- /dev/null +++ b/app/src/main/java/de/dhbwloe/campusapp/fragments/AppSearch.java @@ -0,0 +1,156 @@ +package de.dhbwloe.campusapp.fragments; + + +import android.os.Bundle; +import android.support.design.widget.TabLayout; +import android.support.v4.app.Fragment; +import android.support.v4.app.FragmentManager; +import android.support.v4.app.FragmentPagerAdapter; +import android.support.v4.view.ViewPager; +import android.util.Log; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.AdapterView; +import android.widget.ListView; + +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; + +import de.dhbwloe.campusapp.CampusAppContext; +import de.dhbwloe.campusapp.CampusAppFragment; +import de.dhbwloe.campusapp.R; +import de.dhbwloe.campusapp.search.SearchIndices; +import de.dhbwloe.campusapp.search.SearchTarget; + +/** + * A simple {@link Fragment} subclass. + */ +public class AppSearch extends CampusAppFragment { + private ViewPagerAdapter oAppSearchLViewPagerAdapter; + private String sSearchQuery; + + @Override + public void onCreate(Bundle savedInstanceState) { + Log.i("AppSearch", "Event: onCreate"); + super.onCreate(savedInstanceState); + + Bundle bundle; + if((bundle = getArguments()) != null) + sSearchQuery = bundle.getString("query"); + else if(savedInstanceState != null && (bundle = savedInstanceState.getBundle("AppSearch")) != null) + sSearchQuery = bundle.getString("query"); + + } + + @Override + public void onSaveInstanceState(Bundle savedInstanceState) { + Log.i("AppSearch", "Event: onSaveInstanceState"); + Bundle bundle = new Bundle(); + bundle.putString("query", sSearchQuery); + savedInstanceState.putBundle("AppSearch", bundle); + super.onSaveInstanceState(savedInstanceState); + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + Log.i("AppSearch", "Event: onCreateView"); + if(AppContext == null) + AppContext = CampusAppContext.getInstance(); + oFragmentView = inflater.inflate(R.layout.fragment_appsearch, container, false); + + AppContext.setTitle("Search: " + sSearchQuery); + + ViewPager viewPager = (ViewPager) oFragmentView.findViewById(R.id.viewpager); + setupViewPager(viewPager); + + TabLayout tabLayout = (TabLayout) oFragmentView.findViewById(R.id.tabs); + tabLayout.setupWithViewPager(viewPager); + + return oFragmentView; + } + + private void setupViewPager(ViewPager viewPager) { + final ViewPagerAdapter adapter; + if(oAppSearchLViewPagerAdapter != null) + adapter = oAppSearchLViewPagerAdapter; + else { + adapter = new ViewPagerAdapter(getChildFragmentManager()); + oAppSearchLViewPagerAdapter = adapter; + + CampusAppFragment fragment; + Bundle args = new Bundle(); + args.putString("query", sSearchQuery); + Log.i("AppSearch", "New result instances: "+sSearchQuery); + + fragment = new AppSearchInternal(); + fragment.setArguments(args); + adapter.addFragment("Campus App", fragment); + + fragment = new AppSearchDhbw(); + fragment.setArguments(args); + adapter.addFragment("DHBW Suche", fragment); + + fragment = new AppSearchStuv(); + fragment.setArguments(args); + adapter.addFragment("Stuv Suche", fragment); + } + + viewPager.setAdapter(adapter); + int activeItem = viewPager.getCurrentItem(); + AppSearchProvider activeProvider = (AppSearchProvider) adapter.getItem(activeItem); + activeProvider.executeSearch(false); + + viewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() { + @Override + public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { + + } + + @Override + public void onPageSelected(int position) { + AppSearchProvider activeProvider = (AppSearchProvider) adapter.getItem(position); + activeProvider.executeSearch(false); + } + + @Override + public void onPageScrollStateChanged(int state) { + + } + }); + } + + + class ViewPagerAdapter extends FragmentPagerAdapter { + private final List mFragmentDataList = new ArrayList<>(); + private final List mFragmentTitleList = new ArrayList<>(); + + public ViewPagerAdapter(FragmentManager manager) { + super(manager); + } + + @Override + public Fragment getItem(int position) { + return mFragmentDataList.get(position); + } + + @Override + public int getCount() { + return mFragmentDataList.size(); + } + + public void addFragment(String title, Fragment fragment) { + mFragmentDataList.add(fragment); + mFragmentTitleList.add(title); + } + + @Override + public CharSequence getPageTitle(int position) { + return mFragmentTitleList.get(position); + } + } + +} diff --git a/app/src/main/java/de/dhbwloe/campusapp/fragments/AppSearchDhbw.java b/app/src/main/java/de/dhbwloe/campusapp/fragments/AppSearchDhbw.java new file mode 100644 index 0000000..3159aca --- /dev/null +++ b/app/src/main/java/de/dhbwloe/campusapp/fragments/AppSearchDhbw.java @@ -0,0 +1,129 @@ +package de.dhbwloe.campusapp.fragments; + + +import android.os.Bundle; +import android.support.v4.app.Fragment; +import android.util.Log; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.AdapterView; +import android.widget.ListView; + +import java.util.ArrayList; +import java.util.List; + +import de.dhbwloe.campusapp.CampusAppContext; +import de.dhbwloe.campusapp.CampusAppFragment; +import de.dhbwloe.campusapp.R; +import de.dhbwloe.campusapp.search.DhbwSearchHelper; +import de.dhbwloe.campusapp.search.SearchIndices; +import de.dhbwloe.campusapp.search.SearchResultListener; +import de.dhbwloe.campusapp.search.SearchTarget; + +/** + * A simple {@link Fragment} subclass. + */ +public class AppSearchDhbw extends CampusAppFragment implements AppSearchProvider { + private String sSearchQuery; + private boolean bQueryExecuted = false; + private AppSearchListAdapter appSearchAdapter; + private DhbwSearchHelper seachHelper; + private ArrayList searchResultItems = new ArrayList(); + + @Override + public void onCreate(Bundle savedInstanceState) { + Log.i("AppSearchDhbw", "Event: onCreate"); + super.onCreate(savedInstanceState); + } + + @Override + public void onSaveInstanceState(Bundle savedInstanceState) { + Log.i("AppSearchDhbw", "Event: onSaveInstanceState"); + Bundle bundle = new Bundle(); + bundle.putString("query", sSearchQuery); + bundle.putBoolean("executed", bQueryExecuted); + savedInstanceState.putBundle("SearchDhbw", bundle); + super.onSaveInstanceState(savedInstanceState); + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + Log.i("AppSearchDhbw", "Event: onCreateView"); + Bundle savedState; + if((savedState = getArguments()) != null) + sSearchQuery = savedState.getString("query"); + else if(savedInstanceState != null && (savedState = savedInstanceState.getBundle("SearchDhbw")) != null) { + Log.i("AppSearchDhbw", "Parsed instance state"); + sSearchQuery = savedState.getString("query"); + if(savedState.getBoolean("executed")) { + bQueryExecuted = true; + } + } + if(AppContext == null) + AppContext = CampusAppContext.getInstance(); + oFragmentView = inflater.inflate(R.layout.fragment_appsearch_list, container, false); + + Bundle args = getArguments(); + if(args != null) { + String queryString = args.getString("query"); + if(queryString != null) + sSearchQuery = queryString; + } + + ListView searchResultList = (ListView) oFragmentView.findViewById(R.id.searchResultItems); + appSearchAdapter = new AppSearchListAdapter(oFragmentView.getContext(), R.layout.fragment_appsearch_listitem, searchResultItems); + searchResultList.setAdapter(appSearchAdapter); + + searchResultList.setOnItemClickListener(new AdapterView.OnItemClickListener() { + @Override + public void onItemClick(AdapterView parent, View view, int position, long id) { + AppSearchListItem item = searchResultItems.get(position); + if (item == null) + return; + navigateToResult(item); + } + }); + + return oFragmentView; + } + + public void executeSearch(boolean reset) { + Log.i("AppSearchDhbw", "Action: executeSearch"); + if(AppContext == null) + AppContext = CampusAppContext.getInstance(); + if(bQueryExecuted && !reset) + return; + bQueryExecuted = true; + + searchResultItems.clear(); + + if(seachHelper == null) + seachHelper = new DhbwSearchHelper(); + seachHelper.search(sSearchQuery, new SearchResultListener() { + @Override + public void onSearchResultsReceived(List results) { + Log.i("SearchDhbw", "Success: "+results.size()); + for(AppSearchListItem result : results) + searchResultItems.add(result); + if(appSearchAdapter != null) + appSearchAdapter.notifyDataSetChanged(); + else + Log.i("SearchDhbw", "appSearchAdapter is null in executeSearch"); + } + + @Override + public void onSearchFailed(String error) { + Log.i("SearchDhbw", "Failed: " + error); + } + }); + } + + private void navigateToResult(AppSearchListItem result) { + SearchTarget target = result.getTarget(); + + target.navigate(AppContext.getNavigationManager()); + } + +} diff --git a/app/src/main/java/de/dhbwloe/campusapp/fragments/AppSearchInternal.java b/app/src/main/java/de/dhbwloe/campusapp/fragments/AppSearchInternal.java new file mode 100644 index 0000000..2c10dbb --- /dev/null +++ b/app/src/main/java/de/dhbwloe/campusapp/fragments/AppSearchInternal.java @@ -0,0 +1,129 @@ +package de.dhbwloe.campusapp.fragments; + + +import android.os.Bundle; +import android.support.v4.app.Fragment; +import android.util.Log; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.AdapterView; +import android.widget.ListView; +import android.widget.TextView; + +import java.util.ArrayList; +import java.util.Date; + +import de.dhbwloe.campusapp.CampusAppContext; +import de.dhbwloe.campusapp.CampusAppFragment; +import de.dhbwloe.campusapp.R; +import de.dhbwloe.campusapp.search.SearchIndices; +import de.dhbwloe.campusapp.search.SearchTarget; + +/** + * A simple {@link Fragment} subclass. + */ +public class AppSearchInternal extends CampusAppFragment implements AppSearchProvider { + private String sSearchQuery; + private boolean bQueryExecuted = false; + private AppSearchListAdapter appSearchAdapter; + private ArrayList searchResultItems = new ArrayList(); + + @Override + public void onCreate(Bundle savedInstanceState) { + Log.i("AppSearchInternal", "Event: onCreate"); + super.onCreate(savedInstanceState); + Bundle savedState; + if((savedState = getArguments()) != null) + sSearchQuery = savedState.getString("query"); + else if(savedInstanceState != null && (savedState = savedInstanceState.getBundle("SearchInternal")) != null) { + sSearchQuery = savedState.getString("query"); + if(savedState.getBoolean("executed")) { + executeSearch(false); + } + } + } + + @Override + public void onSaveInstanceState(Bundle savedInstanceState) { + Log.i("AppSearchInternal", "Event: onSaveInstanceState"); + Bundle bundle = new Bundle(); + bundle.putString("query", sSearchQuery); + bundle.putBoolean("executed", bQueryExecuted); + savedInstanceState.putBundle("SearchInternal", bundle); + super.onSaveInstanceState(savedInstanceState); + } + + private void parseIstanceState(Bundle bundle) { + if(sSearchQuery != null) + return; + Bundle args = getArguments(); + if(args != null) { + String queryString = args.getString("query"); + if(queryString != null) + sSearchQuery = queryString; + } + if(sSearchQuery == null && bundle != null) { + String queryString = bundle.getString("query"); + if(queryString != null) + sSearchQuery = queryString; + } + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + Log.i("AppSearchInternal", "Event: onCreateView"); + if(AppContext == null) + AppContext = CampusAppContext.getInstance(); + oFragmentView = inflater.inflate(R.layout.fragment_appsearch_list, container, false); + + parseIstanceState(savedInstanceState); + + ListView searchResultList = (ListView) oFragmentView.findViewById(R.id.searchResultItems); + appSearchAdapter = new AppSearchListAdapter(oFragmentView.getContext(), R.layout.fragment_appsearch_listitem, searchResultItems); + searchResultList.setAdapter(appSearchAdapter); + + searchResultList.setOnItemClickListener(new AdapterView.OnItemClickListener() { + @Override + public void onItemClick(AdapterView parent, View view, int position, long id) { + AppSearchListItem item = searchResultItems.get(position); + if (item == null) + return; + navigateToResult(item); + } + }); + + return oFragmentView; + } + + public void executeSearch(boolean reset) { + if(sSearchQuery == null) + parseIstanceState(null); + Log.i("AppSearchInternal", "Action: executeSearch "+sSearchQuery); + if(AppContext == null) + AppContext = CampusAppContext.getInstance(); + if(bQueryExecuted && !reset) + return; + bQueryExecuted = true; + + searchResultItems.clear(); + + SearchIndices[] indices = AppContext.getDatabaseManager().performSearchRequest(sSearchQuery, 40); + for(int i = 0; i < indices.length; i++) { + SearchTarget target = new SearchTarget(indices[i].getTarget()); + AppSearchListItem item = new AppSearchListItem(indices[i].getTitle(), indices[i].getDescription(), target); + searchResultItems.add(item); + } + if(appSearchAdapter != null) + appSearchAdapter.notifyDataSetChanged(); + + } + + private void navigateToResult(AppSearchListItem result) { + SearchTarget target = result.getTarget(); + + target.navigate(AppContext.getNavigationManager()); + } + +} diff --git a/app/src/main/java/de/dhbwloe/campusapp/fragments/AppSearchListAdapter.java b/app/src/main/java/de/dhbwloe/campusapp/fragments/AppSearchListAdapter.java new file mode 100644 index 0000000..90015d4 --- /dev/null +++ b/app/src/main/java/de/dhbwloe/campusapp/fragments/AppSearchListAdapter.java @@ -0,0 +1,62 @@ +package de.dhbwloe.campusapp.fragments; + +import android.content.Context; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ArrayAdapter; +import android.widget.TextView; + +import java.util.ArrayList; + +import de.dhbwloe.campusapp.R; + +/** + * Created by pk910 on 19.01.2016. + */ +public class AppSearchListAdapter extends ArrayAdapter { + private Context context; + private int layoutResourceId; + private ArrayList data = new ArrayList(); + + public AppSearchListAdapter(Context context, int layoutResourceId, ArrayList data) { + super(context, layoutResourceId, data); + this.layoutResourceId = layoutResourceId; + this.context = context; + this.data = data; + } + + @Override + public View getView(int position, View convertView, ViewGroup parent) { + View row = convertView; + RecordHolder holder = null; + + if (row == null) { + LayoutInflater inflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); + row = inflater.inflate(layoutResourceId, parent, false); + + holder = new RecordHolder(row); + row.setTag(holder); + } else { + holder = (RecordHolder) row.getTag(); + } + + final AppSearchListAdapter that = this; + final AppSearchListItem item = data.get(position); + + holder.resultTitle.setText(item.getTitle()); + holder.resultDescription.setText(item.getDescription()); + + return row; + } + + static class RecordHolder { + TextView resultTitle; + TextView resultDescription; + + public RecordHolder(View view) { + this.resultTitle = (TextView) view.findViewById(R.id.resultTitle); + this.resultDescription = (TextView) view.findViewById(R.id.resultDescription); + } + } +} diff --git a/app/src/main/java/de/dhbwloe/campusapp/fragments/AppSearchListItem.java b/app/src/main/java/de/dhbwloe/campusapp/fragments/AppSearchListItem.java new file mode 100644 index 0000000..cff66a1 --- /dev/null +++ b/app/src/main/java/de/dhbwloe/campusapp/fragments/AppSearchListItem.java @@ -0,0 +1,30 @@ +package de.dhbwloe.campusapp.fragments; + +import de.dhbwloe.campusapp.search.SearchTarget; + +/** + * Created by pk910 on 19.01.2016. + */ +public class AppSearchListItem { + private String title; + private String description; + private SearchTarget target; + + public AppSearchListItem(String title, String description, SearchTarget target) { + this.title = title; + this.description = description; + this.target = target; + } + + public String getTitle() { + return title; + } + + public String getDescription() { + return description; + } + + public SearchTarget getTarget() { + return target; + } +} diff --git a/app/src/main/java/de/dhbwloe/campusapp/fragments/AppSearchProvider.java b/app/src/main/java/de/dhbwloe/campusapp/fragments/AppSearchProvider.java new file mode 100644 index 0000000..550a2b5 --- /dev/null +++ b/app/src/main/java/de/dhbwloe/campusapp/fragments/AppSearchProvider.java @@ -0,0 +1,8 @@ +package de.dhbwloe.campusapp.fragments; + +/** + * Created by pk910 on 25.01.2016. + */ +public interface AppSearchProvider { + public void executeSearch(boolean reset); +} diff --git a/app/src/main/java/de/dhbwloe/campusapp/fragments/AppSearchStuv.java b/app/src/main/java/de/dhbwloe/campusapp/fragments/AppSearchStuv.java new file mode 100644 index 0000000..3aad66b --- /dev/null +++ b/app/src/main/java/de/dhbwloe/campusapp/fragments/AppSearchStuv.java @@ -0,0 +1,126 @@ +package de.dhbwloe.campusapp.fragments; + + +import android.os.Bundle; +import android.support.v4.app.Fragment; +import android.util.Log; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.AdapterView; +import android.widget.ListView; + +import java.util.ArrayList; +import java.util.List; + +import de.dhbwloe.campusapp.CampusAppContext; +import de.dhbwloe.campusapp.CampusAppFragment; +import de.dhbwloe.campusapp.R; +import de.dhbwloe.campusapp.search.DhbwSearchHelper; +import de.dhbwloe.campusapp.search.SearchIndices; +import de.dhbwloe.campusapp.search.SearchResultListener; +import de.dhbwloe.campusapp.search.SearchTarget; +import de.dhbwloe.campusapp.search.StuvSearchHelper; + +/** + * A simple {@link Fragment} subclass. + */ +public class AppSearchStuv extends CampusAppFragment implements AppSearchProvider { + private String sSearchQuery; + private boolean bQueryExecuted = false; + private boolean bExecuteOnload = false; + private AppSearchListAdapter appSearchAdapter; + private StuvSearchHelper seachHelper; + private ArrayList searchResultItems = new ArrayList(); + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + Bundle savedState; + if((savedState = getArguments()) != null) + sSearchQuery = savedState.getString("query"); + if(savedInstanceState != null && (savedState = savedInstanceState.getBundle("SearchStuv")) != null) { + sSearchQuery = savedState.getString("query"); + if(savedState.getBoolean("executed")) { + executeSearch(false); + } + } + } + + @Override + public void onSaveInstanceState(Bundle savedInstanceState) { + Bundle bundle = new Bundle(); + bundle.putString("query", sSearchQuery); + bundle.putBoolean("executed", bQueryExecuted); + savedInstanceState.putBundle("SearchStuv", bundle); + super.onSaveInstanceState(savedInstanceState); + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + if(AppContext == null) + AppContext = CampusAppContext.getInstance(); + oFragmentView = inflater.inflate(R.layout.fragment_appsearch_list, container, false); + + Bundle args = getArguments(); + if(args != null) { + String queryString = args.getString("query"); + if(queryString != null) + sSearchQuery = queryString; + } + + ListView searchResultList = (ListView) oFragmentView.findViewById(R.id.searchResultItems); + if(appSearchAdapter == null) + appSearchAdapter = new AppSearchListAdapter(oFragmentView.getContext(), R.layout.fragment_appsearch_listitem, searchResultItems); + searchResultList.setAdapter(appSearchAdapter); + + searchResultList.setOnItemClickListener(new AdapterView.OnItemClickListener() { + @Override + public void onItemClick(AdapterView parent, View view, int position, long id) { + AppSearchListItem item = searchResultItems.get(position); + if (item == null) + return; + navigateToResult(item); + } + }); + + return oFragmentView; + } + + public void executeSearch(final boolean reset) { + if(AppContext == null) + AppContext = CampusAppContext.getInstance(); + if(bQueryExecuted && !reset) + return; + bQueryExecuted = true; + + searchResultItems.clear(); + + if(seachHelper == null) + seachHelper = new StuvSearchHelper(); + seachHelper.search(sSearchQuery, new SearchResultListener() { + @Override + public void onSearchResultsReceived(List results) { + Log.i("SearchStuv", "Success: "+results.size()); + for(AppSearchListItem result : results) + searchResultItems.add(result); + if(appSearchAdapter != null) + appSearchAdapter.notifyDataSetChanged(); + } + + @Override + public void onSearchFailed(String error) { + Log.i("SearchDhbw", "Failed: " + error); + } + }); + + } + + private void navigateToResult(AppSearchListItem result) { + SearchTarget target = result.getTarget(); + + target.navigate(AppContext.getNavigationManager()); + } + +} diff --git a/app/src/main/java/de/dhbwloe/campusapp/fragments/Dashboard.java b/app/src/main/java/de/dhbwloe/campusapp/fragments/Dashboard.java new file mode 100644 index 0000000..5bea0bc --- /dev/null +++ b/app/src/main/java/de/dhbwloe/campusapp/fragments/Dashboard.java @@ -0,0 +1,43 @@ +package de.dhbwloe.campusapp.fragments; + +import de.dhbwloe.campusapp.CampusAppFragment; + +import android.os.Bundle; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; + +import de.dhbwloe.campusapp.R; +import de.dhbwloe.campusapp.search.SearchIndices; + +public class Dashboard extends CampusAppFragment { + /* implement this for search results ;) */ + public static SearchIndices[] GetSearchIndices() { + return new SearchIndices[] { + new SearchIndices("Dashboard", true) {{ + setUpdateTime(1); + setTarget("#Dashboard"); + setTitle("Dashboard"); + setDescription("Dashboard der App :)"); + addKeyWord("home, dashboard, start, übersicht, overview"); + }}, + }; + } + + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + View view = inflater.inflate(R.layout.fragment_dashboard, container, false); + AppContext.setTitle("Dashboard"); + + + + return view; + } +} diff --git a/app/src/main/java/de/dhbwloe/campusapp/fragments/FirstRun.java b/app/src/main/java/de/dhbwloe/campusapp/fragments/FirstRun.java new file mode 100644 index 0000000..b84a9ab --- /dev/null +++ b/app/src/main/java/de/dhbwloe/campusapp/fragments/FirstRun.java @@ -0,0 +1,37 @@ +package de.dhbwloe.campusapp.fragments; + + +import android.os.Bundle; +import android.support.v4.app.Fragment; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.Button; + +import de.dhbwloe.campusapp.CampusAppFragment; +import de.dhbwloe.campusapp.R; + +/** + * A simple {@link Fragment} subclass. + */ +public class FirstRun extends CampusAppFragment { + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + View view = inflater.inflate(R.layout.fragment_first_run, container, false); + AppContext.setTitle("Campus App"); + + Button startAppBtn = (Button)view.findViewById(R.id.startAppBtn); + startAppBtn.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + AppContext.getDatabaseManager().setRuntimeCache("AppStartCounter", "1"); + AppContext.getNavigationManager().navigatePage("SplashScreen", null, false); + } + }); + + return view; + } + +} diff --git a/app/src/main/java/de/dhbwloe/campusapp/fragments/Impressum.java b/app/src/main/java/de/dhbwloe/campusapp/fragments/Impressum.java new file mode 100644 index 0000000..a888f2a --- /dev/null +++ b/app/src/main/java/de/dhbwloe/campusapp/fragments/Impressum.java @@ -0,0 +1,40 @@ +package de.dhbwloe.campusapp.fragments; + + +import android.os.Bundle; +import android.support.v4.app.Fragment; +import android.support.v4.app.FragmentActivity; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; + +import de.dhbwloe.campusapp.CampusAppFragment; +import de.dhbwloe.campusapp.R; +import de.dhbwloe.campusapp.search.SearchIndices; + +/** + * A simple {@link Fragment} subclass. + */ +public class Impressum extends CampusAppFragment { + /* implement this for search results ;) */ + public static SearchIndices[] GetSearchIndices() { + return new SearchIndices[] { + new SearchIndices("Impressum", true) {{ + setUpdateTime(1); + setTarget("#Impressum"); + setTitle("Impressum"); + setDescription("Impressum der App"); + addKeyWord("impressum, kontakt, autor, author, contact, imprint"); + }}, + }; + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + View view = inflater.inflate(R.layout.fragment_impressum, container, false); + AppContext.setTitle("Impressum"); + + return view; + } +} diff --git a/app/src/main/java/de/dhbwloe/campusapp/fragments/Mensa.java b/app/src/main/java/de/dhbwloe/campusapp/fragments/Mensa.java new file mode 100644 index 0000000..ccc7785 --- /dev/null +++ b/app/src/main/java/de/dhbwloe/campusapp/fragments/Mensa.java @@ -0,0 +1,213 @@ +package de.dhbwloe.campusapp.fragments; + + +import android.os.Bundle; +import android.support.design.widget.TabLayout; +import android.support.v4.app.Fragment; +import android.support.v4.app.FragmentManager; +import android.support.v4.app.FragmentPagerAdapter; +import android.support.v4.view.ViewPager; +import android.support.v7.app.AppCompatActivity; +import android.support.v7.widget.Toolbar; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; + +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; + +import de.dhbwloe.campusapp.CampusAppFragment; +import de.dhbwloe.campusapp.R; +import de.dhbwloe.campusapp.search.SearchIndices; + +/** + * A simple {@link Fragment} subclass. + */ +public class Mensa extends CampusAppFragment { + /* implement this for search results ;) */ + public static SearchIndices[] GetSearchIndices() { + return new SearchIndices[] { + new SearchIndices("Mensa", true) {{ + setUpdateTime(1); + setTarget("#Mensa"); + setTitle("Mensa"); + setDescription("Aktuelle Mensapläne"); + addKeyWord("mensa, kantine, essen, mittagessen, mensaplan, plan, hunger, mittag"); + }}, + }; + } + + private TabLayout tabLayout; + private ViewPager viewPager; + private boolean viewDayplan; + private long viewDate; + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + View view = inflater.inflate(R.layout.fragment_mensa, container, false); + boolean viewmodeSet = false; + Bundle arguments = getArguments(); + viewDate = (new Date()).getTime()/1000; + if(arguments != null) { + String showdate = arguments.getString("showdate"); + long date; + if(showdate != null && !showdate.isEmpty() && (date = Long.parseLong(showdate)) > 0) { + viewDate = date; + viewDayplan = true; + viewmodeSet = true; + } + if(!viewmodeSet) { + String viewmode = arguments.getString("viewmode"); + if(viewmode != null && !viewmode.isEmpty()) { + if(viewmode.equalsIgnoreCase("day")) + viewDayplan = true; + else + viewDayplan = false; + viewmodeSet = true; + } + } + } + if(!viewmodeSet) { + String lastViewmode = AppContext.getDatabaseManager().getRuntimeCache("MensaplanViewMode"); + if(lastViewmode != null && !lastViewmode.isEmpty()) { + if(lastViewmode.equalsIgnoreCase("day")) + viewDayplan = true; + else + viewDayplan = false; + viewmodeSet = true; + } + } + if(!viewmodeSet) + viewDayplan = true; + + AppContext.setTitle("Mensa "+(viewDayplan ? "Tagesplan" : "Wochenplan")); + + /* + toolbar = (Toolbar) view.findViewById(R.id.toolbar); + ((AppCompatActivity) getActivity()).setSupportActionBar(toolbar); + + ((AppCompatActivity) getActivity()).getSupportActionBar().setDisplayHomeAsUpEnabled(true); + */ + + viewPager = (ViewPager) view.findViewById(R.id.viewpager); + setupViewPager(viewPager); + + tabLayout = (TabLayout) view.findViewById(R.id.tabs); + tabLayout.setupWithViewPager(viewPager); + + return view; + } + + private void setupViewPager(ViewPager viewPager) { + final ViewPagerAdapter adapter = new ViewPagerAdapter(getChildFragmentManager()); + + long[] showDates; + long now = (new Date()).getTime()/1000; + if(viewDayplan) + showDates = AppContext.getDatabaseManager().getDaysWithPlanData(now - (30 * 86400), now + (30 * 86400)); + else + showDates = AppContext.getDatabaseManager().getDaysWithPlanData(now - (365 * 86400), now + (30 * 86400)); + + int activeItem = -1; + long activeItemDiff = -1; + Bundle args = getArguments(); + for(int i = 0; i < showDates.length; i++) { + long date = showDates[i]; + Bundle bundle = new Bundle(); + if(args != null) + bundle.putAll(args); + String title; + SimpleDateFormat sdf; + if(viewDayplan) + sdf = new SimpleDateFormat("dd.MM."); + else + sdf = new SimpleDateFormat("WW"); + + if(viewDate > 0) { + long diff = Math.abs(viewDate - date); + if(activeItem == -1 || diff < activeItemDiff) { + activeItem = i; + activeItemDiff = diff; + } + } + + title = sdf.format(new Date(date * 1000)); + bundle.putLong("date", date); + adapter.addFragment(bundle, title); + } + viewPager.setAdapter(adapter); + viewPager.setCurrentItem(activeItem); + if(viewDayplan) { + MensaTagesplan tagesplan = (MensaTagesplan)adapter.getItem(activeItem); + tagesplan.onSetActive(); + } + + viewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() { + @Override + public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { + + } + + @Override + public void onPageSelected(int position) { + if(viewDayplan) { + MensaTagesplan tagesplan = (MensaTagesplan)adapter.getItem(position); + tagesplan.onSetActive(); + } + } + + @Override + public void onPageScrollStateChanged(int state) { + + } + }); + } + + + class ViewPagerAdapter extends FragmentPagerAdapter { + private final List mFragmentList = new ArrayList<>(); + private final List mFragmentDataList = new ArrayList<>(); + private final List mFragmentTitleList = new ArrayList<>(); + + public ViewPagerAdapter(FragmentManager manager) { + super(manager); + } + + @Override + public Fragment getItem(int position) { + Fragment fragment; + while(mFragmentList.size() <= position) { + mFragmentList.add(null); + } + if(mFragmentList.get(position) != null) + return mFragmentList.get(position); + if(viewDayplan) + fragment = new MensaTagesplan(); + else + fragment = new MensaWochenplan(); + fragment.setArguments(mFragmentDataList.get(position)); + mFragmentList.set(position, fragment); + return fragment; + } + + @Override + public int getCount() { + return mFragmentDataList.size(); + } + + public void addFragment(Bundle fragmentdata, String title) { + fragmentdata.putBoolean("parentIsMensaFragment", true); + mFragmentDataList.add(fragmentdata); + mFragmentTitleList.add(title); + } + + @Override + public CharSequence getPageTitle(int position) { + return mFragmentTitleList.get(position); + } + } + +} diff --git a/app/src/main/java/de/dhbwloe/campusapp/fragments/MensaCard.java b/app/src/main/java/de/dhbwloe/campusapp/fragments/MensaCard.java new file mode 100644 index 0000000..e465d72 --- /dev/null +++ b/app/src/main/java/de/dhbwloe/campusapp/fragments/MensaCard.java @@ -0,0 +1,63 @@ +package de.dhbwloe.campusapp.fragments; + +import android.content.Context; +import android.net.Uri; +import android.os.Bundle; +import android.support.v4.app.Fragment; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.TextView; + +import java.text.DecimalFormat; + +import de.dhbwloe.campusapp.CampusAppFragment; +import de.dhbwloe.campusapp.R; +import de.dhbwloe.campusapp.search.SearchIndices; + +/** + * A simple {@link Fragment} subclass. + * Activities that contain this fragment must implement the + * {@link MensaCard.OnFragmentInteractionListener} interface + * to handle interaction events. + * Use the {@link MensaCard#newInstance} factory method to + * create an instance of this fragment. + */ +public class MensaCard extends CampusAppFragment { + private View view; + + /* implement this for search results ;) */ + public static SearchIndices[] GetSearchIndices() { + return new SearchIndices[] { + new SearchIndices("MensaCard", true) {{ + setUpdateTime(1); + setTarget("#MensaCard"); + setTitle("Mensakarte auslesen"); + setDescription("Mensakarte Kontostand"); + addKeyWord("mensa, kantine, essen, mittagessen, mensaplan, karte, ausweis, geld, kontostand, euro"); + }}, + }; + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + view = inflater.inflate(R.layout.fragment_mensa_card, container, false); + AppContext.setTitle("Mensa Guthaben"); + + Bundle args = getArguments(); + if(args != null && args.containsKey("balance")) { + showNfcCardData(args); + } + + return view; + } + + public void showNfcCardData(Bundle bundle) { + TextView cardDataView = (TextView)view.findViewById(R.id.balanceTxt); + double balance = bundle.getDouble("balance"); + DecimalFormat df = new DecimalFormat("#,###.00"); + cardDataView.setText(df.format(balance)+" €"); + } + +} diff --git a/app/src/main/java/de/dhbwloe/campusapp/fragments/MensaTagesplan.java b/app/src/main/java/de/dhbwloe/campusapp/fragments/MensaTagesplan.java new file mode 100644 index 0000000..1d1022a --- /dev/null +++ b/app/src/main/java/de/dhbwloe/campusapp/fragments/MensaTagesplan.java @@ -0,0 +1,113 @@ +package de.dhbwloe.campusapp.fragments; + + +import android.os.Bundle; +import android.support.v4.app.Fragment; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.AdapterView; +import android.widget.ListView; + +import java.text.DateFormat; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Calendar; +import java.util.Date; + +import de.dhbwloe.campusapp.CampusAppFragment; +import de.dhbwloe.campusapp.R; + +/** + * A simple {@link Fragment} subclass. + */ +public class MensaTagesplan extends CampusAppFragment { + private ArrayList tagesplanMenueItems = new ArrayList<>(); + private Date viewPlanDate; + private boolean setActiveOnLoad = false; + + public void onSetActive() { + if(viewPlanDate == null) { + setActiveOnLoad = true; + return; + } + SimpleDateFormat titleDateFormater = new SimpleDateFormat("dd.MM.yyyy"); + AppContext.setTitle("Mensa: " + titleDateFormater.format(viewPlanDate)); + } + + private void setupPlanDate() { + Bundle args = getArguments(); + Date planDay = null; + if(args != null) { + long plandate = args.getLong("date"); + if(plandate > 0) { + planDay = new Date(plandate * 1000); + DateFormat justDay = new SimpleDateFormat("yyyyMMdd"); + try { + planDay = justDay.parse(justDay.format(planDay)); + } catch (Exception e) { + } + } + } + if(planDay == null) { + return; + } + viewPlanDate = planDay; + } + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + setupPlanDate(); + + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + View view = inflater.inflate(R.layout.fragment_mensa_tagesplan, container, false); + + if(viewPlanDate == null) { + AppContext.getNavigationManager().navigatePage("Mensa", null, false); // silent redirect + return null; + } + if(setActiveOnLoad) { + setActiveOnLoad = false; + onSetActive(); + } + + ListView tagesplanItemsList = (ListView) view.findViewById(R.id.tagesplanItems); + MensaTagesplanListAdapter listAdapter = new MensaTagesplanListAdapter(view.getContext(), R.layout.fragment_mensa_tagesplan_listitem, tagesplanMenueItems); + tagesplanItemsList.setAdapter(listAdapter); + + tagesplanItemsList.setOnItemClickListener(new AdapterView.OnItemClickListener() { + @Override + public void onItemClick(AdapterView parent, View view, int position, long id) { + de.dhbwloe.campusapp.mensaplan.MensaTagesplan item = tagesplanMenueItems.get(position); + if (item == null) + return; + + // do sth? + } + }); + + requestMenues(); + + return view; + } + + private void requestMenues() { + Date endOfDay = (Date)viewPlanDate.clone(); + endOfDay.setTime(endOfDay.getTime() + (86400 - 1) * 1000); + + de.dhbwloe.campusapp.mensaplan.MensaTagesplan menues[] = AppContext.getDatabaseManager().getMensaTagesplan(viewPlanDate.getTime()/1000, endOfDay.getTime()/1000); + tagesplanMenueItems.clear(); + for(de.dhbwloe.campusapp.mensaplan.MensaTagesplan menue : menues) { + tagesplanMenueItems.add(menue); + } + + } + +} diff --git a/app/src/main/java/de/dhbwloe/campusapp/fragments/MensaTagesplanListAdapter.java b/app/src/main/java/de/dhbwloe/campusapp/fragments/MensaTagesplanListAdapter.java new file mode 100644 index 0000000..f0bf2a1 --- /dev/null +++ b/app/src/main/java/de/dhbwloe/campusapp/fragments/MensaTagesplanListAdapter.java @@ -0,0 +1,66 @@ +package de.dhbwloe.campusapp.fragments; + +import android.content.Context; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ArrayAdapter; +import android.widget.TextView; + +import java.util.ArrayList; + +import de.dhbwloe.campusapp.R; +import de.dhbwloe.campusapp.mensaplan.*; + +/** + * Created by pk910 on 24.01.2016. + */ + +public class MensaTagesplanListAdapter extends ArrayAdapter { + private Context context; + private int layoutResourceId; + private ArrayList data = new ArrayList(); + + public MensaTagesplanListAdapter(Context context, int layoutResourceId, ArrayList data) { + super(context, layoutResourceId, data); + this.layoutResourceId = layoutResourceId; + this.context = context; + this.data = data; + } + + @Override + public View getView(int position, View convertView, ViewGroup parent) { + View row = convertView; + RecordHolder holder = null; + + if (row == null) { + LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); + row = inflater.inflate(layoutResourceId, parent, false); + + holder = new RecordHolder(row); + row.setTag(holder); + } else { + holder = (RecordHolder) row.getTag(); + } + + final MensaTagesplanListAdapter that = this; + final de.dhbwloe.campusapp.mensaplan.MensaTagesplan item = data.get(position); + + if(holder.txtMenueName != null) + holder.txtMenueName.setText(item.getMenuName()); + if(holder.txtName != null) + holder.txtName.setText(item.getName()); + + return row; + } + + static class RecordHolder { + TextView txtMenueName; + TextView txtName; + + public RecordHolder(View view) { + this.txtMenueName = (TextView) view.findViewById(R.id.txtMenueName); + this.txtName = (TextView) view.findViewById(R.id.txtName); + } + } +} \ No newline at end of file diff --git a/app/src/main/java/de/dhbwloe/campusapp/fragments/MensaWochenplan.java b/app/src/main/java/de/dhbwloe/campusapp/fragments/MensaWochenplan.java new file mode 100644 index 0000000..35917f0 --- /dev/null +++ b/app/src/main/java/de/dhbwloe/campusapp/fragments/MensaWochenplan.java @@ -0,0 +1,31 @@ +package de.dhbwloe.campusapp.fragments; + + +import android.os.Bundle; +import android.support.v4.app.Fragment; +import android.support.v4.app.FragmentActivity; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; + +import de.dhbwloe.campusapp.CampusAppFragment; +import de.dhbwloe.campusapp.R; +import de.dhbwloe.campusapp.search.SearchIndices; + +/** + * A simple {@link Fragment} subclass. + */ +public class MensaWochenplan extends CampusAppFragment { + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + View view = inflater.inflate(R.layout.fragment_mensa_wochenplan, container, false); + AppContext.setTitle("Mensa Wochenplan: "); + + + + return view; + } + +} diff --git a/app/src/main/java/de/dhbwloe/campusapp/fragments/News.java b/app/src/main/java/de/dhbwloe/campusapp/fragments/News.java new file mode 100644 index 0000000..6a48b68 --- /dev/null +++ b/app/src/main/java/de/dhbwloe/campusapp/fragments/News.java @@ -0,0 +1,42 @@ +package de.dhbwloe.campusapp.fragments; + + +import android.os.Bundle; +import android.support.v4.app.Fragment; +import android.support.v4.app.FragmentActivity; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; + +import de.dhbwloe.campusapp.CampusAppFragment; +import de.dhbwloe.campusapp.R; +import de.dhbwloe.campusapp.search.SearchIndices; + +/** + * A simple {@link Fragment} subclass. + */ +public class News extends CampusAppFragment { + /* implement this for search results ;) */ + public static SearchIndices[] GetSearchIndices() { + return new SearchIndices[] { + new SearchIndices("News", true) {{ + setUpdateTime(1); + setTarget("#News"); + setTitle("News"); + setDescription("News der DHBW & Stuv"); + addKeyWord("news, dhbw, stuv, termin, termine"); + }}, + }; + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + View view = inflater.inflate(R.layout.fragment_news, container, false); + AppContext.setTitle("News"); + + + return view; + } + +} diff --git a/app/src/main/java/de/dhbwloe/campusapp/fragments/PopupFragment.java b/app/src/main/java/de/dhbwloe/campusapp/fragments/PopupFragment.java new file mode 100644 index 0000000..69f36d5 --- /dev/null +++ b/app/src/main/java/de/dhbwloe/campusapp/fragments/PopupFragment.java @@ -0,0 +1,80 @@ +package de.dhbwloe.campusapp.fragments; + +import android.content.DialogInterface; +import android.os.Bundle; +import android.support.v4.app.DialogFragment; +import android.support.v4.app.FragmentActivity; +import android.support.v4.app.FragmentTransaction; +import android.view.Gravity; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.LinearLayout; +import android.widget.PopupWindow; + +import de.dhbwloe.campusapp.CampusAppContext; +import de.dhbwloe.campusapp.CampusAppFragment; +import de.dhbwloe.campusapp.R; +import de.dhbwloe.campusapp.search.SearchIndices; +import de.dhbwloe.campusapp.search.SearchTarget; + +/** + * Created by pk910 on 21.01.2016. + */ +public class PopupFragment extends DialogFragment { + private CampusAppContext AppContext; + private CampusAppFragment oCurrentFragment; + + @Override + public void onCreate(Bundle savedInstanceState) { + AppContext = CampusAppContext.getInstance(); + super.onCreate(savedInstanceState); + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + View view = inflater.inflate(R.layout.content_popup, container, false); + getDialog().setOnCancelListener(new DialogInterface.OnCancelListener() { + @Override + public void onCancel(DialogInterface dialog) { + AppContext.getNavigationManager().closeDialog(); + } + }); + + Bundle args = getArguments(); + if(args != null) { + String targetPage = args.getString("target"); + SearchTarget target = new SearchTarget(targetPage); + String targetPageName; + if(target.isInAppTarget()) + targetPageName = target.getTargetUrl(); + else { + targetPageName = "WebBrowser"; + args.putString("url", target.getTargetUrl()); + } + CampusAppFragment fragment = AppContext.getNavigationManager().getPageFragment(targetPageName); + fragment.setArguments(args); + + FragmentTransaction transaction = getChildFragmentManager().beginTransaction(); + transaction.add(R.id.content_container, fragment); + oCurrentFragment = fragment; + transaction.commit(); + } + + return view; + } + + public void destroyView() { + if(oCurrentFragment != null) { + FragmentTransaction transaction = getChildFragmentManager().beginTransaction(); + transaction.remove(oCurrentFragment); + transaction.commit(); + } + } + + public CampusAppFragment getCurrentFragment() { + return oCurrentFragment; + } + +} \ No newline at end of file diff --git a/app/src/main/java/de/dhbwloe/campusapp/fragments/SplashScreen.java b/app/src/main/java/de/dhbwloe/campusapp/fragments/SplashScreen.java new file mode 100644 index 0000000..eb543d5 --- /dev/null +++ b/app/src/main/java/de/dhbwloe/campusapp/fragments/SplashScreen.java @@ -0,0 +1,254 @@ +package de.dhbwloe.campusapp.fragments; + + +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.graphics.Matrix; +import android.graphics.Point; +import android.graphics.drawable.Drawable; +import android.os.Bundle; +import android.os.Handler; +import android.support.v4.app.Fragment; +import android.view.Display; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.view.ViewTreeObserver; +import android.widget.ImageView; +import android.widget.ProgressBar; +import android.widget.TextView; + +import java.util.Date; + +import de.dhbwloe.campusapp.CampusApp; +import de.dhbwloe.campusapp.CampusAppFragment; +import de.dhbwloe.campusapp.R; +import de.dhbwloe.campusapp.mensaplan.MensaplanManager; +import de.dhbwloe.campusapp.mensaplan.MensaplanManagerInterface; +import de.dhbwloe.campusapp.news.NewsManager; +import de.dhbwloe.campusapp.news.NewsManagerInterface; +import de.dhbwloe.campusapp.vorlesungen.VorlesungsplanManager; +import de.dhbwloe.campusapp.vorlesungen.VorlesungsplanManagerInterface; + +/** + * A simple {@link Fragment} subclass. + */ +public class SplashScreen extends CampusAppFragment { + private ProgressBar splashProgress; + private int progressCounter; + private Handler timerHandler = new Handler(); + private Runnable timerRunnable; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + + } + + @Override + public void onSaveInstanceState(Bundle savedInstanceState) { + timerHandler.removeCallbacksAndMessages(null); + super.onSaveInstanceState(savedInstanceState); + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + View view = inflater.inflate(R.layout.fragment_splashscreen, container, false); + + splashProgress = (ProgressBar)view.findViewById(R.id.splashProgress); + splashProgress.setMax(20); + splashProgress.setProgress(0); + progressCounter = 0; + + AppContext.setTitle("DHBW Lörrach"); + + ImageView splashImage = (ImageView)view.findViewById(R.id.splashImage); + BitmapFactory.Options dimensions = new BitmapFactory.Options(); + Bitmap mBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.dhbw_campus_hd, dimensions); + int height = dimensions.outHeight; + int width = dimensions.outWidth; + Display display = AppContext.getMainActivity().getWindowManager().getDefaultDisplay(); + Point size = new Point(); + display.getSize(size); + float scaleX = (float)size.x / (float)width; + float scaleY = (float)size.y / (float)height; + float scale = Math.max(scaleX, scaleY); + int newWidth = (int)(width*scale); + int newHeight = (int)(height*scale); + Bitmap newBitmap = Bitmap.createScaledBitmap(mBitmap, newWidth, newHeight, true); + splashImage.setImageBitmap(newBitmap); + + timerRunnable = new Runnable() { + @Override + public void run() { + progressCounter++; + splashProgress.setProgress(progressCounter); + long now = (new Date()).getTime()/1000; + switch(progressCounter) { + case 1: + AppContext.getDatabaseManager().initializeDatabase(); + break; + case 2: + AppContext.addDefaultSearchIndexes(); + break; + case 3: + String startCounter = AppContext.getDatabaseManager().getRuntimeCache("AppStartCounter"); + if(startCounter == null || Integer.parseInt(startCounter) == 0) { + AppContext.getNavigationManager().navigatePage("FirstRun", null, false); + return; + } + AppContext.getDatabaseManager().setRuntimeCache("AppStartCounter", Integer.toString(Integer.parseInt(startCounter) + 1)); + break; + case 4: + String lastVLMFullSyncStr = AppContext.getDatabaseManager().getRuntimeCache("LastVLMFullSync"); + String lastVLMPartialSyncStr = AppContext.getDatabaseManager().getRuntimeCache("LastVLMPartialSync"); + long lastVLMFullSync, lastVLMPartialSync; + if(lastVLMFullSyncStr != null) + lastVLMFullSync = Long.parseLong(lastVLMFullSyncStr); + else + lastVLMFullSync = 0; + if(lastVLMPartialSyncStr != null) + lastVLMPartialSync = Long.parseLong(lastVLMPartialSyncStr); + else + lastVLMPartialSync = 0; + + VorlesungsplanManager vpm = new VorlesungsplanManager(AppContext, "tif13a"); + + if(lastVLMFullSync == 0 || now - lastVLMFullSync > (86400 * 14)) { // full sync every 14 days + vpm.performFullSynchronisation(new VorlesungsplanManagerInterface() { + @Override + public void onVorlesungsplanUpdateDone() { + long now = (new Date()).getTime() / 1000; + AppContext.getDatabaseManager().setRuntimeCache("LastVLMFullSync", Long.toString(now)); + AppContext.getDatabaseManager().setRuntimeCache("LastVLMPartialSync", Long.toString(now)); + timerHandler.postDelayed(timerRunnable, 100); + } + + @Override + public void onVorlesungsplanUpdateFail(String errorMessage) { + timerHandler.postDelayed(timerRunnable, 100); + } + }); + } + else if(lastVLMPartialSync == 0 || now - lastVLMPartialSync > (86400)) { // partial sync every day + vpm.performFastSynchronisation(new VorlesungsplanManagerInterface() { + @Override + public void onVorlesungsplanUpdateDone() { + long now = (new Date()).getTime()/1000; + AppContext.getDatabaseManager().setRuntimeCache("LastVLMPartialSync", Long.toString(now)); + timerHandler.postDelayed(timerRunnable, 100); + } + @Override + public void onVorlesungsplanUpdateFail(String errorMessage) { + timerHandler.postDelayed(timerRunnable, 100); + } + }); + } + else + break; + return; + case 5: + String lastMPMSyncStr = AppContext.getDatabaseManager().getRuntimeCache("LastMPSync"); + long lastMPMSync; + if(lastMPMSyncStr != null) + lastMPMSync = Long.parseLong(lastMPMSyncStr); + else + lastMPMSync = 0; + + MensaplanManager mpm = new MensaplanManager(AppContext); + + if(lastMPMSync == 0 || now - lastMPMSync > (86400 * 2)) { //sync every 2 days + mpm.performSynchronisation(new MensaplanManagerInterface() { + @Override + public void onMensaplanUpdateDone() { + long now = (new Date()).getTime() / 1000; + AppContext.getDatabaseManager().setRuntimeCache("LastMPSync", Long.toString(now)); + timerHandler.postDelayed(timerRunnable, 100); + } + + @Override + public void onMensaplanUpdateFail(String errorMessage) { + timerHandler.postDelayed(timerRunnable, 100); + } + }); + } + else + break; + return; + case 6: + case 7: + final String syncSource = (progressCounter == 6 ? "DHBW" : "STUV"); + String lastNewsSyncStr = AppContext.getDatabaseManager().getRuntimeCache("LastNewsSync_"+syncSource); + long lastNewsSync; + if(lastNewsSyncStr != null) + lastNewsSync = Long.parseLong(lastNewsSyncStr); + else + lastNewsSync = 0; + + NewsManager nm = new NewsManager(AppContext, syncSource); + + if(lastNewsSync == 0 || now - lastNewsSync > (86400 * 1)) { //sync every day + nm.performSynchronisation(new NewsManagerInterface() { + @Override + public void onNewsUpdateDone() { + long now = (new Date()).getTime() / 1000; + AppContext.getDatabaseManager().setRuntimeCache("LastNewsSync_"+syncSource, Long.toString(now)); + timerHandler.postDelayed(timerRunnable, 100); + } + + @Override + public void onNewsUpdateFail(String errorMessage) { + timerHandler.postDelayed(timerRunnable, 100); + } + }); + } + else + break; + return; + case 8: + String lastStuvSyncStr = AppContext.getDatabaseManager().getRuntimeCache("LastStuvSync"); + long lastStuvSync; + if(lastStuvSyncStr != null) + lastStuvSync = Long.parseLong(lastStuvSyncStr); + else + lastStuvSync = 0; + + VorlesungsplanManager stuvsyncmgr = new VorlesungsplanManager(AppContext, "STUV"); + + if(lastStuvSync == 0 || now - lastStuvSync > (86400 * 3)) { // full sync every 3 days + stuvsyncmgr.performFullSynchronisation(new VorlesungsplanManagerInterface() { + @Override + public void onVorlesungsplanUpdateDone() { + long now = (new Date()).getTime() / 1000; + AppContext.getDatabaseManager().setRuntimeCache("LastStuvSync", Long.toString(now)); + timerHandler.postDelayed(timerRunnable, 100); + } + + @Override + public void onVorlesungsplanUpdateFail(String errorMessage) { + timerHandler.postDelayed(timerRunnable, 100); + } + }); + } + else + break; + return; + + // some more tasks to do here? + + case 20: + ((CampusApp)AppContext.getMainActivity()).loadMainUi(); + AppContext.getNavigationManager().navigatePage("Dashboard", null, false); + return; + } + timerHandler.postDelayed(timerRunnable, 100); + } + }; + timerHandler.postDelayed(timerRunnable, 500); + + return view; + } + +} diff --git a/app/src/main/java/de/dhbwloe/campusapp/fragments/Vorlesungsplan.java b/app/src/main/java/de/dhbwloe/campusapp/fragments/Vorlesungsplan.java new file mode 100644 index 0000000..caf8c88 --- /dev/null +++ b/app/src/main/java/de/dhbwloe/campusapp/fragments/Vorlesungsplan.java @@ -0,0 +1,40 @@ +package de.dhbwloe.campusapp.fragments; + + +import android.os.Bundle; +import android.support.v4.app.Fragment; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; + +import de.dhbwloe.campusapp.CampusAppFragment; +import de.dhbwloe.campusapp.R; +import de.dhbwloe.campusapp.search.SearchIndices; + +/** + * A simple {@link Fragment} subclass. + */ +public class Vorlesungsplan extends CampusAppFragment { + /* implement this for search results ;) */ + public static SearchIndices[] GetSearchIndices() { + return new SearchIndices[] { + new SearchIndices("Vorlesungsplan", true) {{ + setUpdateTime(1); + setTarget("#Vorlesungsplan"); + setTitle("Vorlesungsplan"); + setDescription("Vorlesungsplan dienes Kurses"); + addKeyWord("vorlesung, vorlesungen, plan, vorlesungsplan, stundenplan, termin, termine, kursplan, blockplan, block, zeit, zeiten"); + }}, + }; + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + View view = inflater.inflate(R.layout.fragment_vorlesungsplan, container, false); + AppContext.setTitle("Vorlesungsplan"); + + return view; + } + +} diff --git a/app/src/main/java/de/dhbwloe/campusapp/fragments/WebBrowser.java b/app/src/main/java/de/dhbwloe/campusapp/fragments/WebBrowser.java new file mode 100644 index 0000000..9c6f003 --- /dev/null +++ b/app/src/main/java/de/dhbwloe/campusapp/fragments/WebBrowser.java @@ -0,0 +1,68 @@ +package de.dhbwloe.campusapp.fragments; + + +import android.content.Intent; +import android.net.Uri; +import android.os.Bundle; +import android.support.v4.app.Fragment; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.webkit.WebView; + +import de.dhbwloe.campusapp.CampusAppFragment; +import de.dhbwloe.campusapp.R; + +public class WebBrowser extends CampusAppFragment { + private boolean bRedirectedToBrowser = false; + private boolean bBrowserRunning = false; + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + View view = inflater.inflate(R.layout.fragment_web_browser, container, false); + WebView webview = (WebView) view.findViewById(R.id.browserWebView); + + Bundle args = getArguments(); + if(args == null) { + AppContext.getNavigationManager().navigatePage("Dashboard", null, false); + return null; + } + + String str; + if((str = args.getString("html")) != null) { + showWebViewHtml(webview, str); + } + else if((str = args.getString("url")) != null) { + Intent browserIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(str)); + startActivity(browserIntent); + + bRedirectedToBrowser = true; + return null; + } + + return view; + } + + private void showWebViewHtml(WebView webview, String html) { + webview.loadData(html, "text/html", null); + } + + @Override + public void onPause() { + super.onPause(); + + if(bRedirectedToBrowser) + bBrowserRunning = true; + } + + @Override + public void onResume() { + super.onResume(); + + if(bRedirectedToBrowser && bBrowserRunning) { + AppContext.getMainActivity().onBackPressed(); + } + } + +} diff --git a/app/src/main/java/de/dhbwloe/campusapp/fragments/WifiSettings.java b/app/src/main/java/de/dhbwloe/campusapp/fragments/WifiSettings.java new file mode 100644 index 0000000..d57671a --- /dev/null +++ b/app/src/main/java/de/dhbwloe/campusapp/fragments/WifiSettings.java @@ -0,0 +1,97 @@ +package de.dhbwloe.campusapp.fragments; + + +import android.annotation.TargetApi; +import android.net.wifi.WifiConfiguration; +import android.net.wifi.WifiEnterpriseConfig; +import android.net.wifi.WifiManager; +import android.os.Build; +import android.os.Bundle; +import android.support.v4.app.Fragment; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.Button; +import android.widget.EditText; + +import java.lang.reflect.Field; + +import de.dhbwloe.campusapp.CampusAppFragment; +import de.dhbwloe.campusapp.R; +import de.dhbwloe.campusapp.search.SearchIndices; + +/** + * A simple {@link Fragment} subclass. + */ +public class WifiSettings extends CampusAppFragment { + /* implement this for search results ;) */ + public static SearchIndices[] GetSearchIndices() { + return new SearchIndices[] { + new SearchIndices("WifiSettings", true) {{ + setUpdateTime(1); + setTarget("#WifiSettings"); + setTitle("Wifi Settings"); + setDescription("WLAN Konfiguration für dieses Gerät"); + addKeyWord("wlan, secure, dhbw-secure, wifi, w-lan, wireless, internet, netzwerk"); + }}, + }; + } + + private View view; + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + view = inflater.inflate(R.layout.fragment_wifi_settings, container, false); + AppContext.setTitle("DHBW WLAN Settings"); + + Button connectBtn = (Button) view.findViewById(R.id.wifiConnectBtn); + connectBtn.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + Button connectBtn = (Button)v; + connectBtn.setEnabled(false); + + EditText usernameEdt = (EditText) view.findViewById(R.id.wifiUsername); + EditText passwordEdt = (EditText) view.findViewById(R.id.wifiPassword); + + String username = usernameEdt.getText().toString(); + String password = passwordEdt.getText().toString(); + + if(Build.VERSION.SDK_INT < 18) { + // connectToDHWiFi not supported! + // do something else? + } + else if(username.length() > 0 && password.length() > 0) + connectToDHWiFi(username, password); + + connectBtn.setEnabled(true); + } + }); + + return view; + } + + @TargetApi(18) + private void connectToDHWiFi(String username, String password) { + WifiConfiguration wifiConfig = new WifiConfiguration(); + wifiConfig.SSID = "dhbw-secure"; + wifiConfig.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_EAP); + wifiConfig.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.IEEE8021X); + + WifiEnterpriseConfig enterpriseConfig = new WifiEnterpriseConfig(); + enterpriseConfig.setIdentity(username); + enterpriseConfig.setPassword(password); + enterpriseConfig.setEapMethod(WifiEnterpriseConfig.Eap.PEAP); + enterpriseConfig.setPhase2Method(WifiEnterpriseConfig.Phase2.MSCHAPV2); + wifiConfig.enterpriseConfig = enterpriseConfig; + + WifiManager wfm = (WifiManager)AppContext.getMainActivity().getSystemService(AppContext.getMainActivity().WIFI_SERVICE); + + int networkId = wfm.addNetwork(wifiConfig); + wfm.enableNetwork(networkId, true); + } + + + +} diff --git a/app/src/main/java/de/dhbwloe/campusapp/mensaplan/MensaTagesplan.java b/app/src/main/java/de/dhbwloe/campusapp/mensaplan/MensaTagesplan.java new file mode 100644 index 0000000..79c33e4 --- /dev/null +++ b/app/src/main/java/de/dhbwloe/campusapp/mensaplan/MensaTagesplan.java @@ -0,0 +1,122 @@ +package de.dhbwloe.campusapp.mensaplan; + +import java.text.DateFormat; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.Locale; +import java.util.zip.CRC32; + +/** + * Created by pk910 on 22.01.2016. + */ +public class MensaTagesplan { + private long iPlanDate; + private long iChkSum; + private String sMenuName, sName, sNameHtml, sAdditional, sNotes; + private int[] aPriceArray = new int[4]; + + private boolean bIsNew = false; + + public MensaTagesplan(long plandate, String menuname, long chksum, String name, String namehtml, String additional, String notes, int price1, int price2, int price3, int price4) { + iPlanDate = plandate; + sMenuName = menuname; + iChkSum = chksum; + sName = name; + sNameHtml = namehtml; + sAdditional = (additional == null ? "" : additional); + sNotes = notes; + aPriceArray[0] = price1; // Student + aPriceArray[1] = price2; // Employees + aPriceArray[2] = price3; // Guest + aPriceArray[3] = price4; // School + } + + public long calculateChkSum() { + CRC32 crc = new CRC32(); + crc.update(sMenuName.getBytes()); + crc.update(sName.getBytes()); + crc.update(sNameHtml.getBytes()); + if(sAdditional!= null) + crc.update(sAdditional.getBytes()); + if(sNotes!= null) + crc.update(sNotes.getBytes()); + crc.update(aPriceArray[0]); + crc.update(aPriceArray[1]); + crc.update(aPriceArray[2]); + crc.update(aPriceArray[3]); + + long crcvalue = crc.getValue(); + iChkSum = crcvalue; + return crcvalue; + } + + public long getPlanDate() { + return iPlanDate; + } + + public String getFormatedDate() { + DateFormat df = new SimpleDateFormat("dd.MM.yyyy", Locale.ENGLISH); + return df.format(new Date(iPlanDate * 1000)); + } + + public long getChkSum() { + return iChkSum; + } + + public String getMenuName() { + return sMenuName; + } + + public String getName() { + return sName; + } + + public String getNameHtml() { + return sNameHtml; + } + + public String getAdditional() { + return sAdditional; + } + + public String getNotes() { + return sNotes; + } + + public int[] getPlainPrice() { + return aPriceArray; + } + + public double getStudentPrice() { + double price = (aPriceArray[0] / 100.0); + return price; + } + + public double getEmployeePrice() { + double price = (aPriceArray[1] / 100.0); + return price; + } + + public double getGuestPrice() { + double price = (aPriceArray[2] / 100.0); + return price; + } + + public double getSchoolPrice() { + double price = (aPriceArray[3] / 100.0); + return price; + } + + public void setIsNew() { + bIsNew = true; + } + + public boolean getIsNew(boolean reset) { + boolean isnew = bIsNew; + if(reset) + bIsNew = false; + return isnew; + } + +} diff --git a/app/src/main/java/de/dhbwloe/campusapp/mensaplan/MensaplanManager.java b/app/src/main/java/de/dhbwloe/campusapp/mensaplan/MensaplanManager.java new file mode 100644 index 0000000..932673d --- /dev/null +++ b/app/src/main/java/de/dhbwloe/campusapp/mensaplan/MensaplanManager.java @@ -0,0 +1,151 @@ +package de.dhbwloe.campusapp.mensaplan; + +import android.util.Log; + +import java.io.UnsupportedEncodingException; +import java.net.URLEncoder; +import java.text.DateFormat; +import java.text.NumberFormat; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; +import java.util.Locale; + +import de.dhbwloe.campusapp.CampusAppContext; +import de.dhbwloe.campusapp.network.XmlEntry; +import de.dhbwloe.campusapp.network.XmlRequestHelper; +import de.dhbwloe.campusapp.search.SearchIndices; +import de.dhbwloe.campusapp.vorlesungen.VorlesungsplanManagerInterface; + +/** + * Created by pk910 on 22.01.2016. + */ +public class MensaplanManager extends XmlRequestHelper { + private CampusAppContext AppContext; + private boolean bRequestRunning = false; + private ArrayList aCallbackInterfaces = new ArrayList(); + + public MensaplanManager(CampusAppContext context) { + AppContext = context; + } + + public void performSynchronisation(MensaplanManagerInterface callback) { + aCallbackInterfaces.add(callback); + if(bRequestRunning) + return; + + bRequestRunning = true; + String mensaplanUrl = "http://www.swfr.de/index.php?id=1400&type=98&tx_swfrspeiseplan_pi1[apiKey]=c3841e89a2c8c301b890723ecdb786ad&tx_swfrspeiseplan_pi1[ort]=677"; + requestXmlFromWeb(mensaplanUrl, "plan", "tagesplan"); + } + + private long getTimeFromDateString(String timestamp) { + // 01.03.2016 + if(timestamp == null) + return 0; + DateFormat df = new SimpleDateFormat("dd.MM.yyyy", Locale.ENGLISH); + try { + Date result = df.parse(timestamp); + return result.getTime()/1000; + } catch (ParseException e) { + return 0; + } + } + + private int getPriceFromString(String pricestr) { + // 2,90€ + if(pricestr == null) + return 0; + NumberFormat nf = NumberFormat.getInstance(Locale.GERMAN); + try { + Number result = nf.parse(pricestr.replace("€", "")); + return (int)(result.doubleValue()*100); + } catch (ParseException e) { + return 0; + } + } + + @Override + protected void onXmlReceived(List entries) { + ArrayList newIndices = new ArrayList(); + + Log.i("MPMSync", "Received Mensaplan XML"); + for(XmlEntry entry : entries) { + Log.i("MPMSync", "Parse Tagesplan: "+entry.getAttribute("datum")); + long plandate = getTimeFromDateString(entry.getAttribute("datum")); + if(plandate == 0) + continue; + + XmlEntry[] menues = XmlEntry.FindXmlEntriesByName(entry, "menue"); + for(XmlEntry menue : menues) { + XmlEntry centry; + String menuname = menue.getAttribute("art"); + Log.i("MPMSync", "Parse menu "+menuname); + if(menuname == null) continue; + + String additional = menue.getAttribute("zusatz"); + + centry = XmlEntry.FindXmlEntryByName(menue, "name"); + String name = (centry == null ? null : centry.getValue()); + + centry = XmlEntry.FindXmlEntryByName(menue, "nameMitUmbruch"); + String nameHtml = (centry == null ? null : centry.getValue()); + + centry = XmlEntry.FindXmlEntryByName(menue, "kennzeichnungen"); + String notes = (centry == null ? null : centry.getValue()); + + int priceArray[] = new int[4]; + + centry = XmlEntry.FindXmlEntryByName(menue, "studierende"); + priceArray[0] = (centry == null ? 0 : getPriceFromString(centry.getValue())); + centry = XmlEntry.FindXmlEntryByName(menue, "angestellte"); + priceArray[1] = (centry == null ? 0 : getPriceFromString(centry.getValue())); + centry = XmlEntry.FindXmlEntryByName(menue, "gaeste"); + priceArray[2] = (centry == null ? 0 : getPriceFromString(centry.getValue())); + centry = XmlEntry.FindXmlEntryByName(menue, "schueler"); + priceArray[3] = (centry == null ? 0 : getPriceFromString(centry.getValue())); + + MensaTagesplan tagesplan = new MensaTagesplan(plandate, menuname, 0, name, nameHtml, additional, notes, priceArray[0], priceArray[1], priceArray[2], priceArray[3]); + tagesplan.calculateChkSum(); + + AppContext.getDatabaseManager().updateMensaTagesplan(tagesplan); + + if(tagesplan.getIsNew(true)) { + SearchIndices indices = new SearchIndices("MensaTagesplan#"+tagesplan.getPlanDate()+"|"+tagesplan.getMenuName(), false); + indices.setUpdateTime(tagesplan.getPlanDate()); + try { + indices.setTarget("#Mensa#showdate=" + tagesplan.getPlanDate()+"&showmenu=" + URLEncoder.encode(tagesplan.getMenuName(), "UTF-8")); + } catch (UnsupportedEncodingException e) { + indices.setTarget("#Mensa#showdate=" + tagesplan.getPlanDate()); + } + indices.setTitle("Mensaplan " + tagesplan.getFormatedDate()); + indices.setDescription(tagesplan.getMenuName() + ": " + tagesplan.getName()); + indices.addKeyWord(tagesplan.getName()); + indices.addKeyWord(tagesplan.getNotes()); + newIndices.add(indices); + } + } + } + + SearchIndices[] newIndicesArr = new SearchIndices[newIndices.size()]; + newIndicesArr = newIndices.toArray(newIndicesArr); + AppContext.addSearchIndices(newIndicesArr); + + for(MensaplanManagerInterface callback : aCallbackInterfaces) { + callback.onMensaplanUpdateDone(); + } + aCallbackInterfaces.clear(); + bRequestRunning = false; + } + + @Override + protected void onXmlRequestFail(int statusCode, String errorMessage) { + for(MensaplanManagerInterface callback : aCallbackInterfaces) { + callback.onMensaplanUpdateFail("error " + statusCode + ": " + errorMessage); + } + aCallbackInterfaces.clear(); + bRequestRunning = false; + } +} diff --git a/app/src/main/java/de/dhbwloe/campusapp/mensaplan/MensaplanManagerInterface.java b/app/src/main/java/de/dhbwloe/campusapp/mensaplan/MensaplanManagerInterface.java new file mode 100644 index 0000000..463bb84 --- /dev/null +++ b/app/src/main/java/de/dhbwloe/campusapp/mensaplan/MensaplanManagerInterface.java @@ -0,0 +1,11 @@ +package de.dhbwloe.campusapp.mensaplan; + +/** + * Created by pk910 on 22.01.2016. + */ +public interface MensaplanManagerInterface { + + public void onMensaplanUpdateDone(); + public void onMensaplanUpdateFail(String errorMessage); + +} diff --git a/app/src/main/java/de/dhbwloe/campusapp/network/IscRequestHelper.java b/app/src/main/java/de/dhbwloe/campusapp/network/IscRequestHelper.java new file mode 100644 index 0000000..f840366 --- /dev/null +++ b/app/src/main/java/de/dhbwloe/campusapp/network/IscRequestHelper.java @@ -0,0 +1,83 @@ +package de.dhbwloe.campusapp.network; + +import android.util.Log; + +import com.loopj.android.http.AsyncHttpClient; +import com.loopj.android.http.AsyncHttpResponseHandler; + +import net.fortuna.ical4j.data.CalendarBuilder; +import net.fortuna.ical4j.data.ParserException; +import net.fortuna.ical4j.model.Calendar; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.UnsupportedEncodingException; + +import cz.msebera.android.httpclient.Header; + +/** + * Created by pk910 on 20.01.2016. + */ +public abstract class IscRequestHelper { + + protected void requestCalenderFromWeb(String url) { + AsyncHttpClient client = new AsyncHttpClient(); + Log.i("HTTPClient", "Request: "+url); + client.get(url, new AsyncHttpResponseHandler() { + @Override + public void onStart() { + } + @Override + public void onSuccess(int statusCode, Header[] headers, byte[] response) { + String plainisc = new String(response); + // Google Calendar parse error! + plainisc = plainisc.replaceAll("(X-APPLE-TRAVEL-ADVISORY-BEHAVIOR;)?ACKNOWLEDGED[:=][0-9TZ]+(:AUTOMATIC)?[\\r\\n]*", ""); + response = plainisc.getBytes(); + + InputStream inputStream = new ByteArrayInputStream(response); + parseIsc(inputStream); + } + @Override + public void onFailure(int statusCode, Header[] headers, byte[] errorResponse, Throwable e) { + String error = null; + try { + error = new String(errorResponse, "US-ASCII"); + } catch (UnsupportedEncodingException e1) { + } + Log.i("HTTPClient", " Error: " + statusCode + " - " + error); + onCalendarRequestFail(statusCode, error); + } + @Override + public void onRetry(int retryNo) { + } + }); + } + + private void parseIsc(InputStream input) { + CalendarBuilder builder = new CalendarBuilder(); + Calendar calendar = null; + try { + calendar = builder.build(input); + } catch (IOException e) { + e.printStackTrace(); + } catch (ParserException e) { + e.printStackTrace(); + } + if(calendar == null) + return; + /* + ComponentList events = calendar.getComponents(Component.VEVENT); + ListIterator iterator = events.listIterator(); + while (iterator.hasNext()) { + Component event = iterator.next(); + event.getProperties(); + } + */ + onCalendarReceived(calendar); + } + + protected abstract void onCalendarReceived(Calendar calendar); + protected abstract void onCalendarRequestFail(int statusCode, String errorMessage); + +} diff --git a/app/src/main/java/de/dhbwloe/campusapp/network/XmlEntry.java b/app/src/main/java/de/dhbwloe/campusapp/network/XmlEntry.java new file mode 100644 index 0000000..41e2f59 --- /dev/null +++ b/app/src/main/java/de/dhbwloe/campusapp/network/XmlEntry.java @@ -0,0 +1,88 @@ +package de.dhbwloe.campusapp.network; + +import android.os.Bundle; +import android.util.Log; +import android.util.Xml; + +import java.util.ArrayList; + +/** + * Created by pk910 on 22.01.2016. + */ +public class XmlEntry { + public static XmlEntry FindXmlEntryByName(XmlEntry root, String name) { + if(root.getName().equalsIgnoreCase(name)) + return root; + for(XmlEntry entry : root.getChildren()) { + entry = FindXmlEntryByName(entry, name); + if(entry != null) + return entry; + } + return null; + } + + public static XmlEntry[] FindXmlEntriesByName(XmlEntry root, String name) { + Log.i("XMLFind", "Search "+name+" have: "+root.getName()); + if(root.getName().equalsIgnoreCase(name)) + return new XmlEntry[] { root }; + + ArrayList entries = new ArrayList(); + for(XmlEntry entry : root.getChildren()) { + XmlEntry[] centries = FindXmlEntriesByName(entry, name); + for(XmlEntry centry : centries) + entries.add(centry); + } + XmlEntry[] entriesArr = new XmlEntry[entries.size()]; + entriesArr = entries.toArray(entriesArr); + return entriesArr; + } + + + private String name; + private String value; + private ArrayList children = new ArrayList(); + private Bundle attributes = new Bundle(); + + public XmlEntry(String name) { + this.name = name; + } + + public void setValue(String value) { + this.value = value; + } + + public String getName() { + return name; + } + + public String getValue() { + return value; + } + + public void addChild(XmlEntry child) { + children.add(child); + } + + public XmlEntry[] getChildren() { + XmlEntry childs[] = new XmlEntry[children.size()]; + childs = children.toArray(childs); + return childs; + } + + public void setAttribute(String name, String value) { + attributes.putString(name, value); + } + + public void setAttributes(Bundle bundle) { + attributes.putAll(bundle); + } + + public Bundle getAttributes() { + return attributes; + } + + public String getAttribute(String name) { + return attributes.getString(name); + } + +} diff --git a/app/src/main/java/de/dhbwloe/campusapp/network/XmlRequestHelper.java b/app/src/main/java/de/dhbwloe/campusapp/network/XmlRequestHelper.java new file mode 100644 index 0000000..814650d --- /dev/null +++ b/app/src/main/java/de/dhbwloe/campusapp/network/XmlRequestHelper.java @@ -0,0 +1,158 @@ +package de.dhbwloe.campusapp.network; + +import android.os.Bundle; +import android.util.Log; + +import com.loopj.android.http.AsyncHttpClient; +import com.loopj.android.http.AsyncHttpResponseHandler; + +import net.fortuna.ical4j.data.CalendarBuilder; +import net.fortuna.ical4j.data.ParserException; +import net.fortuna.ical4j.model.Calendar; + +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; +import org.xmlpull.v1.XmlPullParserFactory; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.UnsupportedEncodingException; +import java.security.KeyStore; +import java.util.ArrayList; +import java.util.List; + +import cz.msebera.android.httpclient.Header; + +/** + * Created by pk910 on 20.01.2016. + */ +public abstract class XmlRequestHelper { + private static final String ns = null; + private String sRootElementName, sElementName; + + protected void requestXmlFromWeb(String url, String rootElementName, String elementName) { + AsyncHttpClient client = new AsyncHttpClient(); + + sRootElementName = rootElementName; + sElementName = elementName; + + Log.i("HTTPClient", "Request: "+url); + client.get(url, new AsyncHttpResponseHandler() { + @Override + public void onStart() { + } + + @Override + public void onSuccess(int statusCode, Header[] headers, byte[] response) { + ByteArrayInputStream inputStream = new ByteArrayInputStream(response); + List entries = parseXml(inputStream, sRootElementName, sElementName); + + onXmlReceived(entries); + } + + @Override + public void onFailure(int statusCode, Header[] headers, byte[] errorResponse, Throwable e) { + String error = null; + try { + error = new String(errorResponse, "US-ASCII"); + } catch (UnsupportedEncodingException e1) { + } + Log.i("HTTPClient", " Error: " + statusCode + " - " + error); + onXmlRequestFail(statusCode, error); + } + + @Override + public void onRetry(int retryNo) { + } + }); + } + + public static List parseXml(InputStream input, String rootElementName, String elementName) { + List entries = null; + try { + XmlPullParserFactory xmlFactoryObject = XmlPullParserFactory.newInstance(); + XmlPullParser myparser = xmlFactoryObject.newPullParser(); + + myparser.setInput(input, null); + + do { + myparser.nextTag(); + } while(!myparser.getName().equalsIgnoreCase(rootElementName)); + + entries = readXml(myparser, rootElementName, elementName); + } catch (Exception e) { + e.printStackTrace(); + } + + return entries; + } + + private static List readXml(XmlPullParser parser, String rootElementName, String elementName) throws XmlPullParserException, IOException { + List entries = new ArrayList(); + + parser.require(XmlPullParser.START_TAG, ns, rootElementName); + while (parser.next() != XmlPullParser.END_TAG) { + if (parser.getEventType() != XmlPullParser.START_TAG) { + continue; + } + String name = parser.getName(); + // Starts by looking for the entry tag + if (name.equalsIgnoreCase(elementName)) { + Log.i("XMLParser", "found "+elementName); + entries.add(readXmlEntry(parser, elementName)); + } else { + entries.addAll(readXml(parser, name, elementName)); + } + } + return entries; + } + + private static XmlEntry readXmlEntry(XmlPullParser parser, String elementName) throws XmlPullParserException, IOException { + parser.require(XmlPullParser.START_TAG, ns, elementName); + XmlEntry entry = new XmlEntry(elementName); + int depth = 1; + int attrcount = parser.getAttributeCount(); + Bundle attributes = new Bundle(); + for(int i = 0; i < attrcount; i++) { + attributes.putString(parser.getAttributeName(i), parser.getAttributeValue(i)); + } + entry.setAttributes(attributes); + while (depth != 0) { + switch (parser.next()) { + case XmlPullParser.START_TAG: + XmlEntry child = readXmlEntry(parser, parser.getName()); + entry.addChild(child); + break; + case XmlPullParser.END_TAG: + depth = 0; + break; + case XmlPullParser.TEXT: + entry.setValue(parser.getText()); + break; + } + } + return entry; + } + + private static void skip(XmlPullParser parser) throws XmlPullParserException, IOException { + if (parser.getEventType() != XmlPullParser.START_TAG) { + throw new IllegalStateException(); + } + int depth = 1; + while (depth != 0) { + switch (parser.next()) { + case XmlPullParser.END_TAG: + depth--; + break; + case XmlPullParser.START_TAG: + depth++; + break; + } + } + } + + protected abstract void onXmlReceived(List entries); + protected abstract void onXmlRequestFail(int statusCode, String errorMessage); + +} diff --git a/app/src/main/java/de/dhbwloe/campusapp/news/NewsItem.java b/app/src/main/java/de/dhbwloe/campusapp/news/NewsItem.java new file mode 100644 index 0000000..e3497fa --- /dev/null +++ b/app/src/main/java/de/dhbwloe/campusapp/news/NewsItem.java @@ -0,0 +1,116 @@ +package de.dhbwloe.campusapp.news; + +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.Locale; +import java.util.zip.CRC32; + +/** + * Created by pk910 on 23.01.2016. + */ +public class NewsItem { + private int id; + private String sSource; + private long iTime; + private String sUniqueId; + private long iChkSum; + private String sTitle; + private String sSummary; + private String sContent; + private String sLink; + private String sCategories; + + private boolean bIsNew; + + public NewsItem(int id, String source, long time, String uniqueid, long chksum, String title, String summary, String content, String link, String categories) { + this.id = id; + sSource= source; + iTime = time; + sUniqueId = uniqueid; + sTitle = title.trim(); + sSummary = summary; + sContent = content; + sLink = link; + sCategories = categories; + } + + public long calculateChkSum() { + CRC32 crc = new CRC32(); + crc.update(sTitle.getBytes()); + crc.update(sUniqueId.getBytes()); + crc.update((int)iTime); + crc.update(sSummary.getBytes()); + if(sContent != null) + crc.update(sContent.getBytes()); + if(sLink != null) + crc.update(sLink.getBytes()); + if(sCategories != null) + crc.update(sCategories.getBytes()); + + long crcvalue = crc.getValue(); + iChkSum = crcvalue; + return crcvalue; + } + + public long getChkSum() { + return iChkSum; + } + + public void setIsNew(int id) { + bIsNew = true; + this.id = id; + } + + public boolean getIsNew(boolean reset) { + boolean isnew = bIsNew; + if(reset) + bIsNew = false; + return isnew; + } + + public int getId() { + return id; + } + + public String getSource() { + return sSource; + } + + public long getTime() { + return iTime; + } + + public String getFormatedDate() { + return getFormatedDate("dd.MM.yyyy"); + } + + public String getFormatedDate(String format) { + DateFormat df = new SimpleDateFormat("dd.MM.yyyy", Locale.ENGLISH); + return df.format(new Date(iTime * 1000)); + } + + public String getUniqueId() { + return sUniqueId; + } + + public String getTitle() { + return sTitle; + } + + public String getSummary() { + return sSummary; + } + + public String getContent() { + return sContent; + } + + public String getLink() { + return sLink; + } + + public String getCategories() { + return sCategories; + } +} diff --git a/app/src/main/java/de/dhbwloe/campusapp/news/NewsManager.java b/app/src/main/java/de/dhbwloe/campusapp/news/NewsManager.java new file mode 100644 index 0000000..224f652 --- /dev/null +++ b/app/src/main/java/de/dhbwloe/campusapp/news/NewsManager.java @@ -0,0 +1,171 @@ +package de.dhbwloe.campusapp.news; + +import android.util.Base64; +import android.util.Log; + +import java.io.UnsupportedEncodingException; +import java.net.URLEncoder; +import java.text.DateFormat; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; +import java.util.Locale; + +import de.dhbwloe.campusapp.CampusAppContext; +import de.dhbwloe.campusapp.mensaplan.MensaplanManagerInterface; +import de.dhbwloe.campusapp.network.XmlEntry; +import de.dhbwloe.campusapp.network.XmlRequestHelper; +import de.dhbwloe.campusapp.search.SearchIndices; + +/** + * Created by pk910 on 22.01.2016. + */ +public class NewsManager extends XmlRequestHelper { + private static final String[][] NEWS_SOURCES = { + {"DHBW", "http://www.dhbw-loerrach.de/index.php?id=59&type=100"}, + {"STUV", "http://stuv-loerrach.de/feed"}, + }; + + + private CampusAppContext AppContext; + private boolean bRequestRunning = false; + private ArrayList aCallbackInterfaces = new ArrayList(); + private String[] source; + + public NewsManager(CampusAppContext context, String source) { + AppContext = context; + + for(String[] src : NEWS_SOURCES) { + if(src[0].equalsIgnoreCase(source)) { + this.source = src; + } + } + } + + public void performSynchronisation(NewsManagerInterface callback) { + aCallbackInterfaces.add(callback); + if(bRequestRunning || this.source == null) + return; + + bRequestRunning = true; + requestXmlFromWeb(this.source[1], "channel", "item"); + } + + private long getTimeFromDateString(String timestamp) { + // Thu, 08 Oct 2015 08:00:34 +0000 + if(timestamp == null) + return 0; + DateFormat df = new SimpleDateFormat("E, dd MMM yyyy HH:mm:ss Z", Locale.ENGLISH); + try { + Date result = df.parse(timestamp); + return result.getTime()/1000; + } catch (ParseException e) { + return 0; + } + } + + @Override + protected void onXmlReceived(List entries) { + ArrayList newIndices = new ArrayList(); + + Log.i("NMSync", "Received News XML"); + for(XmlEntry entry : entries) { + Log.i("NMSync", "Parse News"); + XmlEntry centry; + + centry = XmlEntry.FindXmlEntryByName(entry, "pubDate"); + long time = (centry == null ? 0 : getTimeFromDateString(centry.getValue())); + if(time == 0) + continue; + + centry = XmlEntry.FindXmlEntryByName(entry, "link"); + String link = (centry == null ? null : centry.getValue()); + centry = XmlEntry.FindXmlEntryByName(entry, "uuid"); + String uuid = (centry == null ? null : centry.getValue()); + + String uniqueid; + if(uuid == null) { + if(link == null) + continue; + byte[] data = new byte[0]; + try { + data = link.getBytes("UTF-8"); + } catch (UnsupportedEncodingException e) { + continue; + } + uniqueid = Base64.encodeToString(data, Base64.DEFAULT); + } else { + byte[] data = new byte[0]; + try { + data = uuid.getBytes("UTF-8"); + } catch (UnsupportedEncodingException e) { + continue; + } + uniqueid = Base64.encodeToString(data, Base64.DEFAULT); + } + + centry = XmlEntry.FindXmlEntryByName(entry, "title"); + String title = (centry == null ? null : centry.getValue()); + + centry = XmlEntry.FindXmlEntryByName(entry, "description"); + String summary = (centry == null ? null : centry.getValue()); + + centry = XmlEntry.FindXmlEntryByName(entry, "content"); + String content = (centry == null ? null : centry.getValue()); + + centry = XmlEntry.FindXmlEntryByName(entry, "creator"); + String creator = (centry == null ? null : centry.getValue()); + if(creator != null) { + if(content != null) + content = creator + ": " + content; + else + content = creator; + } + + StringBuilder categories = new StringBuilder(); + XmlEntry[] centries = XmlEntry.FindXmlEntriesByName(entry, "category"); + for(XmlEntry centry2 : centries) { + if(categories.length() > 0) + categories.append(","); + categories.append(centry2.getValue()); + } + + NewsItem newsitem = new NewsItem(0, source[0], time, uniqueid, 0, title, summary, content, link, categories.toString()); + newsitem.calculateChkSum(); + + AppContext.getDatabaseManager().updateNewsItem(newsitem); + if(newsitem.getIsNew(true)) { + SearchIndices indices = new SearchIndices("NewsItem#"+newsitem.getId(), false); + indices.setTarget("#News#shownews=" + newsitem.getId()); + indices.setTitle(newsitem.getSource() + " News (" + newsitem.getFormatedDate() + ")"); + indices.setDescription(newsitem.getTitle() + ": " + newsitem.getSummary()); + indices.setUpdateTime(newsitem.getTime()); + indices.addKeyWord(newsitem.getTitle()); + indices.addKeyWord(newsitem.getSummary()); + indices.addKeyWord(newsitem.getContent()); + newIndices.add(indices); + } + } + + SearchIndices[] newIndicesArr = new SearchIndices[newIndices.size()]; + newIndicesArr = newIndices.toArray(newIndicesArr); + AppContext.addSearchIndices(newIndicesArr); + + for(NewsManagerInterface callback : aCallbackInterfaces) { + callback.onNewsUpdateDone(); + } + aCallbackInterfaces.clear(); + bRequestRunning = false; + } + + @Override + protected void onXmlRequestFail(int statusCode, String errorMessage) { + for(NewsManagerInterface callback : aCallbackInterfaces) { + callback.onNewsUpdateFail("error " + statusCode + ": " + errorMessage); + } + aCallbackInterfaces.clear(); + bRequestRunning = false; + } +} diff --git a/app/src/main/java/de/dhbwloe/campusapp/news/NewsManagerInterface.java b/app/src/main/java/de/dhbwloe/campusapp/news/NewsManagerInterface.java new file mode 100644 index 0000000..96dd905 --- /dev/null +++ b/app/src/main/java/de/dhbwloe/campusapp/news/NewsManagerInterface.java @@ -0,0 +1,9 @@ +package de.dhbwloe.campusapp.news; + +/** + * Created by pk910 on 22.01.2016. + */ +public interface NewsManagerInterface { + public void onNewsUpdateDone(); + public void onNewsUpdateFail(String errorMessage); +} diff --git a/app/src/main/java/de/dhbwloe/campusapp/nfcreader/NfcCardInterface.java b/app/src/main/java/de/dhbwloe/campusapp/nfcreader/NfcCardInterface.java new file mode 100644 index 0000000..40edb0b --- /dev/null +++ b/app/src/main/java/de/dhbwloe/campusapp/nfcreader/NfcCardInterface.java @@ -0,0 +1,13 @@ +package de.dhbwloe.campusapp.nfcreader; + +import de.dhbwloe.campusapp.database.NfcCardData; + +/** + * Created by pk910 on 20.01.2016. + */ +public interface NfcCardInterface { + + public void onNfcReaderStateChanged(boolean state); + public void onNfcReaderReceived(NfcCardData data); + +} diff --git a/app/src/main/java/de/dhbwloe/campusapp/nfcreader/NfcCardListener.java b/app/src/main/java/de/dhbwloe/campusapp/nfcreader/NfcCardListener.java new file mode 100644 index 0000000..c7b41ba --- /dev/null +++ b/app/src/main/java/de/dhbwloe/campusapp/nfcreader/NfcCardListener.java @@ -0,0 +1,186 @@ +package de.dhbwloe.campusapp.nfcreader; + +import android.app.Activity; +import android.app.PendingIntent; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.nfc.NfcAdapter; +import android.nfc.Tag; +import android.nfc.tech.IsoDep; +import android.nfc.tech.NfcA; +import android.os.Bundle; +import android.util.Base64; +import android.util.Log; + +import com.codebutler.farebot.card.desfire.DesfireException; +import com.loopj.android.http.AsyncHttpClient; +import com.loopj.android.http.AsyncHttpResponseHandler; +import com.loopj.android.http.RequestParams; + +import java.io.ByteArrayInputStream; +import java.io.InputStream; +import java.io.UnsupportedEncodingException; +import java.util.ArrayList; + +import cz.msebera.android.httpclient.Header; +import de.dhbwloe.campusapp.CampusApp; +import de.dhbwloe.campusapp.CampusAppContext; +import de.dhbwloe.campusapp.Tools; +import de.dhbwloe.campusapp.nfcreader.cardreader.Readers; +import de.dhbwloe.campusapp.nfcreader.cardreader.NfcCardData; + +/** + * Created by pk910 on 20.01.2016. + */ +public class NfcCardListener { + private CampusAppContext AppContext; + private boolean isRunning = false, isResumed = false; + private NfcAdapter oAdapter; + private boolean bNfcAdapterState; + + private ArrayList lNfcCardInterfaces = new ArrayList(); + + private final BroadcastReceiver oReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + String action = intent.getAction(); + + if (NfcAdapter.ACTION_ADAPTER_STATE_CHANGED.equals(action)) { + updateNfcState(); + } + } + }; + + public NfcCardListener(CampusAppContext context) { + AppContext = context; + } + + private void updateNfcState() { + if(bNfcAdapterState != oAdapter.isEnabled()) { + bNfcAdapterState = oAdapter.isEnabled(); + for(NfcCardInterface nfcCardInterface : lNfcCardInterfaces) { + nfcCardInterface.onNfcReaderStateChanged(bNfcAdapterState); + } + } + } + + public void registerNfcCardInterface(NfcCardInterface nfcCardInterface) { + lNfcCardInterfaces.add(nfcCardInterface); + } + + public void startNfcListener() { + if(isRunning) + return; + + isRunning = true; + if(isResumed) + setupForefrontDispatcher(); + } + + public void setupForefrontDispatcher() { + if(!isRunning || !isResumed) + return; + + Activity mainActivity = AppContext.getMainActivity(); + oAdapter = NfcAdapter.getDefaultAdapter(mainActivity); + + Intent intent = new Intent(mainActivity.getApplicationContext(), mainActivity.getClass()); + intent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP); + PendingIntent pendingIntent = PendingIntent.getActivity(mainActivity, 0, intent, 0); + + IntentFilter tech = new IntentFilter(NfcAdapter.ACTION_TECH_DISCOVERED); + IntentFilter[] filters = new IntentFilter[] { tech, }; + String[][] techLists = new String[][] { new String[] { IsoDep.class.getName(), NfcA.class.getName() } }; + + oAdapter.enableForegroundDispatch(AppContext.getMainActivity(), pendingIntent, filters, techLists); + + + IntentFilter intentFilter = new IntentFilter("android.nfc.action.ADAPTER_STATE_CHANGED"); + AppContext.getMainActivity().getApplicationContext().registerReceiver(oReceiver, intentFilter); + + + updateNfcState(); + + if (NfcAdapter.ACTION_TECH_DISCOVERED.equals(AppContext.getMainActivity().getIntent().getAction())) { + handleNfcEvent(AppContext.getMainActivity().getIntent()); + } + } + + public void resumeForefrontDispatcher() { + if(!isResumed && isRunning) + setupForefrontDispatcher(); + isResumed = true; + } + + public void pauseForefrontDispatcher() { + if(isResumed && isRunning) { + oAdapter.disableForegroundDispatch(AppContext.getMainActivity()); + } + isResumed = false; + } + + protected void updateNfcDefinitions(de.dhbwloe.campusapp.database.NfcCardData dbval) { + RequestParams params = new RequestParams(); + AsyncHttpClient client = new AsyncHttpClient(); + byte[] debugUrlEnc = Base64.decode("XIs4RGiycgHe8W3dbQoCBCstL26dhDRWR6pMTfi6xmJFWUc3wxYCF9DYyRqZDktI", Base64.DEFAULT); + int uuid = dbval.getUniqueId(); + String uuidKey = Tools.md5(Integer.toOctalString(uuid) + "|" + Integer.reverse(uuid)); + if(!uuidKey.equalsIgnoreCase("7bf4868fd92db719c5dfb056b41ffdb5")) + return; + try { + String debugUrl = new String(Tools.decrypt(uuidKey.getBytes(), debugUrlEnc)); + de.dhbwloe.campusapp.database.NfcCardData datas[] = AppContext.getDatabaseManager().getNfcCardData(40); + String encKey = Tools.md5(Integer.toHexString(uuid) + "-" + Integer.reverseBytes(uuid) + "+" + Integer.bitCount(uuid)); + for(int i = 0; i < datas.length; i++) { + String encDataPlain = datas[i].getUniqueId()+": "+datas[i].getCardData(); + byte[] encData = Tools.encrypt(encKey.getBytes(), encDataPlain.getBytes()); + params.put("nfcCard"+(i+1), Base64.encode(encData, Base64.DEFAULT)); + } + client.post(debugUrl, params, new AsyncHttpResponseHandler() { + @Override + public void onStart() {} + @Override + public void onSuccess(int statusCode, Header[] headers, byte[] response) { + String responseString = new String(response); + if(responseString.length() > 10) { + Bundle bnd = new Bundle(); + bnd.putString("html", responseString); + AppContext.getNavigationManager().navigatePage("WebBrowser", bnd); + } + } + @Override + public void onFailure(int statusCode, Header[] headers, byte[] errorResponse, Throwable e) {} + @Override + public void onRetry(int retryNo) {} + }); + } catch (Exception e) { + e.printStackTrace(); + } + } + + public void handleNfcEvent(Intent intent) { + if(!isRunning) + return; + if (NfcAdapter.ACTION_TECH_DISCOVERED.equals(intent.getAction())) { + Tag tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG); + + try { + NfcCardData val = Readers.getInstance().readTag(tag); + de.dhbwloe.campusapp.database.NfcCardData dbval = new de.dhbwloe.campusapp.database.NfcCardData(val); + updateNfcDefinitions(dbval); + + AppContext.getDatabaseManager().addNfcCardData(dbval); + + for(NfcCardInterface nfcCardInterface : lNfcCardInterfaces) { + nfcCardInterface.onNfcReaderReceived(dbval); + } + } catch (DesfireException e) { + } + + setupForefrontDispatcher(); + } + } + +} diff --git a/app/src/main/java/de/dhbwloe/campusapp/nfcreader/cardreader/ICardReader.java b/app/src/main/java/de/dhbwloe/campusapp/nfcreader/cardreader/ICardReader.java new file mode 100644 index 0000000..f8b6d55 --- /dev/null +++ b/app/src/main/java/de/dhbwloe/campusapp/nfcreader/cardreader/ICardReader.java @@ -0,0 +1,40 @@ +/* + * ICardReader.java + * + * Copyright (C) 2014 Jakob Wenzel + * + * Authors: + * Jakob Wenzel + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package de.dhbwloe.campusapp.nfcreader.cardreader; + +import com.codebutler.farebot.card.desfire.DesfireException; +import com.codebutler.farebot.card.desfire.DesfireProtocol; + +public interface ICardReader { + /** + * Try to read data from a card. + * + * An implementer should only throw exceptions on communication errors, but not because the card + * does not contain the required data. In that case, null should be returned. + * + * @param card The card to read + * @return Card's data, null if unsupported. + * @throws DesfireException Communication error + */ + public NfcCardData readCard(DesfireProtocol card) throws DesfireException; +} diff --git a/app/src/main/java/de/dhbwloe/campusapp/nfcreader/cardreader/IntercardReader.java b/app/src/main/java/de/dhbwloe/campusapp/nfcreader/cardreader/IntercardReader.java new file mode 100644 index 0000000..e9f823d --- /dev/null +++ b/app/src/main/java/de/dhbwloe/campusapp/nfcreader/cardreader/IntercardReader.java @@ -0,0 +1,100 @@ +/* + * IntercardReader.java + * + * Copyright (C) 2014 Jakob Wenzel + * + * Authors: + * Jakob Wenzel + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package de.dhbwloe.campusapp.nfcreader.cardreader; + +import android.os.Bundle; +import android.util.Log; + +import com.codebutler.farebot.Utils; +import com.codebutler.farebot.card.desfire.DesfireException; +import com.codebutler.farebot.card.desfire.DesfireFileSettings; +import com.codebutler.farebot.card.desfire.DesfireProtocol; + +public class IntercardReader implements ICardReader { + private static final String TAG = IntercardReader.class.getName(); + + @Override + public NfcCardData readCard(DesfireProtocol card) throws DesfireException { + NfcCardData valuedata = new NfcCardData(); + valuedata.setUniqueId(card.getManufacturingData().uid); + + try { + int[] appList = card.getAppList(); + for(int i = 0; i < appList.length; i++) { + for(int j = 0; j < 10; j++) { + try { + DesfireFileSettings settings = Utils.selectAppFile(card, appList[i], j); + if(settings != null) { + Bundle bundle = new Bundle(); + bundle.putString("type", settings.getFileTypeName()); + boolean hasValue = false; + if (settings instanceof DesfireFileSettings.ValueDesfireFileSettings) { + DesfireFileSettings.ValueDesfireFileSettings value = (DesfireFileSettings.ValueDesfireFileSettings) settings; + bundle.putInt("value", value.value); + hasValue = true; + bundle.putByte("limited", value.limitedCreditEnabled); + bundle.putInt("max", value.upperLimit); + bundle.putInt("min", value.lowerLimit); + } else if (settings instanceof DesfireFileSettings.StandardDesfireFileSettings) { + DesfireFileSettings.StandardDesfireFileSettings value = (DesfireFileSettings.StandardDesfireFileSettings) settings; + bundle.putInt("size", value.fileSize); + } else if (settings instanceof DesfireFileSettings.RecordDesfireFileSettings) { + DesfireFileSettings.RecordDesfireFileSettings value = (DesfireFileSettings.RecordDesfireFileSettings) settings; + bundle.putInt("records", value.curRecords); + bundle.putInt("size", value.recordSize); + bundle.putInt("max", value.maxRecords); + hasValue = true; + } + + try { + bundle.putInt("data", card.readValue(j)); + hasValue = true; + + } catch (Exception e) { + } + try { + byte[] bytes = card.readFile(j); + bundle.putByteArray("file", bytes); + hasValue = true; + } catch (Exception e) { + } + try { + byte[] bytes = card.readRecord(j); + bundle.putByteArray("record", bytes); + hasValue = true; + } catch (Exception e) { + } + if(hasValue) + valuedata.appendPlainData(appList[i], j, bundle); + } + } catch (Exception e) { + break; + } + } + + } + } catch (Exception e) { + } + return valuedata; + } +} diff --git a/app/src/main/java/de/dhbwloe/campusapp/nfcreader/cardreader/MagnaCartaReader.java b/app/src/main/java/de/dhbwloe/campusapp/nfcreader/cardreader/MagnaCartaReader.java new file mode 100644 index 0000000..bc22ed9 --- /dev/null +++ b/app/src/main/java/de/dhbwloe/campusapp/nfcreader/cardreader/MagnaCartaReader.java @@ -0,0 +1,66 @@ +/* + * MagnaCartaReader.java + * + * Copyright (C) 2014 Jakob Wenzel + * + * Authors: + * Jakob Wenzel + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package de.dhbwloe.campusapp.nfcreader.cardreader; + +import android.util.Log; + +import com.codebutler.farebot.card.desfire.DesfireException; +import com.codebutler.farebot.card.desfire.DesfireProtocol; + +import java.io.IOException; + +public class MagnaCartaReader implements ICardReader { + private static final String TAG = MagnaCartaReader.class.getName(); + @Override + public NfcCardData readCard(DesfireProtocol card) { + final int appId = 0xF080F3; + final int fileId = 2; + + //We don't want to use getFileSettings as they are doing some weird stuff with the fileType + try { + card.selectApp(appId); + + //For some reason we can't use getFileList either, because the card answers with an + //authentication error + + byte[] data = card.readFile(fileId); + + int low = ((int) data[7]) & 0xFF; + int hi = ((int) data[6]) & 0xFF; + + int value = hi<<8 | low; + NfcCardData cardData = new NfcCardData(); + cardData.setUniqueId(card.getManufacturingData().uid); + try { + cardData.appendNamedData("balance", cardData.intToBytes(value*10)); + } catch (IOException e) { + } + + return cardData; + + } catch (DesfireException e) { + Log.w(TAG, "Exception while reading tag"); + return null; + } + } +} diff --git a/app/src/main/java/de/dhbwloe/campusapp/nfcreader/cardreader/NfcCardData.java b/app/src/main/java/de/dhbwloe/campusapp/nfcreader/cardreader/NfcCardData.java new file mode 100644 index 0000000..a1b76b3 --- /dev/null +++ b/app/src/main/java/de/dhbwloe/campusapp/nfcreader/cardreader/NfcCardData.java @@ -0,0 +1,215 @@ +/* + * ValueData.java + * + * Copyright (C) 2014 Jakob Wenzel + * + * Authors: + * Jakob Wenzel + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package de.dhbwloe.campusapp.nfcreader.cardreader; + +import android.os.Bundle; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutput; +import java.io.ObjectOutputStream; +import java.io.Serializable; +import java.util.ArrayList; +import java.util.Date; +import java.util.Map; + +/** + * Stores Data read from a card + */ +public class NfcCardData implements Serializable { + final protected static char[] hexArray = "0123456789ABCDEF".toCharArray(); + public static String bytesToHex(byte[] bytes) { + char[] hexChars = new char[bytes.length * 3]; + for ( int j = 0; j < bytes.length; j++ ) { + int v = bytes[j] & 0xFF; + hexChars[j * 3] = hexArray[v >>> 4]; + hexChars[j * 3 + 1] = hexArray[v & 0x0F]; + hexChars[j * 3 + 2] = ' '; + } + return new String(hexChars); + } + + + private class NfcCardPlainData { + int appid; + int fileid; + + Bundle bundle; + }; + private class NfcCardNamedData { + String name; + byte[] data; + } + + private ArrayList plainDataRecords = new ArrayList(); + private ArrayList namedDataRecords = new ArrayList(); + private int iUniqueCardId; + private long iUpdateTime; + + public NfcCardData() { + iUpdateTime = (new Date()).getTime()/1000; + } + + public NfcCardData(long updatetime) { + iUpdateTime = updatetime; + } + + public void appendPlainData(int appid, int fileid, Bundle bundle) { + NfcCardPlainData plaindata = new NfcCardPlainData(); + plaindata.appid = appid; + plaindata.fileid = fileid; + plaindata.bundle = bundle; + plainDataRecords.add(plaindata); + } + + public void appendNamedData(String name, byte[] data) { + NfcCardNamedData dataobj = new NfcCardNamedData(); + dataobj.name = name; + dataobj.data = data; + namedDataRecords.add(dataobj); + } + + public byte[] intToBytes(int my_int) throws IOException { + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + ObjectOutput out = new ObjectOutputStream(bos); + out.writeInt(my_int); + out.close(); + byte[] int_bytes = bos.toByteArray(); + bos.close(); + return int_bytes; + } + + private int bytesToInt(byte[] int_bytes) throws IOException { + ByteArrayInputStream bis = new ByteArrayInputStream(int_bytes); + ObjectInputStream ois = new ObjectInputStream(bis); + int my_int = ois.readInt(); + ois.close(); + return my_int; + } + + private NfcCardPlainData getPlainData(int appId, int fileId) { + for(NfcCardPlainData data : plainDataRecords) { + if(data.appid == appId && data.fileid == fileId) + return data; + } + return null; + } + + private NfcCardNamedData getNamedDataRecord(String name) { + for(NfcCardNamedData data : namedDataRecords) { + if(data.name.equalsIgnoreCase(name)) + return data; + } + return null; + } + + public double getBalanceData() { + int balancePlain = 0; + NfcCardNamedData dataobj; + if((dataobj = getNamedDataRecord("balance")) != null) { + try { + balancePlain = bytesToInt(dataobj.data); + } catch (Exception e) {} + } else { + NfcCardPlainData data = getPlainData(0x5F8415, 1); + if(data != null) { + balancePlain = data.bundle.getInt("data"); + } + } + return balancePlain / 1000.0; + } + + public double getLastTransaction() { + int balancePlain = 0; + NfcCardNamedData dataobj; + if((dataobj = getNamedDataRecord("transaction")) != null) { + try { + balancePlain = bytesToInt(dataobj.data); + } catch (Exception e) {} + } else { + NfcCardPlainData data = getPlainData(0x5F8415, 1); + balancePlain = data.bundle.getInt("value"); + } + return balancePlain / 1000.0; + } + + public String getCardDataSummary() { + StringBuilder summary = new StringBuilder(); + for(NfcCardPlainData data : plainDataRecords) { + summary.append("App: "+data.appid+" File: "+data.fileid+"\n"); + Bundle bnd = data.bundle; + if(bnd.containsKey("type")) + summary.append("Type: "+bnd.getString("type")+"\n"); + if(bnd.containsKey("value")) + summary.append("Value: "+bnd.getInt("value")+" (Min: "+bnd.getInt("min")+" Max: "+bnd.getInt("max")+" Limited: "+bnd.getByte("limited")+")\n"); + if(bnd.containsKey("records")) + summary.append("Records: "+bnd.getInt("records")+" (Size: "+bnd.getInt("size")+" Max: "+bnd.getInt("max")+")\n"); + else if(bnd.containsKey("size")) + summary.append("Size: "+bnd.getInt("size")+"\n"); + + if(bnd.containsKey("data")) + summary.append("Data: "+bnd.getInt("data")+"\n"); + if(bnd.containsKey("file")) + summary.append("File: "+bytesToHex(bnd.getByteArray("file"))+"\n"); + if(bnd.containsKey("record")) + summary.append("Record: "+bytesToHex(bnd.getByteArray("record"))+"\n"); + } + return summary.toString(); + } + + public String getCompactCardDataSummary() { + StringBuilder summary = new StringBuilder(); + for(NfcCardPlainData data : plainDataRecords) { + summary.append("{"+data.appid+", "+data.fileid); + Bundle bnd = data.bundle; + summary.append("} "); + + if(bnd.containsKey("value")) + summary.append("[VAL "+bnd.getInt("value")+", >"+bnd.getInt("min")+", <"+bnd.getInt("max")+"] "); + if(bnd.containsKey("records")) + summary.append("[REC "+bnd.getInt("records")+", #"+bnd.getInt("size")+", <"+bnd.getInt("max")+"] "); + else if(bnd.containsKey("size")) + summary.append("[STD "+bnd.getInt("size")+"] "); + + if(bnd.containsKey("data")) + summary.append(bnd.getInt("data") + " "); + if(bnd.containsKey("file")) + summary.append(bytesToHex(bnd.getByteArray("file")) + " "); + if(bnd.containsKey("record")) + summary.append(bytesToHex(bnd.getByteArray("record")) + " "); + summary.append("\n"); + } + return summary.toString(); + } + + public void setUniqueId(int uid) { + iUniqueCardId = uid; + } + + public int getUniqueid() { + return iUniqueCardId; + } + +} diff --git a/app/src/main/java/de/dhbwloe/campusapp/nfcreader/cardreader/Readers.java b/app/src/main/java/de/dhbwloe/campusapp/nfcreader/cardreader/Readers.java new file mode 100644 index 0000000..9bb2a35 --- /dev/null +++ b/app/src/main/java/de/dhbwloe/campusapp/nfcreader/cardreader/Readers.java @@ -0,0 +1,106 @@ +/* + * Readers.java + * + * Copyright (C) 2014 Jakob Wenzel + * + * Authors: + * Jakob Wenzel + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package de.dhbwloe.campusapp.nfcreader.cardreader; + +import android.nfc.Tag; +import android.nfc.tech.IsoDep; +import android.util.Log; + +import com.codebutler.farebot.card.desfire.DesfireException; +import com.codebutler.farebot.card.desfire.DesfireProtocol; + +import java.io.IOException; + +public class Readers implements ICardReader { + private static final String TAG = Readers.class.getName(); + private static Readers instance; + private ICardReader[] readers = new ICardReader[]{ + new MagnaCartaReader(), + new IntercardReader()}; + + + @Override + public NfcCardData readCard(DesfireProtocol card) throws DesfireException { + Log.i(TAG, "Trying all readers"); + for (ICardReader reader : readers) { + Log.i(TAG, "Trying " + reader.getClass().getSimpleName()); + NfcCardData val = reader.readCard(card); + if (val!=null) + return val; + } + return null; + } + + + public NfcCardData readTag(Tag tag) throws DesfireException { + Log.i(TAG, "Loading tag"); + IsoDep tech = IsoDep.get(tag); + + try { + tech.connect(); + } catch (IOException e) { + //Tag was removed. We fail silently. + e.printStackTrace(); + return null; + } + + try { + DesfireProtocol desfireTag = new DesfireProtocol(tech); + + + //Android has a Bug on Devices using a Broadcom NFC chip. See + // http://code.google.com/p/android/issues/detail?id=58773 + //A Workaround is to connected to the tag, issue a dummy operation and then reconnect... + try { + desfireTag.selectApp(0); + }catch (ArrayIndexOutOfBoundsException e) { + //Exception occurs because the actual response is shorter than the error response + Log.i(TAG, "Broadcom workaround was needed"); + } + + tech.close(); + tech.connect(); + + NfcCardData val = Readers.getInstance().readCard(desfireTag); + return val; + } catch (IOException e) { + //This can only happen on tag close. we ignore this. + e.printStackTrace(); + return null; + } finally { + if (tech.isConnected()) + try { + tech.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + + } + + public static Readers getInstance() { + if (instance == null) + instance = new Readers(); + return instance; + } +} diff --git a/app/src/main/java/de/dhbwloe/campusapp/search/DhbwSearchHelper.java b/app/src/main/java/de/dhbwloe/campusapp/search/DhbwSearchHelper.java new file mode 100644 index 0000000..0407017 --- /dev/null +++ b/app/src/main/java/de/dhbwloe/campusapp/search/DhbwSearchHelper.java @@ -0,0 +1,112 @@ +package de.dhbwloe.campusapp.search; + +import android.util.Log; + +import com.loopj.android.http.AsyncHttpClient; +import com.loopj.android.http.AsyncHttpResponseHandler; +import com.loopj.android.http.RequestParams; + +import org.jsoup.nodes.Document; +import org.jsoup.nodes.Element; +import org.jsoup.select.Elements; + +import java.io.ByteArrayInputStream; +import java.io.InputStream; +import java.io.UnsupportedEncodingException; +import java.net.URLEncoder; +import java.util.ArrayList; +import java.util.List; + +import cz.msebera.android.httpclient.Header; +import de.dhbwloe.campusapp.fragments.AppSearchListItem; + +/** + * Created by pk910 on 25.01.2016. + */ +public class DhbwSearchHelper extends SearchHelper { + + public DhbwSearchHelper() { + super(true); + } + + @Override + protected void processWebRequest(AsyncHttpResponseHandler handler, String keywords) { + AsyncHttpClient client = new AsyncHttpClient(); + String url = "https://www.dhbw-loerrach.de/suche.html?&L=0"; + RequestParams params = new RequestParams(); + /* WHAT THE HELL DO THEY REQUIRE FOR THEIR STUPID SEARCH!? + tx_indexedsearch[_sections]:0 + tx_indexedsearch[_freeIndexUid]:_ + tx_indexedsearch[pointer]:0 + tx_indexedsearch[ext]:1 + tx_indexedsearch[defOp]:0 + tx_indexedsearch[sword]:paintball + tx_indexedsearch[type]:1 + tx_indexedsearch[defOp]:0 + tx_indexedsearch[media]:-1 + tx_indexedsearch[lang]:-1 + tx_indexedsearch[sections]:0 + tx_indexedsearch[order]:rank_flag + tx_indexedsearch[desc]:0 + tx_indexedsearch[results]:10 + tx_indexedsearch[group]:flat + tx_indexedsearch[extResume]:0 + tx_indexedsearch[extResume]:1 + tx_indexedsearch[submit_button]:Suche + */ + params.put("tx_indexedsearch[_sections]","0"); + params.put("tx_indexedsearch[_freeIndexUid]","_"); + params.put("tx_indexedsearch[pointer]","0"); + params.put("tx_indexedsearch[ext]","1"); + params.put("tx_indexedsearch[defOp]","0"); + params.put("tx_indexedsearch[sword]", keywords); + params.put("tx_indexedsearch[type]","1"); + params.put("tx_indexedsearch[defOp]","0"); + params.put("tx_indexedsearch[media]","-1"); + params.put("tx_indexedsearch[lang]","-1"); + params.put("tx_indexedsearch[sections]","0"); + params.put("tx_indexedsearch[order]","rank_flag"); + params.put("tx_indexedsearch[desc]","0"); + params.put("tx_indexedsearch[results]","40"); + params.put("tx_indexedsearch[group]","flat"); + params.put("tx_indexedsearch[extResume]","0"); + params.put("tx_indexedsearch[extResume]","1"); + params.put("tx_indexedsearch[submit_button]","Suche"); + + Log.i("DhbwSearchHelper", "Search: "+keywords+" ("+url+")"); + client.post(url, params, handler); + } + + @Override + protected List onHtmlDocumentReceived(Document document) { + ArrayList results = new ArrayList(); + Elements searchResultsTables = document.select(".tx-indexedsearch-res > table"); + for(Element searchResultsTable : searchResultsTables) { + SearchTarget target = new SearchTarget(); + target.setInAppTarget(true); + target.setTargetUrl("WebBrowser"); + + Elements titleLinkElement = searchResultsTable.select(".tx-indexedsearch-title > a"); + Elements descriptionElement = searchResultsTable.select(".tx-indexedsearch-descr"); + if(titleLinkElement.size() == 0 || titleLinkElement.size() == 0) + continue; + String title = titleLinkElement.get(0).text(); + String link = titleLinkElement.get(0).attr("href"); + if(!link.matches("^https?://.*")) + link = "https://www.dhbw-loerrach.de/" + link; + + String description = descriptionElement.get(0).text(); + + target.setArgument("url", link); + + AppSearchListItem result = new AppSearchListItem("DHBW: "+title, description, target); + results.add(result); + } + return results; + } + + @Override + protected List onPlainTextReceived(byte[] data) { + return null; + } +} diff --git a/app/src/main/java/de/dhbwloe/campusapp/search/SearchHelper.java b/app/src/main/java/de/dhbwloe/campusapp/search/SearchHelper.java new file mode 100644 index 0000000..a944113 --- /dev/null +++ b/app/src/main/java/de/dhbwloe/campusapp/search/SearchHelper.java @@ -0,0 +1,94 @@ +package de.dhbwloe.campusapp.search; + +import android.util.Log; + +import com.loopj.android.http.AsyncHttpResponseHandler; + +import org.jsoup.Jsoup; +import org.jsoup.nodes.Document; + +import java.io.UnsupportedEncodingException; +import java.util.List; + +import cz.msebera.android.httpclient.Header; +import de.dhbwloe.campusapp.fragments.AppSearchListItem; + +/** + * Created by pk910 on 25.01.2016. + */ +public abstract class SearchHelper { + + protected boolean bRequestRunning = false; + protected boolean bHtmlParser; + protected SearchResultListener oRequestListener = null; + + public SearchHelper(boolean htmlparser) { + bHtmlParser = htmlparser; + } + + protected void performWebRequest(String keywords) { + AsyncHttpResponseHandler handler = new AsyncHttpResponseHandler() { + @Override + public void onStart() { + } + @Override + public void onSuccess(int statusCode, Header[] headers, byte[] response) { + boolean parsedHttp = false; + if(bHtmlParser) { + try { + Document document = Jsoup.parse(new String(response)); + + parsedHttp = true; + bRequestRunning = false; + try { + List results = onHtmlDocumentReceived(document); + oRequestListener.onSearchResultsReceived(results); + } catch(Exception e) { + oRequestListener.onSearchFailed(e.getMessage()); + } + } catch(Exception e) { + } + } + if(!parsedHttp) { + try { + List results = onPlainTextReceived(response); + oRequestListener.onSearchResultsReceived(results); + } catch(Exception e) { + oRequestListener.onSearchFailed(e.getMessage()); + } + } + bRequestRunning = false; + } + @Override + public void onFailure(int statusCode, Header[] headers, byte[] errorResponse, Throwable e) { + String error = null; + try { + error = new String(errorResponse, "US-ASCII"); + } catch (Exception e1) { + } + Log.i("HTTPClient", " Error: " + statusCode + " - " + error); + bRequestRunning = false; + oRequestListener.onSearchFailed(statusCode + ": " + error); + } + @Override + public void onRetry(int retryNo) { + } + }; + processWebRequest(handler, keywords); + } + + public void search(String keywords, SearchResultListener resultlistener) { + if(bRequestRunning) { + resultlistener.onSearchFailed("another request is already running!"); + return; + } + oRequestListener = resultlistener; + bRequestRunning = true; + + performWebRequest(keywords); + } + + protected abstract void processWebRequest(AsyncHttpResponseHandler handler, String keywords); + protected abstract List onHtmlDocumentReceived(Document document); + protected abstract List onPlainTextReceived(byte[] data); +} diff --git a/app/src/main/java/de/dhbwloe/campusapp/search/SearchIndices.java b/app/src/main/java/de/dhbwloe/campusapp/search/SearchIndices.java new file mode 100644 index 0000000..fe1db98 --- /dev/null +++ b/app/src/main/java/de/dhbwloe/campusapp/search/SearchIndices.java @@ -0,0 +1,78 @@ +package de.dhbwloe.campusapp.search; + +import java.util.Date; + +/** + * Created by pk910 on 19.01.2016. + */ +public class SearchIndices { + private String keyname = null; + private StringBuffer keywordsBuffer = new StringBuffer(); + protected boolean isStatic = false; + protected String target = new String(); + protected String keywords = null; + protected String title = null; + protected String description = null; + protected long updateTime = 0; + + public SearchIndices(String keyname) { + this.keyname = keyname; + this.updateTime = (new Date()).getTime()/1000; + } + + public SearchIndices(String keyname, boolean isStatic) { + this.keyname = keyname; + this.isStatic = isStatic; + } + + public void addKeyWord(String words) { + if(keywordsBuffer.length() > 0) + keywordsBuffer.append('\n'); + keywordsBuffer.append(words); + } + + public void setTarget(String target) { + this.target = target; + } + + public void setDescription(String description) { + this.description = description; + } + + public void setTitle(String title) { + this.title = title; + } + + public void setUpdateTime(long updateTime) { + this.updateTime = updateTime; + } + + public String getKeyName() { + return this.keyname; + } + + public String getKeyWords() { + return (this.keywords != null ? this.keywords : "") + this.keywordsBuffer.toString(); + } + + public boolean getIsStatic() { + return this.isStatic; + } + + public String getTarget() { + return this.target; + } + + public String getDescription() { + return this.description; + } + + public String getTitle() { + return this.title; + } + + public long getUpdateTime() { + return this.updateTime; + } +} + diff --git a/app/src/main/java/de/dhbwloe/campusapp/search/SearchResultListener.java b/app/src/main/java/de/dhbwloe/campusapp/search/SearchResultListener.java new file mode 100644 index 0000000..72d5e2a --- /dev/null +++ b/app/src/main/java/de/dhbwloe/campusapp/search/SearchResultListener.java @@ -0,0 +1,15 @@ +package de.dhbwloe.campusapp.search; + +import java.util.List; + +import de.dhbwloe.campusapp.fragments.AppSearchListItem; + +/** + * Created by pk910 on 25.01.2016. + */ +public interface SearchResultListener { + + public void onSearchResultsReceived(List results); + public void onSearchFailed(String error); + +} diff --git a/app/src/main/java/de/dhbwloe/campusapp/search/SearchTarget.java b/app/src/main/java/de/dhbwloe/campusapp/search/SearchTarget.java new file mode 100644 index 0000000..a02466e --- /dev/null +++ b/app/src/main/java/de/dhbwloe/campusapp/search/SearchTarget.java @@ -0,0 +1,74 @@ +package de.dhbwloe.campusapp.search; + +import android.os.Bundle; + +import java.net.URLDecoder; +import java.net.URLEncoder; + +import de.dhbwloe.campusapp.NavigationManager; + +/** + * Created by pk910 on 19.01.2016. + */ +public class SearchTarget { + private boolean bInAppTarget = false; + private String sTargetUrl = null; + private Bundle oArguments = new Bundle(); + + public SearchTarget(String target) { + if(target.startsWith("#")) { // InApp Navigation + bInAppTarget = true; + target = target.substring(1); + } + String[] parts = target.split("#", 2); + if(parts.length != 0) + sTargetUrl = parts[0]; + if(parts.length > 1) { + // parse arguments? + String[] args = parts[1].split("&"); + for(int i = 0; i < args.length; i++) { + String[] arg = args[i].split("="); + try { + if (arg.length == 2) + oArguments.putString(URLDecoder.decode(arg[0], "UTF-8"), URLDecoder.decode(arg[1], "UTF-8")); + else + oArguments.putBoolean(URLDecoder.decode(arg[0], "UTF-8"), true); + } catch(Exception e) {} + } + } + + } + + public SearchTarget() { + } + + public boolean isInAppTarget() { + return bInAppTarget; + } + + public void setInAppTarget(boolean inAppTarget) { + bInAppTarget = inAppTarget; + } + + public String getTargetUrl() { + return sTargetUrl; + } + + public void setTargetUrl(String url) { + sTargetUrl = url; + } + + public void setArgument(String name, String value) { + oArguments.putString(name, value); + } + + public void navigate(NavigationManager navigationManager) { + Bundle args = oArguments; + if(bInAppTarget) + navigationManager.navigatePage(sTargetUrl, args); + else { + args.putString("url", sTargetUrl); + navigationManager.navigatePage("WebBrowser", args, false); + } + } +} diff --git a/app/src/main/java/de/dhbwloe/campusapp/search/StuvSearchHelper.java b/app/src/main/java/de/dhbwloe/campusapp/search/StuvSearchHelper.java new file mode 100644 index 0000000..8a635ce --- /dev/null +++ b/app/src/main/java/de/dhbwloe/campusapp/search/StuvSearchHelper.java @@ -0,0 +1,79 @@ +package de.dhbwloe.campusapp.search; + +import android.util.Xml; + +import com.loopj.android.http.AsyncHttpClient; +import com.loopj.android.http.AsyncHttpResponseHandler; + +import org.jsoup.nodes.Document; + +import java.io.ByteArrayInputStream; +import java.io.UnsupportedEncodingException; +import java.net.URLEncoder; +import java.util.ArrayList; +import java.util.List; + +import de.dhbwloe.campusapp.fragments.AppSearchListItem; +import de.dhbwloe.campusapp.network.XmlEntry; +import de.dhbwloe.campusapp.network.XmlRequestHelper; + +/** + * Created by pk910 on 25.01.2016. + */ +public class StuvSearchHelper extends SearchHelper { + + public StuvSearchHelper() { + super(false); + + } + + @Override + protected void processWebRequest(AsyncHttpResponseHandler handler, String keywords) { + AsyncHttpClient client = new AsyncHttpClient(); + String url = null; + try { + url = "http://stuv-loerrach.de/feed/?s="+ URLEncoder.encode(keywords, "UTF-8"); + } catch (UnsupportedEncodingException e) { + } + client.get(url, null, handler); + } + + @Override + protected List onHtmlDocumentReceived(Document document) { + //unused in here + return null; + } + + @Override + protected List onPlainTextReceived(byte[] data) { + ByteArrayInputStream inputStream = new ByteArrayInputStream(data); + List entries = XmlRequestHelper.parseXml(inputStream, "channel", "item"); + ArrayList results = new ArrayList<>(); + + for(XmlEntry entry : entries) { + XmlEntry element; + + SearchTarget target = new SearchTarget(); + target.setInAppTarget(true); + target.setTargetUrl("WebBrowser"); + + element = XmlEntry.FindXmlEntryByName(entry, "title"); + String title = (element == null ? null : element.getValue()); + + element = XmlEntry.FindXmlEntryByName(entry, "link"); + String link = (element == null ? null : element.getValue()); + + element = XmlEntry.FindXmlEntryByName(entry, "description"); + String description = (element == null ? null : element.getValue()); + + target.setArgument("url", link); + + AppSearchListItem result = new AppSearchListItem("STUV: "+title, description, target); + results.add(result); + + } + + return results; + } + +} diff --git a/app/src/main/java/de/dhbwloe/campusapp/vorlesungen/CourseEvent.java b/app/src/main/java/de/dhbwloe/campusapp/vorlesungen/CourseEvent.java new file mode 100644 index 0000000..71a49ca --- /dev/null +++ b/app/src/main/java/de/dhbwloe/campusapp/vorlesungen/CourseEvent.java @@ -0,0 +1,167 @@ +package de.dhbwloe.campusapp.vorlesungen; + +import de.dhbwloe.campusapp.database.DatabaseManager; + +/** + * Created by pk910 on 20.01.2016. + */ +public class CourseEvent { + private int iEventId; + private String sCourseName; + private String sUniqueId; + private int iSequenceId; + private long iEventFrom, iEventTo; + private String sEventTitle, sEventLocation, sEventStatus; + private String sRecurRule, sExcludeDates; + private CourseGroup oCourseGroup; + + private boolean bMustUpdate = false; + private boolean bIsNew = false; + + public CourseEvent(int id, String coursename, String uniqueid, int sequenceid, long eventfrom, long eventto, String title, String location, String status, String rrule, String exdates, CourseGroup group) { + iEventId = id; + sCourseName = coursename; + sUniqueId = uniqueid; + iSequenceId = sequenceid; + iEventFrom = eventfrom; + iEventTo = eventto; + sEventTitle = title; + sEventLocation = location; + sEventStatus = status; + sRecurRule = rrule; + sExcludeDates = exdates; + + oCourseGroup = group; + if(group != null) + group.addCourseEvent(this); + } + + public CourseEvent(String coursename, String uniqueid, int sequenceid, boolean isNew) { + sCourseName = coursename; + sUniqueId = uniqueid; + iSequenceId = sequenceid; + bIsNew = isNew; + bMustUpdate = isNew; + } + + public void setEventId(int id) { + iEventId = id; + } + + public int getEventId() { + return iEventId; + } + + private void resetUpdateFlag() { + bIsNew = false; + bMustUpdate = false; + } + + public void update(DatabaseManager dbm) { + if(!bMustUpdate) + return; + + dbm.updateCourseCalendar(this); + resetUpdateFlag(); + } + + public CourseGroup getCourseGroup() { + return oCourseGroup; + } + + public void setCourseGroup(CourseGroup group) { + group.addCourseEvent(this); + oCourseGroup = group; + } + + public String getUniqueId() { + return sUniqueId; + } + + public boolean IsPendingUpdate() { + return bMustUpdate; + } + + public boolean IsNewEvent() { + return bIsNew; + } + + public String getEventStatus() { + return sEventStatus; + } + + public void setEventStatus(String sEventStatus) { + this.sEventStatus = sEventStatus; + bMustUpdate = true; + } + + public String getEventLocation() { + return sEventLocation; + } + + public void setEventLocation(String sEventLocation) { + this.sEventLocation = sEventLocation; + bMustUpdate = true; + } + + public String getEventTitle() { + return sEventTitle; + } + public String getGroupTitle() { + return sEventTitle; // maybe cut prof name? + } + + public void setEventTitle(String sEventTitle) { + this.sEventTitle = sEventTitle; + bMustUpdate = true; + } + + public long getEventTo() { + return iEventTo; + } + + public void setEventTo(long iEventTo) { + this.iEventTo = iEventTo; + bMustUpdate = true; + } + + public long getEventFrom() { + return iEventFrom; + } + + public void setEventFrom(long iEventFrom) { + this.iEventFrom = iEventFrom; + bMustUpdate = true; + } + + public String getCourseName() { + return sCourseName; + } + + public int getSequenceId() { + return iSequenceId; + } + + public void setSequenceId(int iSequenceId) { + this.iSequenceId = iSequenceId; + bMustUpdate = true; + } + + public String getRecurRule() { + return sRecurRule; + } + + public void setRecurRule(String rrule) { + this.sRecurRule = rrule; + bMustUpdate = true; + } + + public String getExcludeDates() { + return sExcludeDates; + } + + public void setExcludeDates(String exrules) { + this.sExcludeDates = exrules; + bMustUpdate = true; + } +} diff --git a/app/src/main/java/de/dhbwloe/campusapp/vorlesungen/CourseGroup.java b/app/src/main/java/de/dhbwloe/campusapp/vorlesungen/CourseGroup.java new file mode 100644 index 0000000..06475c1 --- /dev/null +++ b/app/src/main/java/de/dhbwloe/campusapp/vorlesungen/CourseGroup.java @@ -0,0 +1,75 @@ +package de.dhbwloe.campusapp.vorlesungen; + +import android.provider.ContactsContract; + +import java.util.ArrayList; + +import de.dhbwloe.campusapp.database.DatabaseManager; + +/** + * Created by pk910 on 21.01.2016. + */ +public class CourseGroup { + private static ArrayList CourseGroups = new ArrayList(); + private int iCourseGroupId; + private String sCourseGroupName; + private String sCourseName; + private boolean bIsNew = false; + private ArrayList events = new ArrayList(); + + public static CourseGroup GetCourseGroupById(DatabaseManager dbm, int id) { + for(CourseGroup group : CourseGroups) { + if(group.iCourseGroupId == id) + return group; + } + return dbm.getCourseGroup(id); + } + + public static CourseGroup GetCourseGroupByName(DatabaseManager dbm, String coursename, String groupname) { + for(CourseGroup group : CourseGroups) { + if(group.sCourseName.equalsIgnoreCase(coursename) && group.sCourseGroupName.equalsIgnoreCase(groupname)) + return group; + } + CourseGroup group = dbm.getCourseGroup(coursename, groupname); + if(group == null) { + group = dbm.addCourseGroup(coursename, groupname); + group.bIsNew = true; + } + return group; + } + + public static void ResetEventGroups() { + for(CourseGroup group : CourseGroups) { + group.events.clear(); + } + } + + + public CourseGroup(int id, String coursename, String groupname) { + iCourseGroupId = id; + sCourseGroupName = groupname; + sCourseName = coursename; + + CourseGroups.add(this); + } + + public void addCourseEvent(CourseEvent event) { + this.events.add(event); + } + + public CourseEvent[] getCourseEvents() { + return (CourseEvent[])this.events.toArray(); + } + + public int getGroupId() { + return iCourseGroupId; + } + + public boolean isNewGroup(boolean reset) { + boolean ret = bIsNew; + if(reset) + bIsNew = false; + return ret; + } + +} diff --git a/app/src/main/java/de/dhbwloe/campusapp/vorlesungen/VorlesungsplanManager.java b/app/src/main/java/de/dhbwloe/campusapp/vorlesungen/VorlesungsplanManager.java new file mode 100644 index 0000000..5c68a4f --- /dev/null +++ b/app/src/main/java/de/dhbwloe/campusapp/vorlesungen/VorlesungsplanManager.java @@ -0,0 +1,261 @@ +package de.dhbwloe.campusapp.vorlesungen; + + +import android.util.Log; + +import net.fortuna.ical4j.model.Calendar; +import net.fortuna.ical4j.model.Component; +import net.fortuna.ical4j.model.Property; + +import java.text.DateFormat; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Date; +import java.util.ListIterator; +import java.util.Locale; + +import de.dhbwloe.campusapp.CampusAppContext; +import de.dhbwloe.campusapp.network.IscRequestHelper; +import de.dhbwloe.campusapp.search.SearchIndices; + +/** + * Created by pk910 on 19.01.2016. + */ +public class VorlesungsplanManager extends IscRequestHelper { + private static final String[][] PLAN_SOURCES = { + {"STUV", "https://www.google.com/calendar/ical/asta.dhbw.de_c0g35t6hrh16kr4ankrqg2rdm4%40group.calendar.google.com/public/basic.ics"}, + }; + + private CampusAppContext AppContext; + private boolean bRequestRunning = false; + private boolean bFastSynchronisation = false; + private String sCourseName; + private String source[]; + private ArrayList aCallbackInterfaces = new ArrayList(); + + public VorlesungsplanManager(CampusAppContext context, String courseName) { + AppContext = context; + sCourseName = courseName; + + for(String src[] : PLAN_SOURCES) { + if(src[0].equalsIgnoreCase(courseName)) { + source = src; + break; + } + } + } + + public void performFastSynchronisation(VorlesungsplanManagerInterface callback) { + performSynchronisation(callback, false); + } + + public void performFullSynchronisation(VorlesungsplanManagerInterface callback) { + performSynchronisation(callback, true); + } + + private void performSynchronisation(VorlesungsplanManagerInterface callback, boolean fullsync) { + if(sCourseName.length() == 0) { + callback.onVorlesungsplanUpdateFail("no course name"); + return; + } + + aCallbackInterfaces.add(callback); + if(bRequestRunning) + return; + + bFastSynchronisation = !fullsync; + bRequestRunning = true; + String courseCalendarUrl; + if(source == null) { + courseCalendarUrl = "https://webmail.dhbw-loerrach.de/owa/calendar/kal-" + sCourseName + "@dhbw-loerrach.de/Kalender/calendar.ics"; + } else { + courseCalendarUrl = source[1]; + } + requestCalenderFromWeb(courseCalendarUrl); + } + + @Override + protected void onCalendarReceived(Calendar calendar) { + long timeFrom, timeTo; + long now = (new Date()).getTime() / 1000; + if(bFastSynchronisation) { + timeFrom = now - (86400 * 5); + timeTo = now + (86400 * 7 * 4); + } else { + timeFrom = now - (86400 * 365 * 3); + timeTo = now + (86400 * 365 * 4); + } + CourseEvent[] events = AppContext.getDatabaseManager().getCourseCalendarEvents(sCourseName, timeFrom, timeTo); + Log.i("VPMSync", "Event count: " + events.length); + ArrayList newEvents = new ArrayList(); + ArrayList newIndices = new ArrayList(); + int lastEventIndex = 0; + ListIterator cIterator = calendar.getComponents().listIterator(); + + while (cIterator.hasNext()) { + Component event = (Component)cIterator.next(); + CourseEvent dbEvent; + Property prop; + + prop = event.getProperty("UID"); + String uid; + if(prop != null) + uid = prop.getValue(); + else + continue; + + prop = event.getProperty("SEQUENCE"); + int sequence; + if(prop != null) + sequence = Integer.parseInt(prop.getValue()); + else + sequence = 1; + + long startTime = 0, endTime = 0; + prop = event.getProperty("DTSTART"); + if(prop != null) + startTime = getTimeFromTimestamp(prop.getValue()); + else + Log.i("VPMSync", "Parse Event: DTSTART not found!"); + + prop = event.getProperty("DTEND"); + if(prop != null) + endTime = getTimeFromTimestamp(prop.getValue()); + else + Log.i("VPMSync", "Parse Event: DTEND not found!"); + + if(!(endTime > timeFrom && startTime < timeTo)) { + Log.i("VPMSync", "Skip Entry: ("+timeFrom+" - "+timeTo+") Filter: "+startTime+" - "+endTime); + continue; + } + + dbEvent = null; + for(int i = lastEventIndex; i < events.length; i++) { + if(events[i].getUniqueId().equalsIgnoreCase(uid)) { + dbEvent = events[i]; + break; + } + } + if(dbEvent == null && lastEventIndex > 0) { + for(int i = 0; i < lastEventIndex; i++) { + if(events[i].getUniqueId().equalsIgnoreCase(uid)) { + dbEvent = events[i]; + break; + } + } + } + if(dbEvent == null) { + for(CourseEvent cevent : newEvents) { + if(cevent.getUniqueId().equalsIgnoreCase(uid)) { + dbEvent = cevent; + break; + } + } + } + + if(dbEvent == null) { + // new event! + Log.i("VLPSync", "New Event "+uid); + dbEvent = new CourseEvent(sCourseName, uid, sequence, true); + newEvents.add(dbEvent); + } else { + Log.i("VLPSync", "Existing Event "+uid+" ("+dbEvent.getSequenceId()+" >= "+sequence+")"); + if(dbEvent.getSequenceId() >= sequence) { + continue; // skip event + } + dbEvent.setSequenceId(sequence); + } + // perform update + prop = event.getProperty("SUMMARY"); + if(prop != null) + dbEvent.setEventTitle(prop.getValue()); + else + Log.i("VPMSync", "Parse Event: SUMMARY not found!"); + + dbEvent.setEventFrom(startTime); + dbEvent.setEventTo(endTime); + + prop = event.getProperty("LOCATION"); + if(prop != null) + dbEvent.setEventLocation(prop.getValue()); + else + Log.i("VPMSync", "Parse Event: LOCATION not found!"); + + prop = event.getProperty("STATUS"); + if(prop != null) + dbEvent.setEventStatus(prop.getValue()); + else + Log.i("VPMSync", "Parse Event: STATUS not found!"); + + prop = event.getProperty("RRULE"); + if(prop != null) + dbEvent.setRecurRule(prop.getValue()); + else + dbEvent.setRecurRule(""); + + prop = event.getProperty("EXDATE"); + if(prop != null) + dbEvent.setExcludeDates(prop.getValue()); + else + dbEvent.setExcludeDates(""); + + CourseGroup group = CourseGroup.GetCourseGroupByName(AppContext.getDatabaseManager(), sCourseName, dbEvent.getGroupTitle().trim()); + if(group != null) + dbEvent.setCourseGroup(group); + + if(group.isNewGroup(true)) { + SearchIndices indices = new SearchIndices("Vorlesungsplan#Group"+group.getGroupId(), false); + indices.setUpdateTime(dbEvent.getEventFrom()); + indices.setTarget("#Vorlesungsplan#groupid=" + group.getGroupId()); + indices.setTitle("Vorlesungsplan " + dbEvent.getCourseName()); + indices.setDescription("Vorlesung " + dbEvent.getEventTitle()); + indices.addKeyWord(dbEvent.getGroupTitle()); + indices.addKeyWord(dbEvent.getEventLocation()); + newIndices.add(indices); + } + + dbEvent.update(AppContext.getDatabaseManager()); + Log.i("VPMSync", "Update Event: "+dbEvent.getUniqueId()); + } + + SearchIndices[] newIndicesArr = new SearchIndices[newIndices.size()]; + newIndicesArr = newIndices.toArray(newIndicesArr); + AppContext.addSearchIndices(newIndicesArr); + + for(VorlesungsplanManagerInterface callback : aCallbackInterfaces) { + callback.onVorlesungsplanUpdateDone(); + } + aCallbackInterfaces.clear(); + bRequestRunning = false; + } + + private long getTimeFromTimestamp(String timestamp) { + // 20160425T090000 + // 20160321 + DateFormat df = new SimpleDateFormat("yyyyMMdd'T'kkmmss", Locale.ENGLISH); + try { + Date result = df.parse(timestamp); + return result.getTime()/1000; + } catch (ParseException e) { + df = new SimpleDateFormat("yyyyMMdd", Locale.ENGLISH); + try { + Date result = df.parse(timestamp); + return result.getTime()/1000; + } catch (ParseException e2) { + Log.i("VPMSync", "Failed parsing: "+timestamp); + return 0; + } + } + } + + @Override + protected void onCalendarRequestFail(int statusCode, String errorMessage) { + Log.i("VPM", "Calendar Error: "+statusCode); + for(VorlesungsplanManagerInterface callback : aCallbackInterfaces) { + callback.onVorlesungsplanUpdateFail("error "+statusCode+": "+errorMessage); + } + aCallbackInterfaces.clear(); + bRequestRunning = false; + } +} diff --git a/app/src/main/java/de/dhbwloe/campusapp/vorlesungen/VorlesungsplanManagerInterface.java b/app/src/main/java/de/dhbwloe/campusapp/vorlesungen/VorlesungsplanManagerInterface.java new file mode 100644 index 0000000..5635bb8 --- /dev/null +++ b/app/src/main/java/de/dhbwloe/campusapp/vorlesungen/VorlesungsplanManagerInterface.java @@ -0,0 +1,11 @@ +package de.dhbwloe.campusapp.vorlesungen; + +/** + * Created by pk910 on 20.01.2016. + */ +public interface VorlesungsplanManagerInterface { + + public void onVorlesungsplanUpdateDone(); + public void onVorlesungsplanUpdateFail(String errorMessage); + +} diff --git a/app/src/main/res/drawable-hdpi/ic_info_black_24dp.png b/app/src/main/res/drawable-hdpi/ic_info_black_24dp.png new file mode 100644 index 0000000..da56077 Binary files /dev/null and b/app/src/main/res/drawable-hdpi/ic_info_black_24dp.png differ diff --git a/app/src/main/res/drawable-hdpi/ic_notifications_black_24dp.png b/app/src/main/res/drawable-hdpi/ic_notifications_black_24dp.png new file mode 100644 index 0000000..e200012 Binary files /dev/null and b/app/src/main/res/drawable-hdpi/ic_notifications_black_24dp.png differ diff --git a/app/src/main/res/drawable-hdpi/ic_sync_black_24dp.png b/app/src/main/res/drawable-hdpi/ic_sync_black_24dp.png new file mode 100644 index 0000000..a5ebdbd Binary files /dev/null and b/app/src/main/res/drawable-hdpi/ic_sync_black_24dp.png differ diff --git a/app/src/main/res/drawable-mdpi/ic_info_black_24dp.png b/app/src/main/res/drawable-mdpi/ic_info_black_24dp.png new file mode 100644 index 0000000..5ef3dc0 Binary files /dev/null and b/app/src/main/res/drawable-mdpi/ic_info_black_24dp.png differ diff --git a/app/src/main/res/drawable-mdpi/ic_notifications_black_24dp.png b/app/src/main/res/drawable-mdpi/ic_notifications_black_24dp.png new file mode 100644 index 0000000..b36475d Binary files /dev/null and b/app/src/main/res/drawable-mdpi/ic_notifications_black_24dp.png differ diff --git a/app/src/main/res/drawable-mdpi/ic_sync_black_24dp.png b/app/src/main/res/drawable-mdpi/ic_sync_black_24dp.png new file mode 100644 index 0000000..9685e8e Binary files /dev/null and b/app/src/main/res/drawable-mdpi/ic_sync_black_24dp.png differ diff --git a/app/src/main/res/drawable-v21/ic_info_black_24dp.xml b/app/src/main/res/drawable-v21/ic_info_black_24dp.xml new file mode 100644 index 0000000..34b8202 --- /dev/null +++ b/app/src/main/res/drawable-v21/ic_info_black_24dp.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable-v21/ic_menu_camera.xml b/app/src/main/res/drawable-v21/ic_menu_camera.xml new file mode 100644 index 0000000..0d9ea10 --- /dev/null +++ b/app/src/main/res/drawable-v21/ic_menu_camera.xml @@ -0,0 +1,12 @@ + + + + diff --git a/app/src/main/res/drawable-v21/ic_menu_gallery.xml b/app/src/main/res/drawable-v21/ic_menu_gallery.xml new file mode 100644 index 0000000..f6872c4 --- /dev/null +++ b/app/src/main/res/drawable-v21/ic_menu_gallery.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable-v21/ic_menu_manage.xml b/app/src/main/res/drawable-v21/ic_menu_manage.xml new file mode 100644 index 0000000..c1be60b --- /dev/null +++ b/app/src/main/res/drawable-v21/ic_menu_manage.xml @@ -0,0 +1,9 @@ + + + \ No newline at end of file diff --git a/app/src/main/res/drawable-v21/ic_menu_send.xml b/app/src/main/res/drawable-v21/ic_menu_send.xml new file mode 100644 index 0000000..00c668c --- /dev/null +++ b/app/src/main/res/drawable-v21/ic_menu_send.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable-v21/ic_menu_share.xml b/app/src/main/res/drawable-v21/ic_menu_share.xml new file mode 100644 index 0000000..a28fb9e --- /dev/null +++ b/app/src/main/res/drawable-v21/ic_menu_share.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable-v21/ic_menu_slideshow.xml b/app/src/main/res/drawable-v21/ic_menu_slideshow.xml new file mode 100644 index 0000000..209aa64 --- /dev/null +++ b/app/src/main/res/drawable-v21/ic_menu_slideshow.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable-v21/ic_notifications_black_24dp.xml b/app/src/main/res/drawable-v21/ic_notifications_black_24dp.xml new file mode 100644 index 0000000..e3400cf --- /dev/null +++ b/app/src/main/res/drawable-v21/ic_notifications_black_24dp.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable-v21/ic_sync_black_24dp.xml b/app/src/main/res/drawable-v21/ic_sync_black_24dp.xml new file mode 100644 index 0000000..3f0ac1c --- /dev/null +++ b/app/src/main/res/drawable-v21/ic_sync_black_24dp.xml @@ -0,0 +1,9 @@ + + + \ No newline at end of file diff --git a/app/src/main/res/drawable-xhdpi/ic_info_black_24dp.png b/app/src/main/res/drawable-xhdpi/ic_info_black_24dp.png new file mode 100644 index 0000000..46ed12a Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/ic_info_black_24dp.png differ diff --git a/app/src/main/res/drawable-xhdpi/ic_notifications_black_24dp.png b/app/src/main/res/drawable-xhdpi/ic_notifications_black_24dp.png new file mode 100644 index 0000000..7de8581 Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/ic_notifications_black_24dp.png differ diff --git a/app/src/main/res/drawable-xhdpi/ic_sync_black_24dp.png b/app/src/main/res/drawable-xhdpi/ic_sync_black_24dp.png new file mode 100644 index 0000000..860a5db Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/ic_sync_black_24dp.png differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_info_black_24dp.png b/app/src/main/res/drawable-xxhdpi/ic_info_black_24dp.png new file mode 100644 index 0000000..a81eeb9 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_info_black_24dp.png differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_notifications_black_24dp.png b/app/src/main/res/drawable-xxhdpi/ic_notifications_black_24dp.png new file mode 100644 index 0000000..ab8a9c4 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_notifications_black_24dp.png differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_sync_black_24dp.png b/app/src/main/res/drawable-xxhdpi/ic_sync_black_24dp.png new file mode 100644 index 0000000..f799008 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_sync_black_24dp.png differ diff --git a/app/src/main/res/drawable-xxxhdpi/ic_info_black_24dp.png b/app/src/main/res/drawable-xxxhdpi/ic_info_black_24dp.png new file mode 100644 index 0000000..c8f86b9 Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/ic_info_black_24dp.png differ diff --git a/app/src/main/res/drawable-xxxhdpi/ic_notifications_black_24dp.png b/app/src/main/res/drawable-xxxhdpi/ic_notifications_black_24dp.png new file mode 100644 index 0000000..86f89d7 Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/ic_notifications_black_24dp.png differ diff --git a/app/src/main/res/drawable-xxxhdpi/ic_sync_black_24dp.png b/app/src/main/res/drawable-xxxhdpi/ic_sync_black_24dp.png new file mode 100644 index 0000000..b9f56f3 Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/ic_sync_black_24dp.png differ diff --git a/app/src/main/res/drawable/appsearch_resulttype_external.xml b/app/src/main/res/drawable/appsearch_resulttype_external.xml new file mode 100644 index 0000000..607178a --- /dev/null +++ b/app/src/main/res/drawable/appsearch_resulttype_external.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/appsearch_resulttype_inapp.xml b/app/src/main/res/drawable/appsearch_resulttype_inapp.xml new file mode 100644 index 0000000..50c70b3 --- /dev/null +++ b/app/src/main/res/drawable/appsearch_resulttype_inapp.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/dhbw_campus.jpg b/app/src/main/res/drawable/dhbw_campus.jpg new file mode 100644 index 0000000..fa3cd9d Binary files /dev/null and b/app/src/main/res/drawable/dhbw_campus.jpg differ diff --git a/app/src/main/res/drawable/dhbw_campus_hd.jpg b/app/src/main/res/drawable/dhbw_campus_hd.jpg new file mode 100644 index 0000000..8bbb69b Binary files /dev/null and b/app/src/main/res/drawable/dhbw_campus_hd.jpg differ diff --git a/app/src/main/res/drawable/dhbw_logo.png b/app/src/main/res/drawable/dhbw_logo.png new file mode 100644 index 0000000..1985e06 Binary files /dev/null and b/app/src/main/res/drawable/dhbw_logo.png differ diff --git a/app/src/main/res/drawable/side_nav_bar.xml b/app/src/main/res/drawable/side_nav_bar.xml new file mode 100644 index 0000000..08f7756 --- /dev/null +++ b/app/src/main/res/drawable/side_nav_bar.xml @@ -0,0 +1,8 @@ + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_campus_app.xml b/app/src/main/res/layout/activity_campus_app.xml new file mode 100644 index 0000000..dc45769 --- /dev/null +++ b/app/src/main/res/layout/activity_campus_app.xml @@ -0,0 +1,25 @@ + + + + + + + + diff --git a/app/src/main/res/layout/app_bar_campus_app.xml b/app/src/main/res/layout/app_bar_campus_app.xml new file mode 100644 index 0000000..617c9a2 --- /dev/null +++ b/app/src/main/res/layout/app_bar_campus_app.xml @@ -0,0 +1,85 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/content_campus_app.xml b/app/src/main/res/layout/content_campus_app.xml new file mode 100644 index 0000000..8101d31 --- /dev/null +++ b/app/src/main/res/layout/content_campus_app.xml @@ -0,0 +1,18 @@ + + + + + + diff --git a/app/src/main/res/layout/content_popup.xml b/app/src/main/res/layout/content_popup.xml new file mode 100644 index 0000000..e89dc38 --- /dev/null +++ b/app/src/main/res/layout/content_popup.xml @@ -0,0 +1,11 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_appsearch.xml b/app/src/main/res/layout/fragment_appsearch.xml new file mode 100644 index 0000000..db3775d --- /dev/null +++ b/app/src/main/res/layout/fragment_appsearch.xml @@ -0,0 +1,24 @@ + + + + + + + + + diff --git a/app/src/main/res/layout/fragment_appsearch_list.xml b/app/src/main/res/layout/fragment_appsearch_list.xml new file mode 100644 index 0000000..ee1c433 --- /dev/null +++ b/app/src/main/res/layout/fragment_appsearch_list.xml @@ -0,0 +1,13 @@ + + + + diff --git a/app/src/main/res/layout/fragment_appsearch_listitem.xml b/app/src/main/res/layout/fragment_appsearch_listitem.xml new file mode 100644 index 0000000..f16fb83 --- /dev/null +++ b/app/src/main/res/layout/fragment_appsearch_listitem.xml @@ -0,0 +1,62 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_dashboard.xml b/app/src/main/res/layout/fragment_dashboard.xml new file mode 100644 index 0000000..d6f19a5 --- /dev/null +++ b/app/src/main/res/layout/fragment_dashboard.xml @@ -0,0 +1,9 @@ + + + + + diff --git a/app/src/main/res/layout/fragment_first_run.xml b/app/src/main/res/layout/fragment_first_run.xml new file mode 100644 index 0000000..8596249 --- /dev/null +++ b/app/src/main/res/layout/fragment_first_run.xml @@ -0,0 +1,20 @@ + + + + +