--- /dev/null
+*.iml
+.gradle
+/local.properties
+/.idea/workspace.xml
+/.idea/libraries
+.DS_Store
+/build
+/captures
--- /dev/null
+DHBW Campus App
\ No newline at end of file
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+ <component name="CompilerConfiguration">
+ <option name="DEFAULT_COMPILER" value="Javac" />
+ <resourceExtensions />
+ <wildcardResourcePatterns>
+ <entry name="!?*.java" />
+ <entry name="!?*.form" />
+ <entry name="!?*.class" />
+ <entry name="!?*.groovy" />
+ <entry name="!?*.scala" />
+ <entry name="!?*.flex" />
+ <entry name="!?*.kt" />
+ <entry name="!?*.clj" />
+ </wildcardResourcePatterns>
+ <annotationProcessing>
+ <profile default="true" name="Default" enabled="false">
+ <processorPath useClasspath="true" />
+ </profile>
+ </annotationProcessing>
+ </component>
+</project>
\ No newline at end of file
--- /dev/null
+<component name="CopyrightManager">
+ <settings default="" />
+</component>
\ No newline at end of file
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+ <component name="Encoding">
+ <file url="PROJECT" charset="UTF-8" />
+ </component>
+</project>
\ No newline at end of file
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+ <component name="GradleSettings">
+ <option name="linkedExternalProjectsSettings">
+ <GradleProjectSettings>
+ <option name="distributionType" value="LOCAL" />
+ <option name="externalProjectPath" value="$PROJECT_DIR$" />
+ <option name="gradleHome" value="C:\Program Files\Android\Android Studio\gradle\gradle-2.8" />
+ <option name="gradleJvm" value="1.7" />
+ <option name="modules">
+ <set>
+ <option value="$PROJECT_DIR$" />
+ <option value="$PROJECT_DIR$/app" />
+ </set>
+ </option>
+ </GradleProjectSettings>
+ </option>
+ </component>
+</project>
\ No newline at end of file
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+ <component name="EntryPointsManager">
+ <entry_points version="2.0" />
+ </component>
+ <component name="NullableNotNullManager">
+ <option name="myDefaultNullable" value="android.support.annotation.Nullable" />
+ <option name="myDefaultNotNull" value="android.support.annotation.NonNull" />
+ <option name="myNullables">
+ <value>
+ <list size="4">
+ <item index="0" class="java.lang.String" itemvalue="org.jetbrains.annotations.Nullable" />
+ <item index="1" class="java.lang.String" itemvalue="javax.annotation.Nullable" />
+ <item index="2" class="java.lang.String" itemvalue="edu.umd.cs.findbugs.annotations.Nullable" />
+ <item index="3" class="java.lang.String" itemvalue="android.support.annotation.Nullable" />
+ </list>
+ </value>
+ </option>
+ <option name="myNotNulls">
+ <value>
+ <list size="4">
+ <item index="0" class="java.lang.String" itemvalue="org.jetbrains.annotations.NotNull" />
+ <item index="1" class="java.lang.String" itemvalue="javax.annotation.Nonnull" />
+ <item index="2" class="java.lang.String" itemvalue="edu.umd.cs.findbugs.annotations.NonNull" />
+ <item index="3" class="java.lang.String" itemvalue="android.support.annotation.NonNull" />
+ </list>
+ </value>
+ </option>
+ </component>
+ <component name="ProjectLevelVcsManager" settingsEditedManually="false">
+ <OptionsSetting value="true" id="Add" />
+ <OptionsSetting value="true" id="Remove" />
+ <OptionsSetting value="true" id="Checkout" />
+ <OptionsSetting value="true" id="Update" />
+ <OptionsSetting value="true" id="Status" />
+ <OptionsSetting value="true" id="Edit" />
+ <ConfirmationsSetting value="0" id="Add" />
+ <ConfirmationsSetting value="0" id="Remove" />
+ </component>
+ <component name="ProjectRootManager" version="2" languageLevel="JDK_1_7" assert-keyword="true" jdk-15="true" project-jdk-name="1.7" project-jdk-type="JavaSDK">
+ <output url="file://$PROJECT_DIR$/build/classes" />
+ </component>
+ <component name="ProjectType">
+ <option name="id" value="Android" />
+ </component>
+</project>
\ No newline at end of file
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+ <component name="ProjectModuleManager">
+ <modules>
+ <module fileurl="file://$PROJECT_DIR$/DHBWCampusApp.iml" filepath="$PROJECT_DIR$/DHBWCampusApp.iml" />
+ <module fileurl="file://$PROJECT_DIR$/app/app.iml" filepath="$PROJECT_DIR$/app/app.iml" />
+ </modules>
+ </component>
+</project>
\ No newline at end of file
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+ <component name="RunConfigurationProducerService">
+ <option name="ignoredProducers">
+ <set>
+ <option value="org.jetbrains.plugins.gradle.execution.test.runner.AllInPackageGradleConfigurationProducer" />
+ <option value="org.jetbrains.plugins.gradle.execution.test.runner.TestClassGradleConfigurationProducer" />
+ <option value="org.jetbrains.plugins.gradle.execution.test.runner.TestMethodGradleConfigurationProducer" />
+ </set>
+ </option>
+ </component>
+</project>
\ No newline at end of file
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+ <component name="VcsDirectoryMappings">
+ <mapping directory="$PROJECT_DIR$" vcs="Git" />
+ </component>
+</project>
\ No newline at end of file
--- /dev/null
+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'
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
--- /dev/null
+# 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 *;
+#}
--- /dev/null
+package de.dhbwloe.campusapp;
+
+import android.app.Application;
+import android.test.ApplicationTestCase;
+
+/**
+ * <a href="http://d.android.com/tools/testing/testing_android.html">Testing Fundamentals</a>
+ */
+public class ApplicationTest extends ApplicationTestCase<Application> {
+ public ApplicationTest() {
+ super(Application.class);
+ }
+}
\ No newline at end of file
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="de.dhbwloe.campusapp">
+
+ <uses-permission android:name="android.permission.NFC" />
+ <uses-permission android:name="android.permission.INTERNET" />
+ <uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />
+ <uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
+
+ <uses-feature
+ android:name="android.hardware.nfc"
+ android:required="false" />
+
+ <application
+ android:allowBackup="true"
+ android:icon="@mipmap/ic_launcher"
+ android:label="@string/app_name"
+ android:supportsRtl="true"
+ android:theme="@style/AppTheme">
+ <activity
+ android:name=".CampusApp"
+ android:label="@string/app_name"
+ android:theme="@style/AppTheme.NoActionBar">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ <activity
+ android:name=".SettingsActivity"
+ android:label="@string/title_activity_settings"
+ android:parentActivityName=".CampusApp">
+ <meta-data
+ android:name="android.support.PARENT_ACTIVITY"
+ android:value="de.dhbwloe.campusapp.CampusApp" />
+ </activity>
+ </application>
+
+</manifest>
--- /dev/null
+/*
+ * Utils.java
+ *
+ * Copyright (C) 2011 Eric Butler
+ *
+ * Authors:
+ * Eric Butler <eric@codebutler.com>
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+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> T findInList(List<T> list, Matcher<T> matcher) {
+ for (T item : list) {
+ if (matcher.matches(item)) {
+ return item;
+ }
+ }
+ return null;
+ }
+
+ public static interface Matcher<T> {
+ 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;
+ }
+ }
+}
--- /dev/null
+/*
+ * DesfireApplication.java
+ *
+ * Copyright (C) 2011 Eric Butler
+ *
+ * Authors:
+ * Eric Butler <eric@codebutler.com>
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+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<DesfireApplication> CREATOR = new Parcelable.Creator<DesfireApplication>() {
+ 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
--- /dev/null
+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);
+ }
+}
--- /dev/null
+/*
+ * DesfireFile.java
+ *
+ * Copyright (C) 2011 Eric Butler
+ *
+ * Authors:
+ * Eric Butler <eric@codebutler.com>
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+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<DesfireFile> CREATOR = new Parcelable.Creator<DesfireFile>() {
+ 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
--- /dev/null
+/*
+ * DesfireFileSettings.java
+ *
+ * Copyright (C) 2011 Eric Butler
+ *
+ * Authors:
+ * Eric Butler <eric@codebutler.com>
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+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<DesfireFileSettings> CREATOR = new Parcelable.Creator<DesfireFileSettings>() {
+ 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
--- /dev/null
+/*
+ * DesfireManufacturingData.java
+ *
+ * Copyright (C) 2011 Eric Butler
+ *
+ * Authors:
+ * Eric Butler <eric@codebutler.com>
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+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<DesfireManufacturingData> CREATOR = new Parcelable.Creator<DesfireManufacturingData>() {
+ public DesfireManufacturingData createFromParcel(Parcel source) {
+ return new DesfireManufacturingData(source);
+ }
+
+ public DesfireManufacturingData[] newArray(int size) {
+ return new DesfireManufacturingData[size];
+ }
+ };
+}
\ No newline at end of file
--- /dev/null
+/*
+ * DesfireProtocol.java
+ *
+ * Copyright (C) 2011 Eric Butler
+ *
+ * Authors:
+ * Eric Butler <eric@codebutler.com>
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+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
--- /dev/null
+/*
+ * DesfireRecord.java
+ *
+ * Copyright (C) 2011 Eric Butler
+ *
+ * Authors:
+ * Eric Butler <eric@codebutler.com>
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+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
--- /dev/null
+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;
+ }
+}
--- /dev/null
+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);
+ }
+ }
+
+}
--- /dev/null
+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);
+ }
+
+}
--- /dev/null
+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];
+ }
+
+}
--- /dev/null
+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<CampusAppFragment> 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<NavPage> lNavigationPages = new ArrayList<NavPage>();
+
+ 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<CampusAppFragment>) 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<CampusAppFragment> 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();
+ }
+
+}
--- /dev/null
+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.
+ * <p/>
+ * See <a href="http://developer.android.com/design/patterns/settings.html">
+ * Android Design: Settings</a> for design guidelines and the <a
+ * href="http://developer.android.com/guide/topics/ui/settings.html">Settings
+ * API Guide</a> 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<Header> 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);
+ }
+ }
+}
--- /dev/null
+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;
+ }
+
+}
--- /dev/null
+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<SearchIndices> indices = new ArrayList<SearchIndices>();
+ 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<NfcCardData> nfcCardDatas = new ArrayList<NfcCardData>();
+ 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);
+ }
+
+}
--- /dev/null
+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<MensaTagesplan> results = new ArrayList<MensaTagesplan>();
+ 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<Long> results = new ArrayList<Long>();
+ 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<Long> results = new ArrayList<Long>();
+ 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;
+ }
+
+}
--- /dev/null
+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<NewsItem> results = new ArrayList<NewsItem>();
+ 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;
+ }
+
+}
--- /dev/null
+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;
+ }
+
+}
--- /dev/null
+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<CourseEvent> events = new ArrayList<CourseEvent>();
+ 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;
+ }
+
+}
--- /dev/null
+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<Fragment> mFragmentDataList = new ArrayList<>();
+ private final List<String> 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);
+ }
+ }
+
+}
--- /dev/null
+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<AppSearchListItem> searchResultItems = new ArrayList<AppSearchListItem>();
+
+ @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<AppSearchListItem> 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());
+ }
+
+}
--- /dev/null
+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<AppSearchListItem> searchResultItems = new ArrayList<AppSearchListItem>();
+
+ @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());
+ }
+
+}
--- /dev/null
+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<AppSearchListItem> {
+ private Context context;
+ private int layoutResourceId;
+ private ArrayList<AppSearchListItem> data = new ArrayList<AppSearchListItem>();
+
+ public AppSearchListAdapter(Context context, int layoutResourceId, ArrayList<AppSearchListItem> 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);
+ }
+ }
+}
--- /dev/null
+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;
+ }
+}
--- /dev/null
+package de.dhbwloe.campusapp.fragments;
+
+/**
+ * Created by pk910 on 25.01.2016.
+ */
+public interface AppSearchProvider {
+ public void executeSearch(boolean reset);
+}
--- /dev/null
+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<AppSearchListItem> searchResultItems = new ArrayList<AppSearchListItem>();
+
+ @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<AppSearchListItem> 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());
+ }
+
+}
--- /dev/null
+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;
+ }
+}
--- /dev/null
+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;
+ }
+
+}
--- /dev/null
+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;
+ }
+}
--- /dev/null
+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<Fragment> mFragmentList = new ArrayList<>();
+ private final List<Bundle> mFragmentDataList = new ArrayList<>();
+ private final List<String> 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);
+ }
+ }
+
+}
--- /dev/null
+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)+" €");
+ }
+
+}
--- /dev/null
+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<de.dhbwloe.campusapp.mensaplan.MensaTagesplan> 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);
+ }
+
+ }
+
+}
--- /dev/null
+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<de.dhbwloe.campusapp.mensaplan.MensaTagesplan> {
+ private Context context;
+ private int layoutResourceId;
+ private ArrayList<de.dhbwloe.campusapp.mensaplan.MensaTagesplan> data = new ArrayList<de.dhbwloe.campusapp.mensaplan.MensaTagesplan>();
+
+ public MensaTagesplanListAdapter(Context context, int layoutResourceId, ArrayList<de.dhbwloe.campusapp.mensaplan.MensaTagesplan> 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
--- /dev/null
+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;
+ }
+
+}
--- /dev/null
+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;
+ }
+
+}
--- /dev/null
+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
--- /dev/null
+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;
+ }
+
+}
--- /dev/null
+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;
+ }
+
+}
--- /dev/null
+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();
+ }
+ }
+
+}
--- /dev/null
+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);
+ }
+
+
+
+}
--- /dev/null
+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;
+ }
+
+}
--- /dev/null
+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<MensaplanManagerInterface> aCallbackInterfaces = new ArrayList<MensaplanManagerInterface>();
+
+ 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<XmlEntry> entries) {
+ ArrayList<SearchIndices> newIndices = new ArrayList<SearchIndices>();
+
+ 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;
+ }
+}
--- /dev/null
+package de.dhbwloe.campusapp.mensaplan;
+
+/**
+ * Created by pk910 on 22.01.2016.
+ */
+public interface MensaplanManagerInterface {
+
+ public void onMensaplanUpdateDone();
+ public void onMensaplanUpdateFail(String errorMessage);
+
+}
--- /dev/null
+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<Component> 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);
+
+}
--- /dev/null
+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<XmlEntry> entries = new ArrayList<XmlEntry>();
+ 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<XmlEntry> children = new ArrayList<XmlEntry>();
+ 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);
+ }
+
+}
--- /dev/null
+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<XmlEntry> 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<XmlEntry> parseXml(InputStream input, String rootElementName, String elementName) {
+ List<XmlEntry> 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<XmlEntry> readXml(XmlPullParser parser, String rootElementName, String elementName) throws XmlPullParserException, IOException {
+ List<XmlEntry> entries = new ArrayList<XmlEntry>();
+
+ 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<XmlEntry> entries);
+ protected abstract void onXmlRequestFail(int statusCode, String errorMessage);
+
+}
--- /dev/null
+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;
+ }
+}
--- /dev/null
+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<NewsManagerInterface> aCallbackInterfaces = new ArrayList<NewsManagerInterface>();
+ 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<XmlEntry> entries) {
+ ArrayList<SearchIndices> newIndices = new ArrayList<SearchIndices>();
+
+ 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;
+ }
+}
--- /dev/null
+package de.dhbwloe.campusapp.news;
+
+/**
+ * Created by pk910 on 22.01.2016.
+ */
+public interface NewsManagerInterface {
+ public void onNewsUpdateDone();
+ public void onNewsUpdateFail(String errorMessage);
+}
--- /dev/null
+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);
+
+}
--- /dev/null
+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<NfcCardInterface> lNfcCardInterfaces = new ArrayList<NfcCardInterface>();
+
+ 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();
+ }
+ }
+
+}
--- /dev/null
+/*
+ * ICardReader.java
+ *
+ * Copyright (C) 2014 Jakob Wenzel
+ *
+ * Authors:
+ * Jakob Wenzel <jakobwenzel92@gmail.com>
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+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;
+}
--- /dev/null
+/*
+ * IntercardReader.java
+ *
+ * Copyright (C) 2014 Jakob Wenzel
+ *
+ * Authors:
+ * Jakob Wenzel <jakobwenzel92@gmail.com>
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+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;
+ }
+}
--- /dev/null
+/*
+ * MagnaCartaReader.java
+ *
+ * Copyright (C) 2014 Jakob Wenzel
+ *
+ * Authors:
+ * Jakob Wenzel <jakobwenzel92@gmail.com>
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+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;
+ }
+ }
+}
--- /dev/null
+/*
+ * ValueData.java
+ *
+ * Copyright (C) 2014 Jakob Wenzel
+ *
+ * Authors:
+ * Jakob Wenzel <jakobwenzel92@gmail.com>
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+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<NfcCardPlainData> plainDataRecords = new ArrayList<NfcCardPlainData>();
+ private ArrayList<NfcCardNamedData> namedDataRecords = new ArrayList<NfcCardNamedData>();
+ 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;
+ }
+
+}
--- /dev/null
+/*
+ * Readers.java
+ *
+ * Copyright (C) 2014 Jakob Wenzel
+ *
+ * Authors:
+ * Jakob Wenzel <jakobwenzel92@gmail.com>
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+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;
+ }
+}
--- /dev/null
+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<AppSearchListItem> onHtmlDocumentReceived(Document document) {
+ ArrayList<AppSearchListItem> results = new ArrayList<AppSearchListItem>();
+ 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<AppSearchListItem> onPlainTextReceived(byte[] data) {
+ return null;
+ }
+}
--- /dev/null
+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<AppSearchListItem> results = onHtmlDocumentReceived(document);
+ oRequestListener.onSearchResultsReceived(results);
+ } catch(Exception e) {
+ oRequestListener.onSearchFailed(e.getMessage());
+ }
+ } catch(Exception e) {
+ }
+ }
+ if(!parsedHttp) {
+ try {
+ List<AppSearchListItem> 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<AppSearchListItem> onHtmlDocumentReceived(Document document);
+ protected abstract List<AppSearchListItem> onPlainTextReceived(byte[] data);
+}
--- /dev/null
+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;
+ }
+}
+
--- /dev/null
+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<AppSearchListItem> results);
+ public void onSearchFailed(String error);
+
+}
--- /dev/null
+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);
+ }
+ }
+}
--- /dev/null
+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<AppSearchListItem> onHtmlDocumentReceived(Document document) {
+ //unused in here
+ return null;
+ }
+
+ @Override
+ protected List<AppSearchListItem> onPlainTextReceived(byte[] data) {
+ ByteArrayInputStream inputStream = new ByteArrayInputStream(data);
+ List<XmlEntry> entries = XmlRequestHelper.parseXml(inputStream, "channel", "item");
+ ArrayList<AppSearchListItem> 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;
+ }
+
+}
--- /dev/null
+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;
+ }
+}
--- /dev/null
+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<CourseGroup> CourseGroups = new ArrayList<CourseGroup>();
+ private int iCourseGroupId;
+ private String sCourseGroupName;
+ private String sCourseName;
+ private boolean bIsNew = false;
+ private ArrayList<CourseEvent> events = new ArrayList<CourseEvent>();
+
+ 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;
+ }
+
+}
--- /dev/null
+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<VorlesungsplanManagerInterface> aCallbackInterfaces = new ArrayList<VorlesungsplanManagerInterface>();
+
+ 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<CourseEvent> newEvents = new ArrayList<CourseEvent>();
+ ArrayList<SearchIndices> newIndices = new ArrayList<SearchIndices>();
+ 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;
+ }
+}
--- /dev/null
+package de.dhbwloe.campusapp.vorlesungen;
+
+/**
+ * Created by pk910 on 20.01.2016.
+ */
+public interface VorlesungsplanManagerInterface {
+
+ public void onVorlesungsplanUpdateDone();
+ public void onVorlesungsplanUpdateFail(String errorMessage);
+
+}
--- /dev/null
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportHeight="24.0"
+ android:viewportWidth="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zm1,15h-2v-6h2v6zm0,-8h-2V7h2v2z" />
+</vector>
--- /dev/null
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportHeight="24.0"
+ android:viewportWidth="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M12,12m-3.2,0a3.2,3.2 0,1 1,6.4 0a3.2,3.2 0,1 1,-6.4 0" />
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M9,2L7.17,4H4c-1.1,0 -2,0.9 -2,2v12c0,1.1 0.9,2 2,2h16c1.1,0 2,-0.9 2,-2V6c0,-1.1 -0.9,-2 -2,-2h-3.17L15,2H9zm3,15c-2.76,0 -5,-2.24 -5,-5s2.24,-5 5,-5 5,2.24 5,5 -2.24,5 -5,5z" />
+</vector>
--- /dev/null
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportHeight="24.0"
+ android:viewportWidth="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M22,16V4c0,-1.1 -0.9,-2 -2,-2H8c-1.1,0 -2,0.9 -2,2v12c0,1.1 0.9,2 2,2h12c1.1,0 2,-0.9 2,-2zm-11,-4l2.03,2.71L16,11l4,5H8l3,-4zM2,6v14c0,1.1 0.9,2 2,2h14v-2H4V6H2z" />
+</vector>
--- /dev/null
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportHeight="24.0"
+ android:viewportWidth="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M22.7,19l-9.1,-9.1c0.9,-2.3 0.4,-5 -1.5,-6.9 -2,-2 -5,-2.4 -7.4,-1.3L9,6 6,9 1.6,4.7C0.4,7.1 0.9,10.1 2.9,12.1c1.9,1.9 4.6,2.4 6.9,1.5l9.1,9.1c0.4,0.4 1,0.4 1.4,0l2.3,-2.3c0.5,-0.4 0.5,-1.1 0.1,-1.4z" />
+</vector>
\ No newline at end of file
--- /dev/null
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportHeight="24.0"
+ android:viewportWidth="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M2.01,21L23,12 2.01,3 2,10l15,2 -15,2z" />
+</vector>
--- /dev/null
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportHeight="24.0"
+ android:viewportWidth="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M18,16.08c-0.76,0 -1.44,0.3 -1.96,0.77L8.91,12.7c0.05,-0.23 0.09,-0.46 0.09,-0.7s-0.04,-0.47 -0.09,-0.7l7.05,-4.11c0.54,0.5 1.25,0.81 2.04,0.81 1.66,0 3,-1.34 3,-3s-1.34,-3 -3,-3 -3,1.34 -3,3c0,0.24 0.04,0.47 0.09,0.7L8.04,9.81C7.5,9.31 6.79,9 6,9c-1.66,0 -3,1.34 -3,3s1.34,3 3,3c0.79,0 1.5,-0.31 2.04,-0.81l7.12,4.16c-0.05,0.21 -0.08,0.43 -0.08,0.65 0,1.61 1.31,2.92 2.92,2.92 1.61,0 2.92,-1.31 2.92,-2.92s-1.31,-2.92 -2.92,-2.92z" />
+</vector>
--- /dev/null
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportHeight="24.0"
+ android:viewportWidth="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M4,6H2v14c0,1.1 0.9,2 2,2h14v-2H4V6zm16,-4H8c-1.1,0 -2,0.9 -2,2v12c0,1.1 0.9,2 2,2h12c1.1,0 2,-0.9 2,-2V4c0,-1.1 -0.9,-2 -2,-2zm-8,12.5v-9l6,4.5 -6,4.5z" />
+</vector>
--- /dev/null
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportHeight="24.0"
+ android:viewportWidth="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M11.5,22c1.1,0 2,-0.9 2,-2h-4c0,1.1 0.9,2 2,2zm6.5,-6v-5.5c0,-3.07 -2.13,-5.64 -5,-6.32V3.5c0,-0.83 -0.67,-1.5 -1.5,-1.5S10,2.67 10,3.5v0.68c-2.87,0.68 -5,3.25 -5,6.32V16l-2,2v1h17v-1l-2,-2z" />
+</vector>
--- /dev/null
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24.0dp"
+ android:height="24.0dp"
+ android:viewportHeight="24.0"
+ android:viewportWidth="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M12 4V1L8 5l4 4V6c3.31 0 6 2.69 6 6 0 1.01,-.25 1.97,-.7 2.8l1.46 1.46C19.54 15.03 20 13.57 20 12c0,-4.42,-3.58,-8,-8,-8zm0 14c-3.31 0,-6,-2.69,-6,-6 0,-1.01.25,-1.97.7,-2.8L5.24 7.74C4.46 8.97 4 10.43 4 12c0 4.42 3.58 8 8 8v3l4,-4,-4,-4v3z" />
+</vector>
\ No newline at end of file
--- /dev/null
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M12,8V4l8,8 -8,8v-4H4V8z"/>
+</vector>
--- /dev/null
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M17,1.01L7,1c-1.1,0 -1.99,0.9 -1.99,2v18c0,1.1 0.89,2 1.99,2h10c1.1,0 2,-0.9 2,-2V3c0,-1.1 -0.9,-1.99 -2,-1.99zM17,19H7V5h10v14z"/>
+</vector>
--- /dev/null
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+ android:shape="rectangle">
+ <gradient
+ android:angle="135"
+ android:endColor="#A30000"
+ android:startColor="#D9D9D9"
+ android:type="linear" />
+</shape>
\ No newline at end of file
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+<android.support.v4.widget.DrawerLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:id="@+id/drawer_layout"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:fitsSystemWindows="true"
+ tools:openDrawer="start">
+
+ <include
+ layout="@layout/app_bar_campus_app"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent" />
+
+ <android.support.design.widget.NavigationView
+ android:id="@+id/nav_view"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:layout_gravity="start"
+ android:fitsSystemWindows="true"
+ app:headerLayout="@layout/nav_header_campus_app"
+ app:menu="@menu/activity_campus_app_drawer" />
+
+</android.support.v4.widget.DrawerLayout>
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+<android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:fitsSystemWindows="true"
+ tools:context="de.dhbwloe.campusapp.CampusApp">
+
+ <android.support.design.widget.AppBarLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:theme="@style/AppTheme.AppBarOverlay">
+
+ <android.support.v7.widget.Toolbar
+ android:id="@+id/toolbar"
+ android:layout_width="match_parent"
+ android:layout_height="?attr/actionBarSize"
+ android:background="?attr/colorPrimary"
+ app:popupTheme="@style/AppTheme.PopupOverlay">
+
+ <LinearLayout
+ android:id="@+id/search_container"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:gravity="center_vertical"
+ android:orientation="horizontal"
+ android:visibility="gone">
+
+ <EditText
+ android:id="@+id/search_input"
+ android:layout_width="0dp"
+ android:layout_height="?attr/actionBarSize"
+ android:layout_weight="1"
+ android:background="@android:color/transparent"
+ android:gravity="center_vertical"
+ android:hint="Search"
+ android:imeOptions="actionSearch"
+ android:inputType="text"
+ android:maxLines="1"
+ android:paddingLeft="2dp"
+ android:singleLine="true"
+ android:textColor="#ffffff"
+ android:textColorHint="#b3ffffff" />
+
+ <ImageView
+ android:id="@+id/search_clear"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ android:src="@mipmap/ic_close_white"
+ android:padding="16dp" />
+ </LinearLayout>
+
+ <LinearLayout
+ android:id="@+id/title_container"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:gravity="center_vertical"
+ android:orientation="horizontal"
+ android:visibility="visible">
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textAppearance="?android:attr/textAppearanceLarge"
+ android:text="{Title}"
+ android:layout_weight="1"
+ android:imeOptions="actionSearch"
+ android:id="@+id/title" />
+
+ <ImageView
+ android:id="@+id/search_button"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:src="@mipmap/ic_search"
+ android:padding="16dp" />
+ </LinearLayout>
+ </android.support.v7.widget.Toolbar>
+
+ </android.support.design.widget.AppBarLayout>
+
+ <include layout="@layout/content_campus_app" />
+
+</android.support.design.widget.CoordinatorLayout>
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ app:layout_behavior="@string/appbar_scrolling_view_behavior"
+ tools:context="de.dhbwloe.campusapp.CampusApp"
+ tools:showIn="@layout/app_bar_campus_app">
+
+ <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/fragment_container"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ />
+
+</RelativeLayout>
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical" android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <RelativeLayout
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:id="@+id/content_container"
+ android:background="@android:color/background_light"></RelativeLayout>
+</LinearLayout>
\ No newline at end of file
--- /dev/null
+<android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <android.support.design.widget.AppBarLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar">
+
+ <android.support.design.widget.TabLayout
+ android:id="@+id/tabs"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ app:tabMode="scrollable"
+ />
+ </android.support.design.widget.AppBarLayout>
+
+ <android.support.v4.view.ViewPager
+ android:id="@+id/viewpager"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ app:layout_behavior="@string/appbar_scrolling_view_behavior" />
+</android.support.design.widget.CoordinatorLayout>
--- /dev/null
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical"
+ tools:context=".fragments.AppSearch">
+
+ <ListView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:id="@+id/searchResultItems"
+ android:layout_gravity="bottom|left|right|top" />
+</LinearLayout>
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical" android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <LinearLayout
+ android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_margin="5dp"
+ android:focusable="false"
+ android:clickable="false">
+
+ <RelativeLayout
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginBottom="3dp">
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:text="{Title}"
+ android:id="@+id/resultTitle" />
+ </RelativeLayout>
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="horizontal">
+
+ <LinearLayout
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent">
+
+ <ImageView
+ android:layout_width="40dp"
+ android:layout_height="wrap_content"
+ android:id="@+id/imageView2"
+ android:src="@drawable/appsearch_resulttype_inapp"
+ android:adjustViewBounds="true" />
+ </LinearLayout>
+
+ <LinearLayout
+ android:orientation="vertical"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:layout_gravity="left"
+ android:layout_weight="1">
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="{Description}"
+ android:id="@+id/resultDescription" />
+ </LinearLayout>
+
+ </LinearLayout>
+
+ </LinearLayout>
+
+</LinearLayout>
\ No newline at end of file
--- /dev/null
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ tools:context=".fragments.Dashboard">
+
+ <!-- TODO: Update blank fragment layout -->
+
+</FrameLayout>
--- /dev/null
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ tools:context="de.dhbwloe.campusapp.fragments.FirstRun">
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="{FirstRun}" />
+
+ <Button
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="Start App"
+ android:id="@+id/startAppBtn"
+ android:layout_gravity="center_horizontal" />
+
+</LinearLayout>
--- /dev/null
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ tools:context="de.dhbwloe.campusapp.fragments.Impressum">
+
+ <!-- TODO: Update blank fragment layout -->
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:text="{Impressum}" />
+
+</FrameLayout>
--- /dev/null
+<android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <android.support.design.widget.AppBarLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar">
+
+ <android.support.design.widget.TabLayout
+ android:id="@+id/tabs"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ app:tabMode="scrollable"
+ />
+ </android.support.design.widget.AppBarLayout>
+
+ <android.support.v4.view.ViewPager
+ android:id="@+id/viewpager"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ app:layout_behavior="@string/appbar_scrolling_view_behavior" />
+</android.support.design.widget.CoordinatorLayout>
--- /dev/null
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ tools:context="de.dhbwloe.campusapp.fragments.MensaCard"
+ android:orientation="vertical">
+
+ <!-- TODO: Update blank fragment layout -->
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textAppearance="?android:attr/textAppearanceLarge"
+ android:text="@string/mensacard_guthaben"
+ android:id="@+id/textView10"
+ android:layout_alignParentTop="true"
+ android:layout_centerHorizontal="true"
+ android:layout_marginTop="20dp" />
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textAppearance="?android:attr/textAppearanceLarge"
+ android:text="{balance}"
+ android:id="@+id/balanceTxt"
+ android:layout_alignParentTop="false"
+ android:layout_centerHorizontal="true"
+ android:layout_below="@+id/textView10"
+ android:layout_marginTop="10dp"
+ android:layout_marginBottom="60dp" />
+</RelativeLayout>
--- /dev/null
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical"
+ tools:context="de.dhbwloe.campusapp.fragments.MensaTagesplan">
+
+ <!-- TODO: Update blank fragment layout -->
+
+ <ListView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:id="@+id/tagesplanItems"
+ android:layout_gravity="bottom|left|right|top" />
+</LinearLayout>
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:padding="8dp">
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textAppearance="?android:attr/textAppearanceLarge"
+ android:text="{menuName}"
+ android:id="@+id/txtMenueName"
+ android:layout_alignParentTop="true"
+ android:layout_alignParentLeft="true"
+ android:layout_alignParentStart="true" />
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="{name}"
+ android:id="@+id/txtName"
+ android:layout_below="@+id/txtMenueName"
+ android:layout_alignLeft="@+id/txtMenueName"
+ android:layout_alignStart="@+id/txtMenueName"
+ android:layout_marginLeft="30dp"
+ android:layout_marginStart="31dp"
+ android:layout_marginTop="8dp" />
+</RelativeLayout>
\ No newline at end of file
--- /dev/null
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ tools:context="de.dhbwloe.campusapp.fragments.MensaWochenplan">
+
+ <!-- TODO: Update blank fragment layout -->
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:text="{MensaWochenplan}" />
+
+</FrameLayout>
--- /dev/null
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ tools:context="de.dhbwloe.campusapp.fragments.News">
+
+ <!-- TODO: Update blank fragment layout -->
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:text="{News}" />
+
+</FrameLayout>
--- /dev/null
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ tools:context=".fragments.SplashScreen">
+
+ <!-- TODO: Update blank fragment layout -->
+
+ <ImageView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:id="@+id/splashImage"
+ android:layout_centerVertical="true"
+ android:layout_centerHorizontal="true"
+ android:src="@drawable/dhbw_campus_hd"
+ android:scaleType="centerCrop" />
+
+ <ProgressBar
+ android:layout_width="match_parent"
+ android:layout_height="20dp"
+ android:id="@+id/splashProgress"
+ android:layout_alignParentBottom="true"
+ android:layout_alignParentLeft="true"
+ android:layout_alignParentStart="true"
+ android:layout_alignParentRight="true"
+ android:layout_alignParentEnd="true"
+ style="?android:attr/progressBarStyleHorizontal" />
+
+ <ImageView
+ android:layout_width="200dp"
+ android:layout_height="wrap_content"
+ android:id="@+id/imageView3"
+ android:src="@drawable/dhbw_logo"
+ android:adjustViewBounds="true"
+ android:layout_margin="8dp" />
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="Loading..."
+ android:id="@+id/textView"
+ android:layout_above="@+id/splashProgress"
+ android:layout_centerHorizontal="true" />
+
+</RelativeLayout>
--- /dev/null
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ tools:context="de.dhbwloe.campusapp.fragments.Vorlesungsplan">
+
+ <!-- TODO: Update blank fragment layout -->
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:text="{Vorlesungsplan}" />
+
+</RelativeLayout>
--- /dev/null
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ tools:context="de.dhbwloe.campusapp.fragments.WebBrowser">
+
+ <WebView
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:id="@+id/browserWebView" />
+
+</FrameLayout>
--- /dev/null
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical"
+ tools:context="de.dhbwloe.campusapp.fragments.WifiSettings"
+ android:padding="8dp">
+
+ <!-- TODO: Update blank fragment layout -->
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/wifisettings_about_wifi" />
+
+ <RelativeLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:padding="8dp">
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/wifisettings_ssid_caption"
+ android:id="@+id/textView3" />
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/wifisettings_security_caption"
+ android:id="@+id/textView4"
+ android:layout_below="@+id/textView3"
+ android:layout_alignLeft="@+id/textView3"
+ android:layout_alignStart="@+id/textView3" />
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/wifisettings_eap_caption"
+ android:id="@+id/textView6"
+ android:layout_below="@+id/textView4"
+ android:layout_alignLeft="@+id/textView3"
+ android:layout_alignStart="@+id/textView4" />
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/wifisettings_phase2_caption"
+ android:id="@+id/textView7"
+ android:layout_below="@+id/textView6"
+ android:layout_alignLeft="@+id/textView3"
+ android:layout_alignStart="@+id/textView6" />
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/wifisettings_cacert_caption"
+ android:id="@+id/textView8"
+ android:layout_below="@+id/textView7"
+ android:layout_alignLeft="@+id/textView3"
+ android:layout_alignStart="@+id/textView7" />
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="{ssid}"
+ android:id="@+id/ssidInfo"
+ android:paddingLeft="8dp"
+ android:layout_toRightOf="@+id/textView7"
+ android:layout_toEndOf="@+id/textView7" />
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="{security}"
+ android:id="@+id/securityInfo"
+ android:paddingLeft="8dp"
+ android:layout_below="@+id/textView3"
+ android:layout_toRightOf="@+id/textView7"
+ android:layout_toEndOf="@+id/textView7" />
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="{eap}"
+ android:id="@+id/eapInfo"
+ android:paddingLeft="8dp"
+ android:layout_below="@+id/textView4"
+ android:layout_toRightOf="@+id/textView7"
+ android:layout_toEndOf="@+id/textView7" />
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="{phase2}"
+ android:id="@+id/phase2Info"
+ android:paddingLeft="8dp"
+ android:layout_below="@+id/textView6"
+ android:layout_toRightOf="@+id/textView7"
+ android:layout_toEndOf="@+id/textView7" />
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="{cacert}"
+ android:id="@+id/cacertInfo"
+ android:paddingLeft="8dp"
+ android:layout_below="@+id/textView7"
+ android:layout_toRightOf="@+id/textView7"
+ android:layout_toEndOf="@+id/textView7" />
+ </RelativeLayout>
+ <RelativeLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:padding="8dp">
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/wifisettings_username_caption"
+ android:id="@+id/textView5"
+ android:layout_alignBottom="@+id/wifiUsername"
+ android:layout_marginBottom="8dp" />
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/wifisettings_password_caption"
+ android:id="@+id/textView9"
+ android:layout_alignBottom="@+id/wifiPassword"
+ android:layout_marginBottom="8dp" />
+
+ <EditText
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:id="@+id/wifiUsername"
+ android:layout_toRightOf="@+id/textView5"
+ android:layout_marginLeft="8dp"
+ android:layout_alignRight="@+id/wifiPassword"
+ android:layout_alignEnd="@+id/wifiPassword" />
+
+ <EditText
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:inputType="textPassword"
+ android:ems="10"
+ android:id="@+id/wifiPassword"
+ android:layout_centerHorizontal="true"
+ android:layout_below="@+id/wifiUsername"
+ android:layout_toRightOf="@+id/textView5"
+ android:layout_marginLeft="8dp" />
+ </RelativeLayout>
+
+ <RelativeLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_horizontal">
+
+ <Button
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/wifisettings_connect_button"
+ android:id="@+id/wifiConnectBtn"
+ android:layout_alignParentTop="true"
+ android:layout_centerHorizontal="true" />
+ </RelativeLayout>
+
+</LinearLayout>
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="@dimen/nav_header_height"
+ android:background="@drawable/dhbw_campus"
+ android:gravity="bottom"
+ android:orientation="vertical"
+ android:theme="@style/ThemeOverlay.AppCompat.Dark"
+ android:weightSum="1">
+
+ <RelativeLayout
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <ImageView
+ android:id="@+id/imageView"
+ android:layout_width="140dp"
+ android:layout_height="wrap_content"
+ android:paddingTop="@dimen/nav_header_vertical_spacing"
+ android:src="@drawable/dhbw_logo"
+ android:adjustViewBounds="true"
+ android:layout_alignParentTop="true"
+ android:layout_alignParentLeft="true"
+ android:layout_alignParentStart="true"
+ android:layout_marginTop="-8dp"
+ android:layout_marginLeft="8dp" />
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textAppearance="?android:attr/textAppearanceLarge"
+ android:text="Lörrach"
+ android:id="@+id/textView2"
+ android:textColor="#5d6971"
+ android:layout_alignBottom="@+id/imageView"
+ android:layout_toRightOf="@+id/imageView"
+ android:layout_toEndOf="@+id/imageView"
+ android:layout_marginBottom="18dp"
+ android:layout_marginLeft="8dp"
+ android:textSize="28sp" />
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:paddingTop="@dimen/nav_header_vertical_spacing"
+ android:text="Campus App"
+ android:textAppearance="@style/TextAppearance.AppCompat.Body1"
+ android:textSize="22sp"
+ android:textColor="#ffffff"
+ android:layout_alignLeft="@+id/textView2"
+ android:layout_alignStart="@+id/textView2"
+ android:layout_alignTop="@+id/textView2"
+ android:layout_marginTop="12dp" />
+ </RelativeLayout>
+
+</LinearLayout>
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+<menu xmlns:android="http://schemas.android.com/apk/res/android">
+
+ <group android:checkableBehavior="single" android:title="Campus App">
+ <item
+ android:id="@+id/nav_dashboard"
+ android:icon="@drawable/ic_menu_camera"
+ android:title="Dashboard" />
+ <item
+ android:id="@+id/nav_vorlesungsplan"
+ android:icon="@drawable/ic_menu_gallery"
+ android:title="Vorlesungsplan" />
+ <item
+ android:id="@+id/nav_mensa"
+ android:icon="@drawable/ic_menu_slideshow"
+ android:title="Mensa" />
+ <item
+ android:id="@+id/nav_news"
+ android:icon="@drawable/ic_menu_send"
+ android:title="News" />
+ <item
+ android:id="@+id/nav_settings"
+ android:icon="@drawable/ic_menu_share"
+ android:title="Settings" />
+ <item
+ android:id="@+id/nav_impressum"
+ android:icon="@drawable/ic_menu_send"
+ android:title="Impressum" />
+ </group>
+
+</menu>
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+<menu xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto">
+ <item
+ android:id="@+id/action_settings"
+ android:orderInCategory="100"
+ android:title="@string/action_settings"
+ app:showAsAction="never" />
+</menu>
--- /dev/null
+<resources>>
+
+ <style name="AppTheme.NoActionBar">
+ <item name="windowActionBar">false</item>
+ <item name="windowNoTitle">true</item>
+ <item name="android:windowDrawsSystemBarBackgrounds">true</item>
+ <item name="android:statusBarColor">@android:color/transparent</item>
+ </style>
+</resources>
--- /dev/null
+<resources>
+ <!-- Example customization of dimensions originally defined in res/values/dimens.xml
+ (such as screen margins) for screens with more than 820dp of available width. This
+ would include 7" and 10" devices in landscape (~960dp and ~1280dp respectively). -->
+ <dimen name="activity_horizontal_margin">64dp</dimen>
+</resources>
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <color name="colorPrimary">#3F51B5</color>
+ <color name="colorPrimaryDark">#303F9F</color>
+ <color name="colorAccent">#FF4081</color>
+</resources>
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <item name="popupfragment" type="id">popupfragment</item>
+</resources>
\ No newline at end of file
--- /dev/null
+<resources>
+ <!-- Default screen margins, per the Android Design guidelines. -->
+ <dimen name="nav_header_vertical_spacing">16dp</dimen>
+ <dimen name="nav_header_height">160dp</dimen>
+ <!-- Default screen margins, per the Android Design guidelines. -->
+ <dimen name="activity_horizontal_margin">16dp</dimen>
+ <dimen name="activity_vertical_margin">16dp</dimen>
+ <dimen name="fab_margin">16dp</dimen>
+</resources>
--- /dev/null
+<resources xmlns:android="http://schemas.android.com/apk/res/android">
+ <item name="ic_menu_camera" type="drawable">@android:drawable/ic_menu_camera</item>
+ <item name="ic_menu_gallery" type="drawable">@android:drawable/ic_menu_gallery</item>
+ <item name="ic_menu_slideshow" type="drawable">@android:drawable/ic_menu_slideshow</item>
+ <item name="ic_menu_manage" type="drawable">@android:drawable/ic_menu_manage</item>
+ <item name="ic_menu_share" type="drawable">@android:drawable/ic_menu_share</item>
+ <item name="ic_menu_send" type="drawable">@android:drawable/ic_menu_send</item>
+</resources>
--- /dev/null
+<resources>
+ <string name="app_name">DHBW Campus App</string>
+
+ <string name="navigation_drawer_open">Open navigation drawer</string>
+ <string name="navigation_drawer_close">Close navigation drawer</string>
+
+ <string name="action_settings">Settings</string>
+
+ <string name="wifisettings_about_wifi">Die DHBW Lörrach bietet den Studenten verschiedene Möglichkeiten Geräte mit dem W-LAN zu verbinden. Mithilfe dieser App kannst du dein Gerät dazu konfigurieren sich unter berücksichtigung sicherheitsrelevanter Überprüfungen mit dem dhbw-secure Netzwerk zu verbinden.</string>
+ <string name="wifisettings_ssid_caption">SSID:</string>
+ <string name="wifisettings_security_caption">Security:</string>
+ <string name="wifisettings_eap_caption">EAP-Methode:</string>
+ <string name="wifisettings_phase2_caption">Phase 2-Authentifizierung:</string>
+ <string name="wifisettings_cacert_caption">CA-Zertifikat:</string>
+ <string name="wifisettings_username_caption">Username:</string>
+ <string name="wifisettings_password_caption">Passwort:</string>
+
+ <string name="wifisettings_connect_button">Jetzt Verbinden</string>
+ <string name="title_activity_settings">Settings</string>
+
+ <!-- Strings related to Settings -->
+
+ <!-- Example General settings -->
+ <string name="pref_header_general">General</string>
+
+ <string name="pref_title_social_recommendations">Enable social recommendations</string>
+ <string name="pref_description_social_recommendations">Recommendations for people to contact
+ based on your message history
+ </string>
+
+ <string name="pref_title_display_name">Display name</string>
+ <string name="pref_default_display_name">John Smith</string>
+
+ <string name="pref_title_add_friends_to_messages">Add friends to messages</string>
+ <string-array name="pref_example_list_titles">
+ <item>Always</item>
+ <item>When possible</item>
+ <item>Never</item>
+ </string-array>
+ <string-array name="pref_example_list_values">
+ <item>1</item>
+ <item>0</item>
+ <item>-1</item>
+ </string-array>
+
+ <!-- Example settings for Data & Sync -->
+ <string name="pref_header_data_sync">Data & sync</string>
+
+ <string name="pref_title_sync_frequency">Sync frequency</string>
+ <string-array name="pref_sync_frequency_titles">
+ <item>15 minutes</item>
+ <item>30 minutes</item>
+ <item>1 hour</item>
+ <item>3 hours</item>
+ <item>6 hours</item>
+ <item>Never</item>
+ </string-array>
+ <string-array name="pref_sync_frequency_values">
+ <item>15</item>
+ <item>30</item>
+ <item>60</item>
+ <item>180</item>
+ <item>360</item>
+ <item>-1</item>
+ </string-array>
+
+ <string name="pref_title_system_sync_settings">System sync settings</string>
+
+ <!-- Example settings for Notifications -->
+ <string name="pref_header_notifications">Notifications</string>
+
+ <string name="pref_title_new_message_notifications">New message notifications</string>
+
+ <string name="pref_title_ringtone">Ringtone</string>
+ <string name="pref_ringtone_silent">Silent</string>
+
+ <string name="pref_title_vibrate">Vibrate</string>
+
+ <!-- TODO: Remove or change this placeholder text -->
+ <string name="hello_blank_fragment">Hello blank fragment</string>
+ <string name="mensacard_guthaben">Dein Guthaben:</string>
+</resources>
--- /dev/null
+<resources>
+
+ <!-- Base application theme. -->
+ <style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
+ <!-- Customize your theme here. -->
+ <item name="colorPrimary">@color/colorPrimary</item>
+ <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
+ <item name="colorAccent">@color/colorAccent</item>
+ </style>
+
+ <style name="AppTheme.NoActionBar">
+ <item name="windowActionBar">false</item>
+ <item name="windowNoTitle">true</item>
+ </style>
+
+ <style name="AppTheme.AppBarOverlay" parent="ThemeOverlay.AppCompat.Dark.ActionBar" />
+
+ <style name="AppTheme.PopupOverlay" parent="ThemeOverlay.AppCompat.Light" />
+
+</resources>
--- /dev/null
+<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
+
+ <!-- NOTE: Hide buttons to simplify the UI. Users can touch outside the dialog to
+ dismiss it. -->
+ <!-- NOTE: ListPreference's summary should be set to its value by the activity code. -->
+ <ListPreference
+ android:defaultValue="180"
+ android:entries="@array/pref_sync_frequency_titles"
+ android:entryValues="@array/pref_sync_frequency_values"
+ android:key="sync_frequency"
+ android:negativeButtonText="@null"
+ android:positiveButtonText="@null"
+ android:title="@string/pref_title_sync_frequency" />
+
+ <!-- This preference simply launches an intent when selected. Use this UI sparingly, per
+ design guidelines. -->
+ <Preference android:title="@string/pref_title_system_sync_settings">
+ <intent android:action="android.settings.SYNC_SETTINGS" />
+ </Preference>
+
+</PreferenceScreen>
--- /dev/null
+<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
+
+ <SwitchPreference
+ android:defaultValue="true"
+ android:key="example_switch"
+ android:summary="@string/pref_description_social_recommendations"
+ android:title="@string/pref_title_social_recommendations" />
+
+ <!-- NOTE: EditTextPreference accepts EditText attributes. -->
+ <!-- NOTE: EditTextPreference's summary should be set to its value by the activity code. -->
+ <EditTextPreference
+ android:capitalize="words"
+ android:defaultValue="@string/pref_default_display_name"
+ android:inputType="textCapWords"
+ android:key="example_text"
+ android:maxLines="1"
+ android:selectAllOnFocus="true"
+ android:singleLine="true"
+ android:title="@string/pref_title_display_name" />
+
+ <!-- NOTE: Hide buttons to simplify the UI. Users can touch outside the dialog to
+ dismiss it. -->
+ <!-- NOTE: ListPreference's summary should be set to its value by the activity code. -->
+ <ListPreference
+ android:defaultValue="-1"
+ android:entries="@array/pref_example_list_titles"
+ android:entryValues="@array/pref_example_list_values"
+ android:key="example_list"
+ android:negativeButtonText="@null"
+ android:positiveButtonText="@null"
+ android:title="@string/pref_title_add_friends_to_messages" />
+
+</PreferenceScreen>
--- /dev/null
+<preference-headers xmlns:android="http://schemas.android.com/apk/res/android">
+
+ <!-- These settings headers are only used on tablets. -->
+
+ <header
+ android:fragment="de.dhbwloe.campusapp.SettingsActivity$GeneralPreferenceFragment"
+ android:icon="@drawable/ic_info_black_24dp"
+ android:title="@string/pref_header_general" />
+
+ <header
+ android:fragment="de.dhbwloe.campusapp.SettingsActivity$NotificationPreferenceFragment"
+ android:icon="@drawable/ic_notifications_black_24dp"
+ android:title="@string/pref_header_notifications" />
+
+ <header
+ android:fragment="de.dhbwloe.campusapp.SettingsActivity$DataSyncPreferenceFragment"
+ android:icon="@drawable/ic_sync_black_24dp"
+ android:title="@string/pref_header_data_sync" />
+
+</preference-headers>
--- /dev/null
+<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
+
+ <!-- A 'parent' preference, which enables/disables child preferences (below)
+ when checked/unchecked. -->
+ <SwitchPreference
+ android:defaultValue="true"
+ android:key="notifications_new_message"
+ android:title="@string/pref_title_new_message_notifications" />
+
+ <!-- Allows the user to choose a ringtone in the 'notification' category. -->
+ <!-- NOTE: This preference will be enabled only when the checkbox above is checked. -->
+ <!-- NOTE: RingtonePreference's summary should be set to its value by the activity code. -->
+ <RingtonePreference
+ android:defaultValue="content://settings/system/notification_sound"
+ android:dependency="notifications_new_message"
+ android:key="notifications_new_message_ringtone"
+ android:ringtoneType="notification"
+ android:title="@string/pref_title_ringtone" />
+
+ <!-- NOTE: This preference will be enabled only when the checkbox above is checked. -->
+ <SwitchPreference
+ android:defaultValue="true"
+ android:dependency="notifications_new_message"
+ android:key="notifications_new_message_vibrate"
+ android:title="@string/pref_title_vibrate" />
+
+</PreferenceScreen>
--- /dev/null
+package de.dhbwloe.campusapp;
+
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+/**
+ * To work on unit tests, switch the Test Artifact in the Build Variants view.
+ */
+public class ExampleUnitTest {
+ @Test
+ public void addition_isCorrect() throws Exception {
+ assertEquals(4, 2 + 2);
+ }
+}
\ No newline at end of file
--- /dev/null
+// Top-level build file where you can add configuration options common to all sub-projects/modules.
+
+buildscript {
+ repositories {
+ jcenter()
+ }
+ dependencies {
+ classpath 'com.android.tools.build:gradle:1.5.0'
+
+ // NOTE: Do not place your application dependencies here; they belong
+ // in the individual module build.gradle files
+ }
+}
+
+allprojects {
+ repositories {
+ jcenter()
+ }
+}
+
+task clean(type: Delete) {
+ delete rootProject.buildDir
+}
--- /dev/null
+# Project-wide Gradle settings.
+
+# IDE (e.g. Android Studio) users:
+# Gradle settings configured through the IDE *will override*
+# any settings specified in this file.
+
+# For more details on how to configure your build environment visit
+# http://www.gradle.org/docs/current/userguide/build_environment.html
+
+# Specifies the JVM arguments used for the daemon process.
+# The setting is particularly useful for tweaking memory settings.
+# Default value: -Xmx10248m -XX:MaxPermSize=256m
+# org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
+
+# When configured, Gradle will run in incubating parallel mode.
+# This option should only be used with decoupled projects. More details, visit
+# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
+# org.gradle.parallel=true
\ No newline at end of file
--- /dev/null
+#Wed Oct 21 11:34:03 PDT 2015
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.8-all.zip
--- /dev/null
+#!/usr/bin/env bash
+
+##############################################################################
+##
+## Gradle start up script for UN*X
+##
+##############################################################################
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS=""
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn ( ) {
+ echo "$*"
+}
+
+die ( ) {
+ echo
+ echo "$*"
+ echo
+ exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+case "`uname`" in
+ CYGWIN* )
+ cygwin=true
+ ;;
+ Darwin* )
+ darwin=true
+ ;;
+ MINGW* )
+ msys=true
+ ;;
+esac
+
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+ ls=`ls -ld "$PRG"`
+ link=`expr "$ls" : '.*-> \(.*\)$'`
+ if expr "$link" : '/.*' > /dev/null; then
+ PRG="$link"
+ else
+ PRG=`dirname "$PRG"`"/$link"
+ fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >/dev/null
+APP_HOME="`pwd -P`"
+cd "$SAVED" >/dev/null
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD="$JAVA_HOME/jre/sh/java"
+ else
+ JAVACMD="$JAVA_HOME/bin/java"
+ fi
+ if [ ! -x "$JAVACMD" ] ; then
+ die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+ fi
+else
+ JAVACMD="java"
+ which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
+ MAX_FD_LIMIT=`ulimit -H -n`
+ if [ $? -eq 0 ] ; then
+ if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+ MAX_FD="$MAX_FD_LIMIT"
+ fi
+ ulimit -n $MAX_FD
+ if [ $? -ne 0 ] ; then
+ warn "Could not set maximum file descriptor limit: $MAX_FD"
+ fi
+ else
+ warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+ fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then
+ GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin, switch paths to Windows format before running java
+if $cygwin ; then
+ APP_HOME=`cygpath --path --mixed "$APP_HOME"`
+ CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+ JAVACMD=`cygpath --unix "$JAVACMD"`
+
+ # We build the pattern for arguments to be converted via cygpath
+ ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+ SEP=""
+ for dir in $ROOTDIRSRAW ; do
+ ROOTDIRS="$ROOTDIRS$SEP$dir"
+ SEP="|"
+ done
+ OURCYGPATTERN="(^($ROOTDIRS))"
+ # Add a user-defined pattern to the cygpath arguments
+ if [ "$GRADLE_CYGPATTERN" != "" ] ; then
+ OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+ fi
+ # Now convert the arguments - kludge to limit ourselves to /bin/sh
+ i=0
+ for arg in "$@" ; do
+ CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+ CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
+
+ if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
+ eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
+ else
+ eval `echo args$i`="\"$arg\""
+ fi
+ i=$((i+1))
+ done
+ case $i in
+ (0) set -- ;;
+ (1) set -- "$args0" ;;
+ (2) set -- "$args0" "$args1" ;;
+ (3) set -- "$args0" "$args1" "$args2" ;;
+ (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+ (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+ (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+ (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+ (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+ (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+ esac
+fi
+
+# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
+function splitJvmOpts() {
+ JVM_OPTS=("$@")
+}
+eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
+JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
+
+exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
--- /dev/null
+@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem
+@rem Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS=
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if "%ERRORLEVEL%" == "0" goto init
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto init
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:init
+@rem Get command-line arguments, handling Windowz variants
+
+if not "%OS%" == "Windows_NT" goto win9xME_args
+if "%@eval[2+2]" == "4" goto 4NT_args
+
+:win9xME_args
+@rem Slurp the command line arguments.
+set CMD_LINE_ARGS=
+set _SKIP=2
+
+:win9xME_args_slurp
+if "x%~1" == "x" goto execute
+
+set CMD_LINE_ARGS=%*
+goto execute
+
+:4NT_args
+@rem Get arguments from the 4NT Shell from JP Software
+set CMD_LINE_ARGS=%$
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="0" goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
--- /dev/null
+include ':app'