Initial commit
9
.gitignore
vendored
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
*.iml
|
||||||
|
.gradle
|
||||||
|
/local.properties
|
||||||
|
/.idea/workspace.xml
|
||||||
|
/.idea/libraries
|
||||||
|
.DS_Store
|
||||||
|
/build
|
||||||
|
/captures
|
||||||
|
.externalNativeBuild
|
||||||
BIN
.idea/caches/build_file_checksums.ser
generated
Normal file
29
.idea/codeStyles/Project.xml
generated
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
<component name="ProjectCodeStyleConfiguration">
|
||||||
|
<code_scheme name="Project" version="173">
|
||||||
|
<Objective-C-extensions>
|
||||||
|
<file>
|
||||||
|
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Import" />
|
||||||
|
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Macro" />
|
||||||
|
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Typedef" />
|
||||||
|
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Enum" />
|
||||||
|
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Constant" />
|
||||||
|
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Global" />
|
||||||
|
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Struct" />
|
||||||
|
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="FunctionPredecl" />
|
||||||
|
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Function" />
|
||||||
|
</file>
|
||||||
|
<class>
|
||||||
|
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Property" />
|
||||||
|
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Synthesize" />
|
||||||
|
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="InitMethod" />
|
||||||
|
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="StaticMethod" />
|
||||||
|
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="InstanceMethod" />
|
||||||
|
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="DeallocMethod" />
|
||||||
|
</class>
|
||||||
|
<extensions>
|
||||||
|
<pair source="cpp" header="h" fileNamingConvention="NONE" />
|
||||||
|
<pair source="c" header="h" fileNamingConvention="NONE" />
|
||||||
|
</extensions>
|
||||||
|
</Objective-C-extensions>
|
||||||
|
</code_scheme>
|
||||||
|
</component>
|
||||||
22
.idea/compiler.xml
generated
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="CompilerConfiguration">
|
||||||
|
<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" />
|
||||||
|
<entry name="!?*.aj" />
|
||||||
|
</wildcardResourcePatterns>
|
||||||
|
<annotationProcessing>
|
||||||
|
<profile default="true" name="Default" enabled="false">
|
||||||
|
<processorPath useClasspath="true" />
|
||||||
|
</profile>
|
||||||
|
</annotationProcessing>
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
3
.idea/copyright/profiles_settings.xml
generated
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
<component name="CopyrightManager">
|
||||||
|
<settings default="" />
|
||||||
|
</component>
|
||||||
18
.idea/gradle.xml
generated
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="GradleSettings">
|
||||||
|
<option name="linkedExternalProjectsSettings">
|
||||||
|
<GradleProjectSettings>
|
||||||
|
<option name="distributionType" value="DEFAULT_WRAPPED" />
|
||||||
|
<option name="externalProjectPath" value="$PROJECT_DIR$" />
|
||||||
|
<option name="modules">
|
||||||
|
<set>
|
||||||
|
<option value="$PROJECT_DIR$" />
|
||||||
|
<option value="$PROJECT_DIR$/app" />
|
||||||
|
</set>
|
||||||
|
</option>
|
||||||
|
<option name="resolveModulePerSourceSet" value="false" />
|
||||||
|
</GradleProjectSettings>
|
||||||
|
</option>
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
49
.idea/misc.xml
generated
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<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="ProjectRootManager" version="2" languageLevel="JDK_1_7" default="true" project-jdk-name="1.8" project-jdk-type="JavaSDK">
|
||||||
|
<output url="file://$PROJECT_DIR$/build/classes" />
|
||||||
|
</component>
|
||||||
|
<component name="ProjectType">
|
||||||
|
<option name="id" value="Android" />
|
||||||
|
</component>
|
||||||
|
<component name="masterDetails">
|
||||||
|
<states>
|
||||||
|
<state key="ProjectJDKs.UI">
|
||||||
|
<settings>
|
||||||
|
<last-edited>1.8</last-edited>
|
||||||
|
<splitter-proportions>
|
||||||
|
<option name="proportions">
|
||||||
|
<list>
|
||||||
|
<option value="0.2" />
|
||||||
|
</list>
|
||||||
|
</option>
|
||||||
|
</splitter-proportions>
|
||||||
|
</settings>
|
||||||
|
</state>
|
||||||
|
</states>
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
9
.idea/modules.xml
generated
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="ProjectModuleManager">
|
||||||
|
<modules>
|
||||||
|
<module fileurl="file://$PROJECT_DIR$/FUPlanner.iml" filepath="$PROJECT_DIR$/FUPlanner.iml" />
|
||||||
|
<module fileurl="file://$PROJECT_DIR$/app/app.iml" filepath="$PROJECT_DIR$/app/app.iml" />
|
||||||
|
</modules>
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
12
.idea/runConfigurations.xml
generated
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
<?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>
|
||||||
6
.idea/vcs.xml
generated
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="VcsDirectoryMappings">
|
||||||
|
<mapping directory="$PROJECT_DIR$" vcs="Git" />
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
1
app/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
/build
|
||||||
41
app/build.gradle
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
apply plugin: 'com.android.application'
|
||||||
|
|
||||||
|
android {
|
||||||
|
compileSdkVersion 27
|
||||||
|
buildToolsVersion '27.0.3'
|
||||||
|
defaultConfig {
|
||||||
|
applicationId "de.sebse.fuplanner"
|
||||||
|
minSdkVersion 15
|
||||||
|
targetSdkVersion 27
|
||||||
|
versionCode 1
|
||||||
|
versionName "1.0"
|
||||||
|
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
|
||||||
|
}
|
||||||
|
buildTypes {
|
||||||
|
release {
|
||||||
|
minifyEnabled false
|
||||||
|
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
implementation 'com.android.support:recyclerview-v7:27.1.1'
|
||||||
|
implementation fileTree(include: ['*.jar'], dir: 'libs')
|
||||||
|
androidTestImplementation('com.android.support.test.espresso:espresso-core:2.2.2', {
|
||||||
|
exclude group: 'com.android.support', module: 'support-annotations'
|
||||||
|
})
|
||||||
|
implementation 'com.android.support:appcompat-v7:27.1.1'
|
||||||
|
implementation 'com.android.support:design:27.1.1'
|
||||||
|
implementation 'com.android.support.constraint:constraint-layout:1.1.0'
|
||||||
|
implementation 'com.android.volley:volley:1.0.0'
|
||||||
|
//noinspection GradleDependency
|
||||||
|
implementation 'com.google.android.gms:play-services-auth:15.0.0'
|
||||||
|
implementation 'com.android.support:support-v4:27.1.1'
|
||||||
|
testImplementation 'junit:junit:4.12'
|
||||||
|
implementation fileTree(include: ['*.jar'], dir: 'libs')
|
||||||
|
implementation 'com.android.support:appcompat-v7:27.1.1'
|
||||||
|
implementation 'org.jbundle.util.osgi.wrapped:org.jbundle.util.osgi.wrapped.org.apache.http.client:4.1.2'
|
||||||
|
implementation 'com.android.support:support-v4:27.1.1'
|
||||||
|
}
|
||||||
25
app/proguard-rules.pro
vendored
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
# Add project specific ProGuard rules here.
|
||||||
|
# By default, the flags in this file are appended to flags specified
|
||||||
|
# in /home/sebastian/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 *;
|
||||||
|
#}
|
||||||
|
|
||||||
|
# Uncomment this to preserve the line number information for
|
||||||
|
# debugging stack traces.
|
||||||
|
#-keepattributes SourceFile,LineNumberTable
|
||||||
|
|
||||||
|
# If you keep the line number information, uncomment this to
|
||||||
|
# hide the original source file name.
|
||||||
|
#-renamesourcefileattribute SourceFile
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
package de.sebse.fuplanner;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.support.test.InstrumentationRegistry;
|
||||||
|
import android.support.test.runner.AndroidJUnit4;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
|
||||||
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Instrumentation test, which will execute on an Android device.
|
||||||
|
*
|
||||||
|
* @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
|
||||||
|
*/
|
||||||
|
@RunWith(AndroidJUnit4.class)
|
||||||
|
public class ExampleInstrumentedTest {
|
||||||
|
@Test
|
||||||
|
public void useAppContext() throws Exception {
|
||||||
|
// Context of the app under test.
|
||||||
|
Context appContext = InstrumentationRegistry.getTargetContext();
|
||||||
|
|
||||||
|
assertEquals("de.sebse.fuplanner", appContext.getPackageName());
|
||||||
|
}
|
||||||
|
}
|
||||||
26
app/src/main/AndroidManifest.xml
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
package="de.sebse.fuplanner">
|
||||||
|
|
||||||
|
<uses-permission android:name="android.permission.INTERNET" />
|
||||||
|
|
||||||
|
<application
|
||||||
|
android:allowBackup="true"
|
||||||
|
android:icon="@mipmap/ic_launcher"
|
||||||
|
android:label="@string/app_name"
|
||||||
|
android:roundIcon="@mipmap/ic_launcher_round"
|
||||||
|
android:supportsRtl="true"
|
||||||
|
android:theme="@style/FUTheme"
|
||||||
|
android:fullBackupContent="@xml/backup_descriptor">
|
||||||
|
<activity
|
||||||
|
android:name=".MainActivity"
|
||||||
|
android:label="@string/app_name">
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.intent.action.MAIN" />
|
||||||
|
|
||||||
|
<category android:name="android.intent.category.LAUNCHER" />
|
||||||
|
</intent-filter>
|
||||||
|
</activity>
|
||||||
|
</application>
|
||||||
|
|
||||||
|
</manifest>
|
||||||
BIN
app/src/main/ic_launcher-web.png
Normal file
|
After Width: | Height: | Size: 54 KiB |
292
app/src/main/java/de/sebse/fuplanner/MainActivity.java
Normal file
@@ -0,0 +1,292 @@
|
|||||||
|
package de.sebse.fuplanner;
|
||||||
|
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.net.Uri;
|
||||||
|
import android.support.v4.app.FragmentManager;
|
||||||
|
import android.support.v4.app.FragmentTransaction;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
|
import android.util.Log;
|
||||||
|
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 de.sebse.fuplanner.fragments.LoginFragment;
|
||||||
|
import de.sebse.fuplanner.fragments.ModulesFragment;
|
||||||
|
import de.sebse.fuplanner.fragments.StartupFragment;
|
||||||
|
import de.sebse.fuplanner.services.GoogleAuth.ConnectedListener;
|
||||||
|
import de.sebse.fuplanner.services.GoogleAuth.Credentials;
|
||||||
|
import de.sebse.fuplanner.services.GoogleAuth.CredentialsListener;
|
||||||
|
import de.sebse.fuplanner.services.GoogleAuth.GoogleAuth;
|
||||||
|
import de.sebse.fuplanner.services.KVV.KVV;
|
||||||
|
import de.sebse.fuplanner.services.KVV.LoginToken;
|
||||||
|
import de.sebse.fuplanner.services.KVV.Modules;
|
||||||
|
import de.sebse.fuplanner.tools.activity.Logger;
|
||||||
|
import de.sebse.fuplanner.tools.network.NetworkCallback;
|
||||||
|
import de.sebse.fuplanner.tools.network.NetworkError;
|
||||||
|
import de.sebse.fuplanner.tools.network.NetworkErrorCallback;
|
||||||
|
|
||||||
|
public class MainActivity extends AppCompatActivity
|
||||||
|
implements NavigationView.OnNavigationItemSelectedListener,
|
||||||
|
LoginFragment.OnLoginFragmentInteractionListener,
|
||||||
|
StartupFragment.OnStartupFragmentInteractionListener,
|
||||||
|
ModulesFragment.OnModulesFragmentInteractionListener {
|
||||||
|
|
||||||
|
FragmentManager fragmentManager;
|
||||||
|
private GoogleAuth mGoogleAuth;
|
||||||
|
private KVV mKVV;
|
||||||
|
private Logger log = new Logger("MainAct");
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
setContentView(R.layout.activity_main);
|
||||||
|
Toolbar toolbar = findViewById(R.id.toolbar);
|
||||||
|
setSupportActionBar(toolbar);
|
||||||
|
|
||||||
|
DrawerLayout drawer = 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 = findViewById(R.id.nav_view);
|
||||||
|
navigationView.setNavigationItemSelectedListener(this);
|
||||||
|
|
||||||
|
/*try {
|
||||||
|
(new MensaPlan(getApplicationContext())).request(0, new Date());
|
||||||
|
} catch (JSONException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}*/
|
||||||
|
this.getKVV().startUpdate();
|
||||||
|
|
||||||
|
fragmentManager = getSupportFragmentManager();
|
||||||
|
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
|
||||||
|
fragmentTransaction.replace(R.id.fragcontainer, StartupFragment.newInstance());
|
||||||
|
fragmentTransaction.commit();
|
||||||
|
findViewById(R.id.app_bar_layout).setVisibility(View.GONE);
|
||||||
|
|
||||||
|
|
||||||
|
this.getGoogleAuth().connect(new ConnectedListener() {
|
||||||
|
@Override
|
||||||
|
public void connected() {
|
||||||
|
// getGoogleAuth().setLoginState("seedorf96", "m&gcwBaT@");
|
||||||
|
getGoogleAuth().getLoginState(new CredentialsListener() {
|
||||||
|
@Override
|
||||||
|
public void onCredentials(final Credentials credentials) {
|
||||||
|
if (credentials == null || credentials.getUsername() == null || credentials.getPassword() == null) {
|
||||||
|
MainActivity.this.getKVV().endUpdate();
|
||||||
|
changeLoginState(null);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
MainActivity.this.getKVV().login(credentials.getUsername(), credentials.getPassword(), new NetworkCallback<LoginToken>() {
|
||||||
|
@Override
|
||||||
|
public void onResponse(@NonNull LoginToken success) {
|
||||||
|
MainActivity.this.getKVV().endUpdate();
|
||||||
|
changeLoginState(credentials);
|
||||||
|
}
|
||||||
|
}, new NetworkErrorCallback() {
|
||||||
|
@Override
|
||||||
|
public void onError(NetworkError error) {
|
||||||
|
MainActivity.this.getKVV().endUpdate();
|
||||||
|
changeLoginState(null);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onBackPressed() {
|
||||||
|
DrawerLayout drawer = findViewById(R.id.drawer_layout);
|
||||||
|
if (drawer.isDrawerOpen(GravityCompat.START)) {
|
||||||
|
drawer.closeDrawer(GravityCompat.START);
|
||||||
|
} else {
|
||||||
|
super.onBackPressed();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onCreateOptionsMenu(Menu menu) {
|
||||||
|
// Inflate the menu; this adds items to the action bar if it is present.
|
||||||
|
getMenuInflater().inflate(R.menu.main, 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("StatementWithEmptyBody")
|
||||||
|
@Override
|
||||||
|
public boolean onNavigationItemSelected(@NonNull MenuItem item) {
|
||||||
|
// Handle navigation view item clicks here.
|
||||||
|
int id = item.getItemId();
|
||||||
|
|
||||||
|
if (id == R.id.nav_schedule) {
|
||||||
|
|
||||||
|
} else if (id == R.id.nav_share) {
|
||||||
|
|
||||||
|
} else if (id == R.id.nav_logout) {
|
||||||
|
this.getKVV().logout();
|
||||||
|
this.getGoogleAuth().getLoginState(new CredentialsListener() {
|
||||||
|
@Override
|
||||||
|
public void onCredentials(Credentials credentials) {
|
||||||
|
if (credentials != null) {
|
||||||
|
MainActivity.this.getGoogleAuth().deleteLoginState(credentials.getUsername(), credentials.getPassword());
|
||||||
|
}
|
||||||
|
MainActivity.this.changeLoginState(null);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
DrawerLayout drawer = findViewById(R.id.drawer_layout);
|
||||||
|
drawer.closeDrawer(GravityCompat.START);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||||
|
super.onActivityResult(requestCode, resultCode, data);
|
||||||
|
this.getGoogleAuth().onActivityResult(requestCode, resultCode, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* --------------------------------------------*/
|
||||||
|
/* --------------------------------------------*/
|
||||||
|
/* --------------------------------------------*/
|
||||||
|
|
||||||
|
public GoogleAuth getGoogleAuth() {
|
||||||
|
log.d("Holla die Waldfee");
|
||||||
|
if (this.mGoogleAuth == null) {
|
||||||
|
this.mGoogleAuth = new GoogleAuth(this);
|
||||||
|
}
|
||||||
|
return this.mGoogleAuth;
|
||||||
|
}
|
||||||
|
|
||||||
|
public KVV getKVV() {
|
||||||
|
if (this.mKVV == null) {
|
||||||
|
this.mKVV = new KVV(this);
|
||||||
|
}
|
||||||
|
return this.mKVV;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void changeLoginState(Credentials credentials) {
|
||||||
|
log.d("change login state");
|
||||||
|
if (credentials == null) {
|
||||||
|
log.d("null");
|
||||||
|
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
|
||||||
|
fragmentTransaction.replace(R.id.fragcontainer, LoginFragment.newInstance());
|
||||||
|
fragmentTransaction.commit();
|
||||||
|
findViewById(R.id.app_bar_layout).setVisibility(View.VISIBLE);
|
||||||
|
|
||||||
|
NavigationView navigationView = findViewById(R.id.nav_view);
|
||||||
|
View header = navigationView.getHeaderView(0);
|
||||||
|
header.findViewById(R.id.imageView).setVisibility(View.GONE);
|
||||||
|
header.findViewById(R.id.login_name).setVisibility(View.GONE);
|
||||||
|
header.findViewById(R.id.login_page).setVisibility(View.VISIBLE);
|
||||||
|
navigationView.getMenu().clear();
|
||||||
|
navigationView.inflateMenu(R.menu.activity_main_drawer);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
log.d(credentials.getUsername());
|
||||||
|
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
|
||||||
|
fragmentTransaction.replace(R.id.fragcontainer, ModulesFragment.newInstance());
|
||||||
|
fragmentTransaction.commit();
|
||||||
|
findViewById(R.id.app_bar_layout).setVisibility(View.VISIBLE);
|
||||||
|
|
||||||
|
final NavigationView navigationView = findViewById(R.id.nav_view);
|
||||||
|
View header = navigationView.getHeaderView(0);
|
||||||
|
header.findViewById(R.id.imageView).setVisibility(View.VISIBLE);
|
||||||
|
header.findViewById(R.id.login_name).setVisibility(View.VISIBLE);
|
||||||
|
header.findViewById(R.id.login_page).setVisibility(View.GONE);
|
||||||
|
navigationView.getMenu().clear();
|
||||||
|
navigationView.inflateMenu(R.menu.activity_main_drawer_login);
|
||||||
|
navigationView.setCheckedItem(R.id.nav_modules);
|
||||||
|
getKVV().getModuleList(new NetworkCallback<Modules>() {
|
||||||
|
@Override
|
||||||
|
public void onResponse(@NonNull Modules success) {
|
||||||
|
log.d("Modules.get "+success.size());
|
||||||
|
//SubMenu moduleMenu = navigationView.getMenu().findItem(R.id.nav_modules).getSubMenu();
|
||||||
|
int i = 0;
|
||||||
|
for (Modules.Module module: success) {
|
||||||
|
if (module.semester.equals("SS 18")) {
|
||||||
|
MenuItem menuItem = navigationView.getMenu().add(Menu.NONE, Menu.NONE, 101 + i, module.title);
|
||||||
|
final int finalI = i;
|
||||||
|
menuItem.setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() {
|
||||||
|
@Override
|
||||||
|
public boolean onMenuItemClick(MenuItem item) {
|
||||||
|
onModulesFragmentInteraction(finalI);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, new NetworkErrorCallback() {
|
||||||
|
@Override
|
||||||
|
public void onError(NetworkError error) {
|
||||||
|
log.d("Modules.error "+error.toString());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onLoginFragmentInteraction(Credentials credentials) {
|
||||||
|
changeLoginState(credentials);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onStartupFragmentInteraction(Uri uri) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onModulesFragmentInteraction(final int itemPosition) {
|
||||||
|
Log.d("MainAct", "Item clicked "+itemPosition);
|
||||||
|
getKVV().getModuleList(new NetworkCallback<Modules>() {
|
||||||
|
@Override
|
||||||
|
public void onResponse(@NonNull Modules success) {
|
||||||
|
Log.d("MainAct", success.get(itemPosition).title);
|
||||||
|
}
|
||||||
|
}, new NetworkErrorCallback() {
|
||||||
|
@Override
|
||||||
|
public void onError(NetworkError error) {
|
||||||
|
// TODO
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,153 @@
|
|||||||
|
package de.sebse.fuplanner.fragments;
|
||||||
|
|
||||||
|
import android.app.ProgressDialog;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
|
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.EditText;
|
||||||
|
|
||||||
|
import de.sebse.fuplanner.MainActivity;
|
||||||
|
import de.sebse.fuplanner.R;
|
||||||
|
import de.sebse.fuplanner.services.GoogleAuth.Credentials;
|
||||||
|
import de.sebse.fuplanner.services.GoogleAuth.GoogleAuth;
|
||||||
|
import de.sebse.fuplanner.services.KVV.KVV;
|
||||||
|
import de.sebse.fuplanner.services.KVV.LoginToken;
|
||||||
|
import de.sebse.fuplanner.tools.network.NetworkCallback;
|
||||||
|
import de.sebse.fuplanner.tools.network.NetworkError;
|
||||||
|
import de.sebse.fuplanner.tools.network.NetworkErrorCallback;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A simple {@link Fragment} subclass.
|
||||||
|
* Activities that contain this fragment must implement the
|
||||||
|
* {@link LoginFragment.OnLoginFragmentInteractionListener} interface
|
||||||
|
* to handle interaction events.
|
||||||
|
* Use the {@link LoginFragment#newInstance} factory method to
|
||||||
|
* create an instance of this fragment.
|
||||||
|
*/
|
||||||
|
public class LoginFragment extends Fragment {
|
||||||
|
// TODO: Rename parameter arguments, choose names that match
|
||||||
|
// the fragment initialization parameters, e.g. ARG_ITEM_NUMBER
|
||||||
|
|
||||||
|
// TODO: Rename and change types of parameters
|
||||||
|
|
||||||
|
private OnLoginFragmentInteractionListener mListener;
|
||||||
|
|
||||||
|
public LoginFragment() {
|
||||||
|
// Required empty public constructor
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use this factory method to create a new instance of
|
||||||
|
* this fragment using the provided parameters.
|
||||||
|
*
|
||||||
|
* @return A new instance of fragment LoginFragment.
|
||||||
|
*/
|
||||||
|
// TODO: Rename and change types and number of parameters
|
||||||
|
public static LoginFragment newInstance() {
|
||||||
|
LoginFragment fragment = new LoginFragment();
|
||||||
|
Bundle args = new Bundle();
|
||||||
|
fragment.setArguments(args);
|
||||||
|
return fragment;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCreate(Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public View onCreateView(LayoutInflater inflater, ViewGroup container,
|
||||||
|
Bundle savedInstanceState) {
|
||||||
|
// Inflate the layout for this fragment
|
||||||
|
|
||||||
|
View v = inflater.inflate(R.layout.fragment_login, container, false);
|
||||||
|
View btn_login = v.findViewById(R.id.btn_login);
|
||||||
|
if (btn_login != null) {
|
||||||
|
btn_login.setOnClickListener(new View.OnClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onClick(View view) {
|
||||||
|
final ProgressDialog progressDialog = new ProgressDialog(LoginFragment.this.getContext(),
|
||||||
|
R.style.FUTheme_Dialog);
|
||||||
|
progressDialog.setIndeterminate(true);
|
||||||
|
progressDialog.setMessage("Authenticating...");
|
||||||
|
progressDialog.show();
|
||||||
|
|
||||||
|
|
||||||
|
EditText input_usr = ((View) view.getParent()).findViewById(R.id.input_username);
|
||||||
|
EditText input_pwd = ((View) view.getParent()).findViewById(R.id.input_password);
|
||||||
|
if (input_usr != null) {
|
||||||
|
if (input_pwd != null) {
|
||||||
|
if (LoginFragment.this.getActivity() == null) {
|
||||||
|
Log.e("KVVLogin", "Login fragment has no activity!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
final String username = input_usr.getText().toString();
|
||||||
|
final String password = input_pwd.getText().toString();
|
||||||
|
final KVV kvv = ((MainActivity) LoginFragment.this.getActivity()).getKVV();
|
||||||
|
final GoogleAuth gauth = ((MainActivity) LoginFragment.this.getActivity()).getGoogleAuth();
|
||||||
|
kvv.login(username, password, new NetworkCallback<LoginToken>() {
|
||||||
|
@Override
|
||||||
|
public void onResponse(@NonNull LoginToken success) {
|
||||||
|
progressDialog.dismiss();
|
||||||
|
Log.d("KVVLogin", success.toString());
|
||||||
|
gauth.setLoginState(username, password);
|
||||||
|
if (LoginFragment.this.mListener != null) {
|
||||||
|
Credentials cred = new Credentials(username, password);
|
||||||
|
LoginFragment.this.mListener.onLoginFragmentInteraction(cred);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, new NetworkErrorCallback() {
|
||||||
|
@Override
|
||||||
|
public void onError(NetworkError error) {
|
||||||
|
progressDialog.dismiss();
|
||||||
|
Log.e("KVVLoginError", error.getCode()+"");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onAttach(Context context) {
|
||||||
|
super.onAttach(context);
|
||||||
|
if (context instanceof OnLoginFragmentInteractionListener) {
|
||||||
|
mListener = (OnLoginFragmentInteractionListener) context;
|
||||||
|
} else {
|
||||||
|
throw new RuntimeException(context.toString()
|
||||||
|
+ " must implement OnLoginFragmentInteractionListener");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDetach() {
|
||||||
|
super.onDetach();
|
||||||
|
mListener = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This interface must be implemented by activities that contain this
|
||||||
|
* fragment to allow an interaction in this fragment to be communicated
|
||||||
|
* to the activity and potentially other fragments contained in that
|
||||||
|
* activity.
|
||||||
|
* <p>
|
||||||
|
* See the Android Training lesson <a href=
|
||||||
|
* "http://developer.android.com/training/basics/fragments/communicating.html"
|
||||||
|
* >Communicating with Other Fragments</a> for more information.
|
||||||
|
*/
|
||||||
|
public interface OnLoginFragmentInteractionListener {
|
||||||
|
// TODO: Update argument type and name
|
||||||
|
void onLoginFragmentInteraction(Credentials credentials);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,125 @@
|
|||||||
|
package de.sebse.fuplanner.fragments;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
|
import android.support.v4.app.Fragment;
|
||||||
|
import android.support.v7.widget.LinearLayoutManager;
|
||||||
|
import android.support.v7.widget.RecyclerView;
|
||||||
|
import android.util.Log;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
|
||||||
|
import de.sebse.fuplanner.MainActivity;
|
||||||
|
import de.sebse.fuplanner.R;
|
||||||
|
import de.sebse.fuplanner.services.KVV.Modules;
|
||||||
|
import de.sebse.fuplanner.tools.network.NetworkCallback;
|
||||||
|
import de.sebse.fuplanner.tools.network.NetworkError;
|
||||||
|
import de.sebse.fuplanner.tools.network.NetworkErrorCallback;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A fragment representing a list of Items.
|
||||||
|
* <p/>
|
||||||
|
* Activities containing this fragment MUST implement the {@link OnModulesFragmentInteractionListener}
|
||||||
|
* interface.
|
||||||
|
*/
|
||||||
|
public class ModulesFragment extends Fragment {
|
||||||
|
|
||||||
|
// TODO: Customize parameter argument names
|
||||||
|
//private static final String ARG_COLUMN_COUNT = "column-count";
|
||||||
|
// TODO: Customize parameters
|
||||||
|
private OnModulesFragmentInteractionListener mListener;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Mandatory empty constructor for the fragment manager to instantiate the
|
||||||
|
* fragment (e.g. upon screen orientation changes).
|
||||||
|
*/
|
||||||
|
public ModulesFragment() {
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Customize parameter initialization
|
||||||
|
@SuppressWarnings("unused")
|
||||||
|
public static ModulesFragment newInstance() {
|
||||||
|
ModulesFragment fragment = new ModulesFragment();
|
||||||
|
Bundle args = new Bundle();
|
||||||
|
/*args.putInt(ARG_COLUMN_COUNT, columnCount);*/
|
||||||
|
fragment.setArguments(args);
|
||||||
|
return fragment;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCreate(Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
|
||||||
|
/*if (getArguments() != null) {
|
||||||
|
mColumnCount = getArguments().getInt(ARG_COLUMN_COUNT);
|
||||||
|
}*/
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container,
|
||||||
|
Bundle savedInstanceState) {
|
||||||
|
Log.d("ModFrag", "onCreateView"+(getActivity() != null));
|
||||||
|
View view = inflater.inflate(R.layout.fragment_modules_list, container, false);
|
||||||
|
|
||||||
|
// Set the adapter
|
||||||
|
if (view instanceof RecyclerView) {
|
||||||
|
Context context = view.getContext();
|
||||||
|
RecyclerView recyclerView = (RecyclerView) view;
|
||||||
|
recyclerView.setLayoutManager(new LinearLayoutManager(context));
|
||||||
|
final ModulesRecyclerViewAdapter adapter = new ModulesRecyclerViewAdapter(mListener);
|
||||||
|
recyclerView.setAdapter(adapter);
|
||||||
|
|
||||||
|
|
||||||
|
if (getActivity() != null) {
|
||||||
|
((MainActivity) getActivity()).getKVV().getModuleList(new NetworkCallback<Modules>() {
|
||||||
|
@Override
|
||||||
|
public void onResponse(@NonNull Modules success) {
|
||||||
|
adapter.setModules(success);
|
||||||
|
}
|
||||||
|
}, new NetworkErrorCallback() {
|
||||||
|
@Override
|
||||||
|
public void onError(NetworkError error) {
|
||||||
|
Log.e("ModFrag", error.toString());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return view;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onAttach(Context context) {
|
||||||
|
Log.d("ModFrag", "onAttach");
|
||||||
|
super.onAttach(context);
|
||||||
|
if (context instanceof OnModulesFragmentInteractionListener) {
|
||||||
|
mListener = (OnModulesFragmentInteractionListener) context;
|
||||||
|
} else {
|
||||||
|
throw new RuntimeException(context.toString()
|
||||||
|
+ " must implement OnModulesFragmentInteractionListener");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDetach() {
|
||||||
|
super.onDetach();
|
||||||
|
mListener = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This interface must be implemented by activities that contain this
|
||||||
|
* fragment to allow an interaction in this fragment to be communicated
|
||||||
|
* to the activity and potentially other fragments contained in that
|
||||||
|
* activity.
|
||||||
|
* <p/>
|
||||||
|
* See the Android Training lesson <a href=
|
||||||
|
* "http://developer.android.com/training/basics/fragments/communicating.html"
|
||||||
|
* >Communicating with Other Fragments</a> for more information.
|
||||||
|
*/
|
||||||
|
public interface OnModulesFragmentInteractionListener {
|
||||||
|
// TODO: Update argument type and name
|
||||||
|
void onModulesFragmentInteraction(int item);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,89 @@
|
|||||||
|
package de.sebse.fuplanner.fragments;
|
||||||
|
|
||||||
|
import android.support.v7.widget.RecyclerView;
|
||||||
|
import android.util.Log;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
import de.sebse.fuplanner.R;
|
||||||
|
import de.sebse.fuplanner.fragments.ModulesFragment.OnModulesFragmentInteractionListener;
|
||||||
|
import de.sebse.fuplanner.services.KVV.Modules;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@link RecyclerView.Adapter} that can display a {@link Modules.Module} and makes a call to the
|
||||||
|
* specified {@link OnModulesFragmentInteractionListener}.
|
||||||
|
* TODO: Replace the implementation with code for your data type.
|
||||||
|
*/
|
||||||
|
public class ModulesRecyclerViewAdapter extends RecyclerView.Adapter<ModulesRecyclerViewAdapter.ViewHolder> {
|
||||||
|
|
||||||
|
private Modules mValues;
|
||||||
|
private final OnModulesFragmentInteractionListener mListener;
|
||||||
|
|
||||||
|
public ModulesRecyclerViewAdapter(OnModulesFragmentInteractionListener listener) {
|
||||||
|
mValues = null;
|
||||||
|
mListener = listener;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setModules(Modules modules) {
|
||||||
|
mValues = modules;
|
||||||
|
this.notifyDataSetChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
|
||||||
|
View view = LayoutInflater.from(parent.getContext())
|
||||||
|
.inflate(R.layout.fragment_modules, parent, false);
|
||||||
|
return new ViewHolder(view);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onBindViewHolder(final ViewHolder holder, int position) {
|
||||||
|
if (mValues == null)
|
||||||
|
return;
|
||||||
|
holder.mItemPosition = position;
|
||||||
|
holder.mIdView.setText(mValues.get(position).semester);
|
||||||
|
holder.mContentView.setText(mValues.get(position).title);
|
||||||
|
|
||||||
|
holder.mView.setOnClickListener(new View.OnClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onClick(View v) {
|
||||||
|
if (null != mListener) {
|
||||||
|
// Notify the active callbacks interface (the activity, if the
|
||||||
|
// fragment is attached to one) that an item has been selected.
|
||||||
|
mListener.onModulesFragmentInteraction(holder.mItemPosition);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getItemCount() {
|
||||||
|
if (mValues != null) {
|
||||||
|
Log.d("Recycler", String.valueOf(mValues.size()));
|
||||||
|
return mValues.size();
|
||||||
|
}
|
||||||
|
Log.d("Recycler", "0");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public class ViewHolder extends RecyclerView.ViewHolder {
|
||||||
|
public final View mView;
|
||||||
|
public final TextView mIdView;
|
||||||
|
public final TextView mContentView;
|
||||||
|
public int mItemPosition;
|
||||||
|
|
||||||
|
public ViewHolder(View view) {
|
||||||
|
super(view);
|
||||||
|
mView = view;
|
||||||
|
mIdView = (TextView) view.findViewById(R.id.id);
|
||||||
|
mContentView = (TextView) view.findViewById(R.id.content);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return super.toString() + " '" + mContentView.getText() + "'";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,91 @@
|
|||||||
|
package de.sebse.fuplanner.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 de.sebse.fuplanner.R;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A simple {@link Fragment} subclass.
|
||||||
|
* Activities that contain this fragment must implement the
|
||||||
|
* {@link StartupFragment.OnStartupFragmentInteractionListener} interface
|
||||||
|
* to handle interaction events.
|
||||||
|
* Use the {@link StartupFragment#newInstance} factory method to
|
||||||
|
* create an instance of this fragment.
|
||||||
|
*/
|
||||||
|
public class StartupFragment extends Fragment {
|
||||||
|
// TODO: Rename parameter arguments, choose names that match
|
||||||
|
// the fragment initialization parameters, e.g. ARG_ITEM_NUMBER
|
||||||
|
|
||||||
|
// TODO: Rename and change types of parameters
|
||||||
|
|
||||||
|
private OnStartupFragmentInteractionListener mListener;
|
||||||
|
|
||||||
|
public StartupFragment() {
|
||||||
|
// Required empty public constructor
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use this factory method to create a new instance of
|
||||||
|
* this fragment using the provided parameters.
|
||||||
|
*
|
||||||
|
* @return A new instance of fragment StartupFragment.
|
||||||
|
*/
|
||||||
|
// TODO: Rename and change types and number of parameters
|
||||||
|
public static StartupFragment newInstance() {
|
||||||
|
StartupFragment fragment = new StartupFragment();
|
||||||
|
Bundle args = new Bundle();
|
||||||
|
fragment.setArguments(args);
|
||||||
|
return fragment;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCreate(Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public View onCreateView(LayoutInflater inflater, ViewGroup container,
|
||||||
|
Bundle savedInstanceState) {
|
||||||
|
// Inflate the layout for this fragment
|
||||||
|
View v = inflater.inflate(R.layout.fragment_startup, container, false);
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onAttach(Context context) {
|
||||||
|
super.onAttach(context);
|
||||||
|
if (context instanceof OnStartupFragmentInteractionListener) {
|
||||||
|
mListener = (OnStartupFragmentInteractionListener) context;
|
||||||
|
} else {
|
||||||
|
throw new RuntimeException(context.toString()
|
||||||
|
+ " must implement OnStartupFragmentInteractionListener");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDetach() {
|
||||||
|
super.onDetach();
|
||||||
|
mListener = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This interface must be implemented by activities that contain this
|
||||||
|
* fragment to allow an interaction in this fragment to be communicated
|
||||||
|
* to the activity and potentially other fragments contained in that
|
||||||
|
* activity.
|
||||||
|
* <p>
|
||||||
|
* See the Android Training lesson <a href=
|
||||||
|
* "http://developer.android.com/training/basics/fragments/communicating.html"
|
||||||
|
* >Communicating with Other Fragments</a> for more information.
|
||||||
|
*/
|
||||||
|
public interface OnStartupFragmentInteractionListener {
|
||||||
|
// TODO: Update argument type and name
|
||||||
|
void onStartupFragmentInteraction(Uri uri);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
package de.sebse.fuplanner.services.GoogleAuth;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by sebastian on 07.11.17.
|
||||||
|
*/
|
||||||
|
|
||||||
|
public interface ConnectedListener {
|
||||||
|
public void connected();
|
||||||
|
}
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
package de.sebse.fuplanner.services.GoogleAuth;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by Sebastian on 06.11.2017.
|
||||||
|
*/
|
||||||
|
|
||||||
|
public class Credentials {
|
||||||
|
private final String username;
|
||||||
|
private final String password;
|
||||||
|
|
||||||
|
public Credentials(String username, String password) {
|
||||||
|
this.username = username;
|
||||||
|
this.password = password;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getUsername() {
|
||||||
|
return username;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getPassword() {
|
||||||
|
return password;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
package de.sebse.fuplanner.services.GoogleAuth;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by Sebastian on 06.11.2017.
|
||||||
|
*/
|
||||||
|
|
||||||
|
public interface CredentialsListener {
|
||||||
|
public void onCredentials(Credentials credentials);
|
||||||
|
}
|
||||||
@@ -0,0 +1,227 @@
|
|||||||
|
package de.sebse.fuplanner.services.GoogleAuth;
|
||||||
|
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.content.IntentSender;
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
|
import android.support.annotation.Nullable;
|
||||||
|
import android.support.v4.app.FragmentActivity;
|
||||||
|
import android.util.Log;
|
||||||
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
import com.google.android.gms.auth.api.credentials.Credential;
|
||||||
|
import com.google.android.gms.auth.api.credentials.CredentialRequest;
|
||||||
|
import com.google.android.gms.auth.api.credentials.CredentialRequestResponse;
|
||||||
|
import com.google.android.gms.auth.api.credentials.CredentialsClient;
|
||||||
|
import com.google.android.gms.auth.api.credentials.CredentialsOptions;
|
||||||
|
import com.google.android.gms.common.ConnectionResult;
|
||||||
|
import com.google.android.gms.common.GoogleApiAvailability;
|
||||||
|
import com.google.android.gms.common.api.ApiException;
|
||||||
|
import com.google.android.gms.common.api.CommonStatusCodes;
|
||||||
|
import com.google.android.gms.common.api.ResolvableApiException;
|
||||||
|
import com.google.android.gms.tasks.OnCompleteListener;
|
||||||
|
import com.google.android.gms.tasks.Task;
|
||||||
|
|
||||||
|
import static android.app.Activity.RESULT_OK;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by Sebastian on 06.11.2017.
|
||||||
|
*/
|
||||||
|
|
||||||
|
public class GoogleAuth {
|
||||||
|
// https://developers.google.com/identity/smartlock-passwords/android/retrieve-credentials
|
||||||
|
|
||||||
|
private static final String TAG = "GoogleAuth";
|
||||||
|
private final FragmentActivity activity;
|
||||||
|
private CredentialsClient mCredentialsClient;
|
||||||
|
private boolean mIsResolving;
|
||||||
|
@Nullable
|
||||||
|
private CredentialsListener mCredentialsListener;
|
||||||
|
|
||||||
|
public GoogleAuth(FragmentActivity activity) {
|
||||||
|
this.activity = activity;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void connect(final ConnectedListener listener) {
|
||||||
|
if (this.isUnavailable()) {
|
||||||
|
Log.d(TAG, "STATUS: Google auth not available!");
|
||||||
|
listener.connected();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.mCredentialsClient = getClient();
|
||||||
|
listener.connected();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public void getLoginState(final CredentialsListener credentialsListener) {
|
||||||
|
if (this.isUnavailable()) {
|
||||||
|
Log.d(TAG, "STATUS: Google auth not available!");
|
||||||
|
credentialsListener.onCredentials(null);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
CredentialRequest request = new CredentialRequest.Builder()
|
||||||
|
.setPasswordLoginSupported(true)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
|
||||||
|
mCredentialsClient.request(request).addOnCompleteListener(new OnCompleteListener<CredentialRequestResponse>() {
|
||||||
|
@Override
|
||||||
|
public void onComplete(@NonNull Task<CredentialRequestResponse> task) {
|
||||||
|
if (task.isSuccessful()) {
|
||||||
|
// Successfully read the credential without any user interaction, this
|
||||||
|
// means there was only a single credential and the user has auto
|
||||||
|
// sign-in enabled.
|
||||||
|
Credential credential = task.getResult().getCredential();
|
||||||
|
credentialsListener.onCredentials(new Credentials(credential.getId(), credential.getPassword()));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Exception e = task.getException();
|
||||||
|
if (e instanceof ResolvableApiException) {
|
||||||
|
// This is most likely the case where the user has multiple saved
|
||||||
|
// credentials and needs to pick one. This requires showing UI to
|
||||||
|
// resolve the read request.
|
||||||
|
GoogleAuth.this.mCredentialsListener = credentialsListener;
|
||||||
|
ResolvableApiException rae = (ResolvableApiException) e;
|
||||||
|
resolveResult(rae, RequestCode.RC_READ);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (e instanceof ApiException) {
|
||||||
|
ApiException ae = (ApiException) e;
|
||||||
|
if (ae.getStatusCode() == CommonStatusCodes.SIGN_IN_REQUIRED) {
|
||||||
|
// This means only a hint is available, but we are handling that
|
||||||
|
// elsewhere so no need to act here.
|
||||||
|
} else {
|
||||||
|
Log.w(TAG, "Unexpected status code: " + ae.getStatusCode());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setLoginState(String username, String password) {
|
||||||
|
if (this.isUnavailable()) {
|
||||||
|
Log.d(TAG, "STATUS: Google auth not available!");
|
||||||
|
Toast.makeText(activity, "Google auth not available!", Toast.LENGTH_SHORT).show();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Credential credential = new Credential.Builder(username)
|
||||||
|
.setPassword(password)
|
||||||
|
.build();
|
||||||
|
mCredentialsClient.save(credential).addOnCompleteListener(new OnCompleteListener<Void>() {
|
||||||
|
@Override
|
||||||
|
public void onComplete(@NonNull Task<Void> task) {
|
||||||
|
if (task.isSuccessful()) {
|
||||||
|
showToast("Credential saved.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Exception e = task.getException();
|
||||||
|
if (e instanceof ResolvableApiException) {
|
||||||
|
// The first time a credential is saved, the user is shown UI
|
||||||
|
// to confirm the action. This requires resolution.
|
||||||
|
ResolvableApiException rae = (ResolvableApiException) e;
|
||||||
|
resolveResult(rae, RequestCode.RC_SAVE);
|
||||||
|
} else {
|
||||||
|
// Save failure cannot be resolved.
|
||||||
|
Log.w(TAG, "Save failed.", e);
|
||||||
|
showToast("Credential Save Failed");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public void deleteLoginState(String username, String password) {
|
||||||
|
if (this.isUnavailable()) {
|
||||||
|
Log.d(TAG, "STATUS: Google auth not available!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Credential credential = new Credential.Builder(username)
|
||||||
|
.setPassword(password)
|
||||||
|
.build();
|
||||||
|
mCredentialsClient.delete(credential).addOnCompleteListener(new OnCompleteListener<Void>() {
|
||||||
|
@Override
|
||||||
|
public void onComplete(@NonNull Task<Void> task) {
|
||||||
|
if (task.isSuccessful()) {
|
||||||
|
// Credential was deleted successfully
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private boolean isUnavailable() {
|
||||||
|
return GoogleApiAvailability.getInstance().isGooglePlayServicesAvailable(this.activity) != ConnectionResult.SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
private CredentialsClient getClient() {
|
||||||
|
CredentialsOptions options = new CredentialsOptions.Builder()
|
||||||
|
.forceEnableSaveDialog()
|
||||||
|
.build();
|
||||||
|
return com.google.android.gms.auth.api.credentials.Credentials.getClient(this.activity, options);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||||
|
Log.d(TAG, "onActivityResult:" + requestCode + ":" + resultCode + ":" + data);
|
||||||
|
|
||||||
|
switch (requestCode) {
|
||||||
|
case RequestCode.RC_HINT:
|
||||||
|
// Drop into handling for RC_READ
|
||||||
|
case RequestCode.RC_READ:
|
||||||
|
if (resultCode == RESULT_OK) {
|
||||||
|
boolean isHint = (requestCode == RequestCode.RC_HINT);
|
||||||
|
Credential credential = data.getParcelableExtra(Credential.EXTRA_KEY);
|
||||||
|
if (mCredentialsListener != null)
|
||||||
|
this.mCredentialsListener.onCredentials(new Credentials(credential.getId(), credential.getPassword()));
|
||||||
|
else
|
||||||
|
Log.d(TAG, "No Credentials Listener");
|
||||||
|
} else {
|
||||||
|
if (mCredentialsListener != null)
|
||||||
|
this.mCredentialsListener.onCredentials(null);
|
||||||
|
else
|
||||||
|
Log.d(TAG, "No Credentials Listener");
|
||||||
|
Log.e(TAG, "Credential Read: NOT OK");
|
||||||
|
showToast("Credential Read Failed");
|
||||||
|
}
|
||||||
|
|
||||||
|
mIsResolving = false;
|
||||||
|
break;
|
||||||
|
case RequestCode.RC_SAVE:
|
||||||
|
if (resultCode == RESULT_OK) {
|
||||||
|
Log.d(TAG, "Credential Save: OK");
|
||||||
|
showToast("Credential Save Success");
|
||||||
|
} else {
|
||||||
|
Log.e(TAG, "Credential Save: NOT OK");
|
||||||
|
showToast("Credential Save Failed");
|
||||||
|
}
|
||||||
|
|
||||||
|
mIsResolving = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private void resolveResult(ResolvableApiException rae, int requestCode) {
|
||||||
|
// We don't want to fire multiple resolutions at once since that can result
|
||||||
|
// in stacked dialogs after rotation or another similar event.
|
||||||
|
if (mIsResolving) {
|
||||||
|
Log.w(TAG, "resolveResult: already resolving.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Log.d(TAG, "Resolving: " + rae);
|
||||||
|
try {
|
||||||
|
rae.startResolutionForResult(this.activity, requestCode);
|
||||||
|
mIsResolving = true;
|
||||||
|
} catch (IntentSender.SendIntentException e) {
|
||||||
|
Log.e(TAG, "STATUS: Failed to send resolution.", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Display a short Toast message **/
|
||||||
|
private void showToast(String msg) {
|
||||||
|
Toast.makeText(this.activity, msg, Toast.LENGTH_SHORT).show();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
package de.sebse.fuplanner.services.GoogleAuth;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by sebastian on 07.11.17.
|
||||||
|
*/
|
||||||
|
|
||||||
|
public class RequestCode {
|
||||||
|
public static final int RC_SAVE = 1;
|
||||||
|
public static final int RC_HINT = 2;
|
||||||
|
public static final int RC_READ = 3;
|
||||||
|
}
|
||||||
77
app/src/main/java/de/sebse/fuplanner/services/KVV/KVV.java
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
package de.sebse.fuplanner.services.KVV;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
|
|
||||||
|
import de.sebse.fuplanner.tools.network.NetworkCallback;
|
||||||
|
import de.sebse.fuplanner.tools.network.NetworkErrorCallback;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by sebastian on 29.10.17.
|
||||||
|
*/
|
||||||
|
|
||||||
|
public class KVV {
|
||||||
|
private Context context;
|
||||||
|
private LoginToken lastToken;
|
||||||
|
private boolean isUpdating;
|
||||||
|
private ArrayList<LastTokenCallback> updatingList;
|
||||||
|
private HashMap<String, Object> addons = new HashMap<>();
|
||||||
|
|
||||||
|
public KVV(Context context) {
|
||||||
|
this.context = context;
|
||||||
|
this.isUpdating = false;
|
||||||
|
this.updatingList = new ArrayList<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void login(@NonNull String username, @NonNull String password, final NetworkCallback<LoginToken> callback, NetworkErrorCallback error) {
|
||||||
|
KVVLogin login = new KVVLogin(this.context);
|
||||||
|
login.login(username, password, new NetworkCallback<LoginToken>() {
|
||||||
|
@Override
|
||||||
|
public void onResponse(@NonNull LoginToken success) {
|
||||||
|
lastToken = success;
|
||||||
|
callback.onResponse(success);
|
||||||
|
}
|
||||||
|
}, error);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void logout() {
|
||||||
|
lastToken = null;
|
||||||
|
addons.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void getModuleList(final NetworkCallback<Modules> callback, final NetworkErrorCallback error) {
|
||||||
|
this.getLastToken(new LastTokenCallback() {
|
||||||
|
@Override
|
||||||
|
public void onReceived(LoginToken token) {
|
||||||
|
KVVModuleList modules = (KVVModuleList) addons.get("modules");
|
||||||
|
if (modules == null) {
|
||||||
|
modules = new KVVModuleList(KVV.this.context, token);
|
||||||
|
addons.put("modules", modules);
|
||||||
|
}
|
||||||
|
modules.getModuleList(callback, error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void getLastToken(LastTokenCallback lastTokenCallback) {
|
||||||
|
if (this.isUpdating) {
|
||||||
|
this.updatingList.add(lastTokenCallback);
|
||||||
|
} else {
|
||||||
|
lastTokenCallback.onReceived(this.lastToken);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void startUpdate() {
|
||||||
|
this.isUpdating = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void endUpdate() {
|
||||||
|
this.isUpdating = false;
|
||||||
|
for (LastTokenCallback s: this.updatingList) {
|
||||||
|
s.onReceived(this.lastToken);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
365
app/src/main/java/de/sebse/fuplanner/services/KVV/KVVLogin.java
Normal file
@@ -0,0 +1,365 @@
|
|||||||
|
package de.sebse.fuplanner.services.KVV;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import com.android.volley.Response;
|
||||||
|
import com.android.volley.VolleyError;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.regex.Matcher;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
import de.sebse.fuplanner.tools.network.HTTPService;
|
||||||
|
import de.sebse.fuplanner.tools.network.NetworkCallback;
|
||||||
|
import de.sebse.fuplanner.tools.network.NetworkError;
|
||||||
|
import de.sebse.fuplanner.tools.network.NetworkErrorCallback;
|
||||||
|
import de.sebse.fuplanner.tools.network.Result;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by sebastian on 24.10.17.
|
||||||
|
*/
|
||||||
|
|
||||||
|
class KVVLogin extends HTTPService {
|
||||||
|
public KVVLogin(Context context) {
|
||||||
|
super(context, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void login(final String username, final String password, final NetworkCallback<LoginToken> callback, final NetworkErrorCallback error) {
|
||||||
|
Log.d("KVVMaster0", username+" - "+password);
|
||||||
|
startKVVSession(new NetworkCallback<HashMap<String, String>>() {
|
||||||
|
@Override
|
||||||
|
public void onResponse(@NonNull HashMap<String, String> success) {
|
||||||
|
final String kvvJSESSIONID = success.get("JSESSIONID");
|
||||||
|
getSAMLRequest(kvvJSESSIONID, new NetworkCallback<HashMap<String, String>>() {
|
||||||
|
@Override
|
||||||
|
public void onResponse(@NonNull HashMap<String, String> success) {
|
||||||
|
startIdentSession(success.get("Location"), new NetworkCallback<HashMap<String, String>>() {
|
||||||
|
@Override
|
||||||
|
public void onResponse(@NonNull HashMap<String, String> success) {
|
||||||
|
final String identJSESSIONID = success.get("JSESSIONID");
|
||||||
|
final String ident_idp_authn_lc_key = success.get("_idp_authn_lc_key");
|
||||||
|
final String identROUTEID = success.get("ROUTEID");
|
||||||
|
loginIdent(true, username, password, identJSESSIONID, ident_idp_authn_lc_key, identROUTEID, new NetworkCallback<HashMap<String, String>>() {
|
||||||
|
@Override
|
||||||
|
public void onResponse(@NonNull HashMap<String, String> success) {
|
||||||
|
loginIdent(false, username, password, identJSESSIONID, ident_idp_authn_lc_key, identROUTEID, new NetworkCallback<HashMap<String, String>>() {
|
||||||
|
@Override
|
||||||
|
public void onResponse(@NonNull HashMap<String, String> success) {
|
||||||
|
final String ident_idp_session = success.get("_idp_session");
|
||||||
|
getSAMLResponse(identJSESSIONID, ident_idp_authn_lc_key, identROUTEID, ident_idp_session, new NetworkCallback<HashMap<String, String>>() {
|
||||||
|
@Override
|
||||||
|
public void onResponse(@NonNull HashMap<String, String> success) {
|
||||||
|
loginKVV(success.get("RelayState"), success.get("SAMLResponse"), kvvJSESSIONID, new NetworkCallback<HashMap<String, String>>() {
|
||||||
|
@Override
|
||||||
|
public void onResponse(@NonNull HashMap<String, String> success) {
|
||||||
|
final LoginToken token = new LoginToken(username, success.get("shibsessionKey"), success.get("shibsessionName"), kvvJSESSIONID);
|
||||||
|
finishKVVlogin(token, new NetworkCallback<HashMap<String, String>>() {
|
||||||
|
@Override
|
||||||
|
public void onResponse(@NonNull HashMap<String, String> success) {
|
||||||
|
Log.d("KVVMaster", "Login worked!");
|
||||||
|
callback.onResponse(token);
|
||||||
|
}
|
||||||
|
}, error);
|
||||||
|
}
|
||||||
|
}, error);
|
||||||
|
}
|
||||||
|
}, error);
|
||||||
|
}
|
||||||
|
}, error);
|
||||||
|
}
|
||||||
|
}, error);
|
||||||
|
}
|
||||||
|
}, error);
|
||||||
|
}
|
||||||
|
}, error);
|
||||||
|
}
|
||||||
|
}, error);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
GET https://kvv.imp.fu-berlin.de/portal/login
|
||||||
|
-> JSESSIONID 5c10406f-588c-4c16-96e9-c80d115417de.tomcat1
|
||||||
|
*/
|
||||||
|
private void startKVVSession(final NetworkCallback<HashMap<String, String>> callback, final NetworkErrorCallback errorCallback) {
|
||||||
|
get("https://kvv.imp.fu-berlin.de/portal/login", null, new Response.Listener<Result>() {
|
||||||
|
@Override
|
||||||
|
public void onResponse(Result response) {
|
||||||
|
String cookies = response.getHeaders().get("Set-Cookie");
|
||||||
|
if (cookies==null) {
|
||||||
|
errorCallback.onError(new NetworkError(100101, -1, "Error on starting KVV session!"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
HashMap<String, String> object;
|
||||||
|
try {
|
||||||
|
object = getCookie(cookies, new String[]{"JSESSIONID"});
|
||||||
|
} catch (NoSuchFieldException e) {
|
||||||
|
errorCallback.onError(new NetworkError(100102, -1, "Error on starting KVV session!"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
callback.onResponse(object);
|
||||||
|
}
|
||||||
|
}, new Response.ErrorListener() {
|
||||||
|
@Override
|
||||||
|
public void onErrorResponse(VolleyError error) {
|
||||||
|
errorCallback.onError(new NetworkError(100100, error.networkResponse.statusCode, "Error on starting KVV session!"));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
GET https://kvv.imp.fu-berlin.de/sakai-login-tool/container
|
||||||
|
<- JSESSIONID
|
||||||
|
-> (Location-Header) https://identity.fu-berlin.de/idp-fub/profile/SAML2/Redirect/SSO
|
||||||
|
?SAMLRequest=fZLLb.....Q8yre3X1IHwkJKE0Mnpy/V9TH4A
|
||||||
|
&RelayState=ss:mem:7ea01e29157b8bd906f7002176.....0d1a505f2c8bf
|
||||||
|
*/
|
||||||
|
private void getSAMLRequest(String JSESSIONID, final NetworkCallback<HashMap<String, String>> callback, final NetworkErrorCallback errorCallback) {
|
||||||
|
HashMap<String, String> cookies = new HashMap<>();
|
||||||
|
cookies.put("JSESSIONID", JSESSIONID);
|
||||||
|
get("https://kvv.imp.fu-berlin.de/sakai-login-tool/container", cookies, new Response.Listener<Result>() {
|
||||||
|
@Override
|
||||||
|
public void onResponse(Result response) {
|
||||||
|
String location = response.getHeaders().get("Location");
|
||||||
|
if (location==null) {
|
||||||
|
errorCallback.onError(new NetworkError(100111, -1, "Error on getting SAML request!"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
HashMap<String, String> object = new HashMap<>();
|
||||||
|
object.put("Location", location);
|
||||||
|
callback.onResponse(object);
|
||||||
|
}
|
||||||
|
}, new Response.ErrorListener() {
|
||||||
|
@Override
|
||||||
|
public void onErrorResponse(VolleyError error) {
|
||||||
|
errorCallback.onError(new NetworkError(100110, error.networkResponse.statusCode, "Error on getting SAML request!"));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
GET https://identity.fu-berlin.de/idp-fub/profile/SAML2/Redirect/SSO
|
||||||
|
?SAMLRequest=fZLLbsIwEEV/JfI+cWJAUIsgpbAoEi2IpF10UznxUKw6dupxaPn7hkdb2LD29bkzRzNGUeuGZ63fmjV8toA++K61QX58SEnrDLcCFXIjakDuK55njwvOopg3znpbWU2CDBGcV9ZMrcG2BpeD26kKnteLlGy9b5BT+rHbRapuok0bluC0MpEEmm9VWVoNfhshWnpgM7pa5gUJZt0wyogD9h+iJBiv/P6aomQTbtqSdhNtlIYzZg1SOag8zfMlCeazlLyNqpHsy1gO2V1fVsNBMuqJoUyAJaxXDUaiiyG2MDfohfEpYXEyDJM4ZKxgCe/FPI5fSbA6L36vjFTm/bal8hRC/lAUq/C02gs4PK7VBchkfHDNj8Xuwv5trPhVTiY3BeOf4DG96DmVNvypA89nK6tVtQ8yre3X1IHwkJKE0Mnpy/V9TH4A
|
||||||
|
&RelayState=ss:mem:7ea01e29157b8bd906f7002176213b6db5e1f45ebb88716a9820d1a505f2c8bf
|
||||||
|
-> JSESSIONID C4B6A428BA1F50746235D03F5D107A57
|
||||||
|
-> _idp_authn_lc_key 57a6ae26067f374cc3d0ccfc47e27b04b47752d2a3d4eb2782af0d3994535395
|
||||||
|
-> ROUTEID .1
|
||||||
|
*/
|
||||||
|
private void startIdentSession(String url, final NetworkCallback<HashMap<String, String>> callback, final NetworkErrorCallback errorCallback) {
|
||||||
|
get(url, null, new Response.Listener<Result>() {
|
||||||
|
@Override
|
||||||
|
public void onResponse(Result response) {
|
||||||
|
String cookies = response.getHeaders().get("Set-Cookie");
|
||||||
|
if (cookies==null) {
|
||||||
|
errorCallback.onError(new NetworkError(100121, -1, "Error on starting Ident session!"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
HashMap<String, String> object;
|
||||||
|
try {
|
||||||
|
object = getCookie(cookies, new String[]{"JSESSIONID", "_idp_authn_lc_key", "ROUTEID"});
|
||||||
|
} catch (NoSuchFieldException e) {
|
||||||
|
errorCallback.onError(new NetworkError(100122, -1, "Error on starting Ident session!"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
callback.onResponse(object);
|
||||||
|
}
|
||||||
|
}, new Response.ErrorListener() {
|
||||||
|
@Override
|
||||||
|
public void onErrorResponse(VolleyError error) {
|
||||||
|
errorCallback.onError(new NetworkError(100120, error.networkResponse.statusCode, "Error on starting Ident session!"));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
POST https://identity.fu-berlin.de/idp-fub/Authn/UserPassword
|
||||||
|
<- j_username seedorf96
|
||||||
|
<- j_password neinhieristpatrick
|
||||||
|
<- (Header-"Content-Type") application/x-www-form-urlencoded
|
||||||
|
<- JSESSIONID
|
||||||
|
<- _idp_authn_lc_key
|
||||||
|
<- ROUTEID
|
||||||
|
-> _idp_session OTMuMTkzLjg1LjMz|LQ==|OGYxOWI4MjA2NTQ4YWUwYzJkOWM4Mjk4YzcwZDMwZmJiZjBmMTdmMzkyZGU2OWIwY2JkNmZlNjlmNTRmNzBlMQ==|wLlzQal7VqyntmG2vLNn06wt8wQ=
|
||||||
|
*/
|
||||||
|
private void loginIdent(final boolean first, String username, String password, String JSESSIONID, String _idp_authn_lc_key, String ROUTEID, final NetworkCallback<HashMap<String, String>> callback, final NetworkErrorCallback errorCallback) {
|
||||||
|
HashMap<String, String> cookies = new HashMap<>();
|
||||||
|
cookies.put("JSESSIONID", JSESSIONID);
|
||||||
|
cookies.put("_idp_authn_lc_key", _idp_authn_lc_key);
|
||||||
|
cookies.put("ROUTEID", ROUTEID);
|
||||||
|
HashMap<String, String> body = new HashMap<>();
|
||||||
|
body.put("j_username", username);
|
||||||
|
body.put("j_password", password);
|
||||||
|
post("https://identity.fu-berlin.de/idp-fub/Authn/UserPassword", cookies, body, new Response.Listener<Result>() {
|
||||||
|
@Override
|
||||||
|
public void onResponse(Result response) {
|
||||||
|
if (first) {
|
||||||
|
callback.onResponse(new HashMap<String, String>());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
String cookies = response.getHeaders().get("Set-Cookie");
|
||||||
|
if (cookies==null) {
|
||||||
|
errorCallback.onError(new NetworkError(100131, -1, "Error on logging in to Identity Server!"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
HashMap<String, String> object;
|
||||||
|
try {
|
||||||
|
object = getCookie(cookies, new String[]{"_idp_session"});
|
||||||
|
} catch (NoSuchFieldException e) {
|
||||||
|
errorCallback.onError(new NetworkError(100132, -1, "Error on logging in to Identity Server!"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
callback.onResponse(object);
|
||||||
|
}
|
||||||
|
}, new Response.ErrorListener() {
|
||||||
|
@Override
|
||||||
|
public void onErrorResponse(VolleyError error) {
|
||||||
|
errorCallback.onError(new NetworkError(100130, error.networkResponse.statusCode, "Error on logging in to Identity Server!"));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
GET https://identity.fu-berlin.de/idp-fub/profile/SAML2/Redirect/SSO
|
||||||
|
<- JSESSIONID
|
||||||
|
<- _idp_authn_lc_key
|
||||||
|
<- ROUTEID
|
||||||
|
<- _idp_session
|
||||||
|
-> (BODY) RelayState 7ea01e29157b8bd906f7002176213b6db5e1f45ebb88716a9820d1a505f2c8bf
|
||||||
|
-> (BODY) SAMLResponse PD94bWwgdmVyc2lvbj0...........wvc2FtbDJwOlJlc3BvbnNlPg==
|
||||||
|
*/
|
||||||
|
private void getSAMLResponse(String JSESSIONID, String _idp_authn_lc_key, String ROUTEID, String _idp_session, final NetworkCallback<HashMap<String, String>> callback, final NetworkErrorCallback errorCallback) {
|
||||||
|
HashMap<String, String> cookies = new HashMap<>();
|
||||||
|
cookies.put("JSESSIONID", JSESSIONID);
|
||||||
|
cookies.put("_idp_authn_lc_key", _idp_authn_lc_key);
|
||||||
|
cookies.put("ROUTEID", ROUTEID);
|
||||||
|
cookies.put("_idp_session", _idp_session);
|
||||||
|
get("https://identity.fu-berlin.de/idp-fub/profile/SAML2/Redirect/SSO", cookies, new Response.Listener<Result>() {
|
||||||
|
@Override
|
||||||
|
public void onResponse(Result response) {
|
||||||
|
HashMap<String, String> object = new HashMap<>();
|
||||||
|
|
||||||
|
Pattern pattern = Pattern.compile("ss:mem:([0-9a-f]+)");
|
||||||
|
Matcher matcher = pattern.matcher(response.getParsed());
|
||||||
|
if (!matcher.find()) {
|
||||||
|
errorCallback.onError(new NetworkError(100142, -1, "Error on getting SAML response!"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
object.put("RelayState", "ss:mem:"+matcher.group(1));
|
||||||
|
|
||||||
|
pattern = Pattern.compile("([0-9a-zA-Z+]+==)");
|
||||||
|
matcher = pattern.matcher(response.getParsed());
|
||||||
|
if (!matcher.find()) {
|
||||||
|
errorCallback.onError(new NetworkError(100141, -1, "Error on getting SAML response!"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
object.put("SAMLResponse", matcher.group(1));
|
||||||
|
|
||||||
|
callback.onResponse(object);
|
||||||
|
}
|
||||||
|
}, new Response.ErrorListener() {
|
||||||
|
@Override
|
||||||
|
public void onErrorResponse(VolleyError error) {
|
||||||
|
errorCallback.onError(new NetworkError(100140, error.networkResponse.statusCode, "Error on getting SAML response!"));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
POST https://kvv.imp.fu-berlin.de/Shibboleth.sso/SAML2/POST
|
||||||
|
<- RelayState 7ea01e29157b8bd906f7002176213b6db5e1f45ebb88716a9820d1a505f2c8bf
|
||||||
|
<- SAMLResponse PD94bWwgdmVyc2lvbj0...........wvc2FtbDJwOlJlc3BvbnNlPg==
|
||||||
|
<- JSESSIONID
|
||||||
|
-> _shibsession_64656661756c7468747470733a2f2f6b76762e696d702e66752d6265726c696e2e64652f73686962626f6c657468
|
||||||
|
_b1912c5a03d733a80bd3fee772bf68d4
|
||||||
|
*/
|
||||||
|
private void loginKVV(String RelayState, String SAMLResponse, String JSESSIONID, final NetworkCallback<HashMap<String, String>> callback, final NetworkErrorCallback errorCallback) {
|
||||||
|
HashMap<String, String> cookies = new HashMap<>();
|
||||||
|
cookies.put("JSESSIONID", JSESSIONID);
|
||||||
|
HashMap<String, String> body = new HashMap<>();
|
||||||
|
body.put("RelayState", RelayState);
|
||||||
|
body.put("SAMLResponse", SAMLResponse);
|
||||||
|
post("https://kvv.imp.fu-berlin.de/Shibboleth.sso/SAML2/POST", cookies, body, new Response.Listener<Result>() {
|
||||||
|
@Override
|
||||||
|
public void onResponse(Result response) {
|
||||||
|
String cookies = response.getHeaders().get("Set-Cookie");
|
||||||
|
if (cookies==null) {
|
||||||
|
errorCallback.onError(new NetworkError(100151, -1, "Error on starting KVV session!"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
HashMap<String, String> object = new HashMap<>();
|
||||||
|
|
||||||
|
|
||||||
|
Pattern pattern = Pattern.compile("(_shibsession_[0-9a-f]+)=([^;]+);");
|
||||||
|
Matcher matcher = pattern.matcher(cookies);
|
||||||
|
if (!matcher.find()) {
|
||||||
|
errorCallback.onError(new NetworkError(100152, -1, "Error on starting Ident session!"));
|
||||||
|
}
|
||||||
|
object.put("shibsessionKey", matcher.group(1));
|
||||||
|
object.put("shibsessionName", matcher.group(2));
|
||||||
|
|
||||||
|
callback.onResponse(object);
|
||||||
|
}
|
||||||
|
}, new Response.ErrorListener() {
|
||||||
|
@Override
|
||||||
|
public void onErrorResponse(VolleyError error) {
|
||||||
|
errorCallback.onError(new NetworkError(100150, error.networkResponse.statusCode, "Error on starting Ident session!"));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
GET https://kvv.imp.fu-berlin.de/sakai-login-tool/container
|
||||||
|
<- JSESSIONID
|
||||||
|
<- _shibsession_64656661756c7468747470733a2f2f6b76762e696d702e66752d6265726c696e2e64652f73686962626f6c657468
|
||||||
|
_b1912c5a03d733a80bd3fee772bf68d4
|
||||||
|
*/
|
||||||
|
private void finishKVVlogin(LoginToken loginToken, final NetworkCallback<HashMap<String, String>> callback, final NetworkErrorCallback errorCallback) {
|
||||||
|
get("https://kvv.imp.fu-berlin.de/sakai-login-tool/container", loginToken.getCookies(), new Response.Listener<Result>() {
|
||||||
|
@Override
|
||||||
|
public void onResponse(Result response) {
|
||||||
|
callback.onResponse(new HashMap<String, String>());
|
||||||
|
}
|
||||||
|
}, new Response.ErrorListener() {
|
||||||
|
@Override
|
||||||
|
public void onErrorResponse(VolleyError error) {
|
||||||
|
errorCallback.onError(new NetworkError(100160, error.networkResponse.statusCode, "Cannot finish login process!"));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
private String getCookie(String cookies, String name) throws NoSuchFieldException {
|
||||||
|
Pattern pattern = Pattern.compile(name+"=([^;]+);");
|
||||||
|
Matcher matcher = pattern.matcher(cookies);
|
||||||
|
if (!matcher.find()) {
|
||||||
|
Log.d("GETcookie failed", name);
|
||||||
|
Log.d("GETcookie failed", cookies);
|
||||||
|
throw new NoSuchFieldException();
|
||||||
|
}
|
||||||
|
return matcher.group(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
private HashMap<String, String> getCookie(String cookies, String[] names) throws NoSuchFieldException {
|
||||||
|
HashMap<String, String> result = new HashMap<>();
|
||||||
|
for (String name: names) {
|
||||||
|
result.put(name,this.getCookie(cookies, name));
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -0,0 +1,208 @@
|
|||||||
|
package de.sebse.fuplanner.services.KVV;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
|
|
||||||
|
import com.android.volley.Response;
|
||||||
|
import com.android.volley.VolleyError;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.NoSuchElementException;
|
||||||
|
import java.util.regex.MatchResult;
|
||||||
|
import java.util.regex.Matcher;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
import de.sebse.fuplanner.tools.network.HTTPService;
|
||||||
|
import de.sebse.fuplanner.tools.network.NetworkCallback;
|
||||||
|
import de.sebse.fuplanner.tools.network.NetworkError;
|
||||||
|
import de.sebse.fuplanner.tools.network.NetworkErrorCallback;
|
||||||
|
import de.sebse.fuplanner.tools.network.Result;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by sebastian on 29.10.17.
|
||||||
|
*/
|
||||||
|
|
||||||
|
public class KVVModuleList extends HTTPService {
|
||||||
|
private final LoginToken token;
|
||||||
|
private Modules moduleList;
|
||||||
|
|
||||||
|
public KVVModuleList(Context context, LoginToken token) {
|
||||||
|
super(context);
|
||||||
|
this.token = token;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void getModuleList(final NetworkCallback<Modules> callback, final NetworkErrorCallback errorCallback) {
|
||||||
|
if (moduleList != null) {
|
||||||
|
callback.onResponse(moduleList);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
get("https://kvv.imp.fu-berlin.de/portal/site/~" + token.getUsername() + "/tool/9df8c796-7d6b-4416-8ba1-f505ea6b8224/", token.getCookies(), new Response.Listener<Result>() {
|
||||||
|
@Override
|
||||||
|
public void onResponse(Result response) {
|
||||||
|
String location = response.getHeaders().get("Location");
|
||||||
|
get("https://kvv.imp.fu-berlin.de/portal/site/~"+token.getUsername()+"/tool/9df8c796-7d6b-4416-8ba1-f505ea6b8224/"+location, token.getCookies(), new Response.Listener<Result>() {
|
||||||
|
@Override
|
||||||
|
public void onResponse(Result response) {
|
||||||
|
String body = response.getParsed();
|
||||||
|
//largeLog("KVVModules", body);
|
||||||
|
Pattern pattern = Pattern.compile("<tbody>(.*)</tbody>", Pattern.DOTALL);
|
||||||
|
Matcher matcher = pattern.matcher(body);
|
||||||
|
if (!matcher.find()) {
|
||||||
|
errorCallback.onError(new NetworkError(101101, -1, "Cannot parse module list!"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
//largeLog("KVVGroup", matcher.group(1));
|
||||||
|
Modules modules = new Modules();
|
||||||
|
//<tr.*?>(.|\n)+?</tr>
|
||||||
|
for (MatchResult match: allMatches(Pattern.compile("<tr.*?>.+?</tr>", Pattern.DOTALL), matcher.group(1))) {
|
||||||
|
try {
|
||||||
|
// Instructors
|
||||||
|
HashSet<String> instructs = new HashSet<>();
|
||||||
|
String instructors = regex("<td class=\"instructors\">.+?<div>(.+?)</div>.+?</td>", match.group());
|
||||||
|
for (MatchResult match2: allMatches(Pattern.compile(".*(?=,)"), instructors)) {
|
||||||
|
if (match2.group()!=null && match2.group().length()>0)
|
||||||
|
instructs.add(match2.group());
|
||||||
|
}
|
||||||
|
for (MatchResult match2: allMatches(Pattern.compile(".*(?=\\s+$|$)"), instructors)) {
|
||||||
|
if (match2.group()!=null && match2.group().length()>0)
|
||||||
|
instructs.add(match2.group());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Semester
|
||||||
|
String semester = regex("<div>((Winter|Sommer)semester.*?)</div>", match.group());
|
||||||
|
|
||||||
|
// lvNumber
|
||||||
|
HashSet<String> lvNumbers = new HashSet<>();
|
||||||
|
String numbers = regex("<td class=\"vvid\">.*?<div>(.*?)</div", match.group());
|
||||||
|
for (MatchResult match2: allMatches(Pattern.compile("\\d+"), numbers)) {
|
||||||
|
if (match2.group()!=null && match2.group().length()>0)
|
||||||
|
lvNumbers.add(match2.group());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Title
|
||||||
|
//language=RegExp
|
||||||
|
String title = regex("<span class=\"fs\"></span>(.+?)</a><span>", match.group());
|
||||||
|
|
||||||
|
// Type
|
||||||
|
//language=RegExp
|
||||||
|
String type = regex("<td class=\"typelong\">.*?<div>(.*?)</div>", match.group());
|
||||||
|
|
||||||
|
// Upgrade URI
|
||||||
|
//language=RegExp
|
||||||
|
String upgradeURI = regex("<a href=\"\\./\\?([0-9]*-[0-9]*\\.ILinkListener-tableform-coursetable-body-rows-[0-9]*-cells-[0-9]*-cell-thelink)\"><span class=\"fs\"></span>.+?</a><span>", match.group());
|
||||||
|
|
||||||
|
modules.addModule(semester, lvNumbers, title, instructs, type, upgradeURI);
|
||||||
|
} catch (NoSuchFieldException e) {
|
||||||
|
errorCallback.onError(new NetworkError(101102, -1, "Cannot parse module list!"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
KVVModuleList.this.moduleList = modules;
|
||||||
|
callback.onResponse(modules);
|
||||||
|
}
|
||||||
|
}, new Response.ErrorListener() {
|
||||||
|
@Override
|
||||||
|
public void onErrorResponse(VolleyError error) {
|
||||||
|
errorCallback.onError(new NetworkError(101102, error.networkResponse.statusCode, "Cannot get module list!"));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, new Response.ErrorListener() {
|
||||||
|
@Override
|
||||||
|
public void onErrorResponse(VolleyError error) {
|
||||||
|
errorCallback.onError(new NetworkError(101100, error.networkResponse.statusCode, "Cannot get module list!"));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public void getModuleDetails(final int index, final NetworkCallback<Modules.UpgradeModule> callback, final NetworkErrorCallback errorCallback) {
|
||||||
|
if (moduleList == null) {
|
||||||
|
this.getModuleList(new NetworkCallback<Modules>() {
|
||||||
|
@Override
|
||||||
|
public void onResponse(@NonNull Modules success) {
|
||||||
|
getModuleDetails(index, callback, errorCallback);
|
||||||
|
}
|
||||||
|
}, errorCallback);
|
||||||
|
} else {
|
||||||
|
Modules.Module m = moduleList.get(index);
|
||||||
|
if (m instanceof Modules.SimpleModule) {
|
||||||
|
upgradeModule((Modules.SimpleModule) m, new NetworkCallback<HashMap<String, String>>() {
|
||||||
|
@Override
|
||||||
|
public void onResponse(@NonNull HashMap<String, String> success) {
|
||||||
|
moduleList.upgradeItem(index, success.get("moduleID"), success.get("description"));
|
||||||
|
callback.onResponse((Modules.UpgradeModule) moduleList.get(index));
|
||||||
|
}
|
||||||
|
}, errorCallback);
|
||||||
|
} else {
|
||||||
|
callback.onResponse((Modules.UpgradeModule) moduleList.get(index));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void upgradeModule(Modules.SimpleModule module, final NetworkCallback<HashMap<String, String>> callback, final NetworkErrorCallback errorCallback) {
|
||||||
|
HashMap<String, String> success = new HashMap<>();
|
||||||
|
success.put("moduleID", "cool ID");
|
||||||
|
success.put("desctiption", "cool desctiption");
|
||||||
|
callback.onResponse(success);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
private String regex(String regex, String match) throws NoSuchFieldException {
|
||||||
|
return regex(regex, match, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String regex(String regex, String match, int group) throws NoSuchFieldException {
|
||||||
|
Pattern pattern = Pattern.compile(regex, Pattern.DOTALL);
|
||||||
|
Matcher matcher = pattern.matcher(match);
|
||||||
|
if (!matcher.find()) {
|
||||||
|
throw new NoSuchFieldException();
|
||||||
|
}
|
||||||
|
return matcher.group(group);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Iterable<MatchResult> allMatches(final Pattern p, final CharSequence input) {
|
||||||
|
return new Iterable<MatchResult>() {
|
||||||
|
@NonNull
|
||||||
|
public Iterator<MatchResult> iterator() {
|
||||||
|
return new Iterator<MatchResult>() {
|
||||||
|
// Use a matcher internally.
|
||||||
|
final Matcher matcher = p.matcher(input);
|
||||||
|
// Keep a match around that supports any interleaving of hasNext/next calls.
|
||||||
|
MatchResult pending;
|
||||||
|
|
||||||
|
public boolean hasNext() {
|
||||||
|
// Lazily fill pending, and avoid calling find() multiple times if the
|
||||||
|
// clients call hasNext() repeatedly before sampling via next().
|
||||||
|
if (pending == null && matcher.find()) {
|
||||||
|
pending = matcher.toMatchResult();
|
||||||
|
}
|
||||||
|
return pending != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public MatchResult next() {
|
||||||
|
// Fill pending if necessary (as when clients call next() without
|
||||||
|
// checking hasNext()), throw if not possible.
|
||||||
|
if (!hasNext()) { throw new NoSuchElementException(); }
|
||||||
|
// Consume pending so next call to hasNext() does a find().
|
||||||
|
MatchResult next = pending;
|
||||||
|
pending = null;
|
||||||
|
return next;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Required to satisfy the interface, but unsupported. */
|
||||||
|
public void remove() { throw new UnsupportedOperationException(); }
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
package de.sebse.fuplanner.services.KVV;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by sebastian on 31.01.18.
|
||||||
|
*/
|
||||||
|
|
||||||
|
interface LastTokenCallback {
|
||||||
|
void onReceived(LoginToken token);
|
||||||
|
}
|
||||||
@@ -0,0 +1,55 @@
|
|||||||
|
package de.sebse.fuplanner.services.KVV;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by sebastian on 29.10.17.
|
||||||
|
*/
|
||||||
|
|
||||||
|
public class LoginToken {
|
||||||
|
private final String username;
|
||||||
|
private final String shibsessionKey;
|
||||||
|
private final String shibsessionName;
|
||||||
|
private String JSESSIONID;
|
||||||
|
|
||||||
|
public LoginToken(String username, String shibsessionKey, String shibsessionName, String JSESSIONID) {
|
||||||
|
this.username = username;
|
||||||
|
this.shibsessionKey = shibsessionKey;
|
||||||
|
this.shibsessionName = shibsessionName;
|
||||||
|
this.JSESSIONID = JSESSIONID;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getUsername() {
|
||||||
|
return username;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getShibsessionKey() {
|
||||||
|
return shibsessionKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getShibsessionName() {
|
||||||
|
return shibsessionName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getJSESSIONID() {
|
||||||
|
return JSESSIONID;
|
||||||
|
}
|
||||||
|
|
||||||
|
public HashMap<String, String> getCookies() {
|
||||||
|
HashMap<String, String> cookies = new HashMap<>();
|
||||||
|
cookies.put("JSESSIONID", getJSESSIONID());
|
||||||
|
cookies.put(getShibsessionKey(), getShibsessionName());
|
||||||
|
cookies.put("pasystem_timezone_ok", "true");
|
||||||
|
return cookies;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
StringBuilder result = new StringBuilder();
|
||||||
|
HashMap<String, String> cookies = this.getCookies();
|
||||||
|
for (String header: cookies.keySet()) {
|
||||||
|
result.append(header).append("=").append(cookies.get(header)).append(";");
|
||||||
|
}
|
||||||
|
return result.substring(0, result.length()-1);
|
||||||
|
}
|
||||||
|
}
|
||||||
132
app/src/main/java/de/sebse/fuplanner/services/KVV/Modules.java
Normal file
@@ -0,0 +1,132 @@
|
|||||||
|
package de.sebse.fuplanner.services.KVV;
|
||||||
|
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Iterator;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by sebastian on 29.10.17.
|
||||||
|
*/
|
||||||
|
|
||||||
|
public class Modules implements Iterable<Modules.Module> {
|
||||||
|
private final ArrayList<Module> list;
|
||||||
|
|
||||||
|
Modules() {
|
||||||
|
this.list = new ArrayList<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
Module addModule(String semester, HashSet<String> lvNumber, String title, HashSet<String> lecturer, String type, String upgradeURI) {
|
||||||
|
Module m = new SimpleModule(semester, lvNumber, title, lecturer, type, upgradeURI);
|
||||||
|
this.list.add(m);
|
||||||
|
return m;
|
||||||
|
}
|
||||||
|
|
||||||
|
UpgradeModule upgradeItem(int index, String moduleID, String description) {
|
||||||
|
Module sm = list.get(index);
|
||||||
|
if (sm instanceof SimpleModule) {
|
||||||
|
UpgradeModule um = new UpgradeModule((SimpleModule) sm, moduleID, description);
|
||||||
|
list.set(index, um);
|
||||||
|
return um;
|
||||||
|
} else {
|
||||||
|
return (UpgradeModule) sm;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
@Override
|
||||||
|
public Iterator<Module> iterator() {
|
||||||
|
return new Iterator<Module>() {
|
||||||
|
private final Iterator<Module> iter = Modules.this.list.iterator();
|
||||||
|
@Override
|
||||||
|
public boolean hasNext() {
|
||||||
|
return iter.hasNext();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Module next() {
|
||||||
|
return iter.next();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void remove() {
|
||||||
|
throw new UnsupportedOperationException("no changes allowed");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public int size() {
|
||||||
|
return this.list.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Module get(int index) {
|
||||||
|
return this.list.get(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract public class Module {
|
||||||
|
public final String semester;
|
||||||
|
public final HashSet<String> lvNumber;
|
||||||
|
public final String title;
|
||||||
|
public final HashSet<String> lecturer;
|
||||||
|
public final String type;
|
||||||
|
|
||||||
|
/*private Module() {
|
||||||
|
this(null, null, null, null, null);
|
||||||
|
throw new AssertionError("Do not use this constructor!");
|
||||||
|
}*/
|
||||||
|
|
||||||
|
private Module(String semester, HashSet<String> lvNumber, String title, HashSet<String> lecturer, String type) {
|
||||||
|
semester = semester.replace("Sommersemester", "SS").replace("Wintersemester", "WS");
|
||||||
|
semester = semester.replaceAll("[0-9]{2}([0-9]{2})", "$1");
|
||||||
|
|
||||||
|
this.semester = semester;
|
||||||
|
this.lvNumber = lvNumber;
|
||||||
|
this.title = title;
|
||||||
|
this.lecturer = lecturer;
|
||||||
|
this.type = type;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "Semester: "+semester+
|
||||||
|
"\nlvNumber: "+lvNumber.toString()+
|
||||||
|
"\ntitle: "+title+
|
||||||
|
"\nlecturer: "+lecturer.toString()+
|
||||||
|
"\ntype: "+type;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class SimpleModule extends Module {
|
||||||
|
public final String upgradeURI;
|
||||||
|
|
||||||
|
private SimpleModule(String semester, HashSet<String> lvNumber, String title, HashSet<String> lecturer, String type, String upgradeURI) {
|
||||||
|
super(semester, lvNumber, title, lecturer, type);
|
||||||
|
this.upgradeURI = upgradeURI;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return super.toString()+
|
||||||
|
"\nupgradeURI: "+upgradeURI;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class UpgradeModule extends Module {
|
||||||
|
public final String moduleID;
|
||||||
|
public final String description;
|
||||||
|
|
||||||
|
private UpgradeModule(SimpleModule module, String moduleID, String description) {
|
||||||
|
super(module.semester, module.lvNumber, module.title, module.lecturer, module.type);
|
||||||
|
this.moduleID = moduleID;
|
||||||
|
this.description = description;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return super.toString()+
|
||||||
|
"\nmoduleID: "+moduleID+
|
||||||
|
"\ndescription: "+description;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
78
app/src/main/java/de/sebse/fuplanner/services/MensaPlan.java
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
package de.sebse.fuplanner.services;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import com.android.volley.AuthFailureError;
|
||||||
|
import com.android.volley.NetworkResponse;
|
||||||
|
import com.android.volley.Request;
|
||||||
|
import com.android.volley.RequestQueue;
|
||||||
|
import com.android.volley.Response;
|
||||||
|
import com.android.volley.VolleyError;
|
||||||
|
import com.android.volley.VolleyLog;
|
||||||
|
import com.android.volley.toolbox.HttpHeaderParser;
|
||||||
|
import com.android.volley.toolbox.StringRequest;
|
||||||
|
import com.android.volley.toolbox.Volley;
|
||||||
|
|
||||||
|
import org.json.JSONException;
|
||||||
|
import org.json.JSONObject;
|
||||||
|
|
||||||
|
import java.io.UnsupportedEncodingException;
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by sebastian on 12.10.17.
|
||||||
|
*/
|
||||||
|
|
||||||
|
public class MensaPlan {
|
||||||
|
|
||||||
|
private final RequestQueue requestQueue;
|
||||||
|
public MensaPlan(Context context) {
|
||||||
|
requestQueue = Volley.newRequestQueue(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void request(int MensaID, Date date) throws JSONException {
|
||||||
|
JSONObject post = new JSONObject();
|
||||||
|
post.put("resources_id", 528);
|
||||||
|
post.put("date", "2017-10-13");
|
||||||
|
final String requestBody = post.toString();
|
||||||
|
|
||||||
|
StringRequest request = new StringRequest(Request.Method.POST, "https://www.stw.berlin/xhr/speiseplan-wochentag.html", new Response.Listener<String>() {
|
||||||
|
@Override
|
||||||
|
public void onResponse(String response) {
|
||||||
|
Log.d("MensaPlan DDDD", response);
|
||||||
|
}
|
||||||
|
}, new Response.ErrorListener() {
|
||||||
|
@Override
|
||||||
|
public void onErrorResponse(VolleyError error) {
|
||||||
|
Log.d("MensaPlan EEEE", error.getMessage());
|
||||||
|
}
|
||||||
|
}) {
|
||||||
|
@Override
|
||||||
|
public String getBodyContentType() {
|
||||||
|
return "application/json; charset=utf-8";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public byte[] getBody() throws AuthFailureError {
|
||||||
|
try {
|
||||||
|
return requestBody == null ? null : requestBody.getBytes("utf-8");
|
||||||
|
} catch (UnsupportedEncodingException uee) {
|
||||||
|
VolleyLog.wtf("Unsupported Encoding while trying to get the bytes of %s using %s", requestBody, "utf-8");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*@Override
|
||||||
|
protected Response<String> parseNetworkResponse(NetworkResponse response) {
|
||||||
|
String responseString = "";
|
||||||
|
responseString = String.valueOf(response.statusCode);
|
||||||
|
// can get more details such as response.headers
|
||||||
|
|
||||||
|
return Response.success(responseString, HttpHeaderParser.parseCacheHeaders(response));
|
||||||
|
}*/
|
||||||
|
};
|
||||||
|
requestQueue.add(request);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
package de.sebse.fuplanner.tools.activity;
|
||||||
|
|
||||||
|
import android.support.v7.app.AppCompatActivity;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by sebastian on 19.04.18.
|
||||||
|
*/
|
||||||
|
|
||||||
|
public class Logger {
|
||||||
|
private final String tag;
|
||||||
|
|
||||||
|
public Logger(String tag) {
|
||||||
|
super();
|
||||||
|
this.tag = tag;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void d(String text) {
|
||||||
|
Log.d(tag, text);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void d(boolean text) {
|
||||||
|
Log.d(tag, String.valueOf(text));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void d(int text) {
|
||||||
|
Log.d(tag, String.valueOf(text));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void e(String text) {
|
||||||
|
Log.e(tag, text);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void e(boolean text) {
|
||||||
|
Log.e(tag, String.valueOf(text));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void e(int text) {
|
||||||
|
Log.e(tag, String.valueOf(text));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,232 @@
|
|||||||
|
package de.sebse.fuplanner.tools.network;
|
||||||
|
|
||||||
|
import com.android.volley.AuthFailureError;
|
||||||
|
import com.android.volley.Request;
|
||||||
|
import com.android.volley.toolbox.HurlStack;
|
||||||
|
|
||||||
|
import org.apache.http.Header;
|
||||||
|
import org.apache.http.HttpEntity;
|
||||||
|
import org.apache.http.HttpResponse;
|
||||||
|
import org.apache.http.ProtocolVersion;
|
||||||
|
import org.apache.http.StatusLine;
|
||||||
|
import org.apache.http.entity.BasicHttpEntity;
|
||||||
|
import org.apache.http.message.BasicHeader;
|
||||||
|
import org.apache.http.message.BasicHttpResponse;
|
||||||
|
import org.apache.http.message.BasicStatusLine;
|
||||||
|
|
||||||
|
import java.io.DataOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.net.HttpURLConnection;
|
||||||
|
import java.net.URL;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.regex.Matcher;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
import javax.net.ssl.HttpsURLConnection;
|
||||||
|
import javax.net.ssl.SSLSocketFactory;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by sebastian on 29.10.17.
|
||||||
|
*/
|
||||||
|
|
||||||
|
public class BetterHurlStack extends HurlStack {
|
||||||
|
private static final String HEADER_CONTENT_TYPE = "Content-Type";
|
||||||
|
|
||||||
|
private final UrlRewriter mUrlRewriter;
|
||||||
|
private final SSLSocketFactory mSslSocketFactory;
|
||||||
|
private boolean followRedirects;
|
||||||
|
|
||||||
|
BetterHurlStack() {
|
||||||
|
this(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
BetterHurlStack(boolean followRedirects) {
|
||||||
|
this(followRedirects, null);
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* @param urlRewriter Rewriter to use for request URLs
|
||||||
|
*/
|
||||||
|
BetterHurlStack(boolean followRedirects, UrlRewriter urlRewriter) {
|
||||||
|
this(followRedirects, urlRewriter, null);
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* @param urlRewriter Rewriter to use for request URLs
|
||||||
|
* @param sslSocketFactory SSL factory to use for HTTPS connections
|
||||||
|
*/
|
||||||
|
BetterHurlStack(boolean followRedirects, UrlRewriter urlRewriter, SSLSocketFactory sslSocketFactory) {
|
||||||
|
super(urlRewriter, sslSocketFactory);
|
||||||
|
mUrlRewriter = urlRewriter;
|
||||||
|
mSslSocketFactory = sslSocketFactory;
|
||||||
|
this.followRedirects = followRedirects;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
protected HttpURLConnection createConnection(URL url) throws IOException {
|
||||||
|
HttpURLConnection connection = super.createConnection(url);
|
||||||
|
connection.setInstanceFollowRedirects(false);
|
||||||
|
|
||||||
|
return connection;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public HttpResponse performRequest(Request<?> request, Map<String, String> additionalHeaders)
|
||||||
|
throws IOException, AuthFailureError {
|
||||||
|
String url = request.getUrl();
|
||||||
|
HashMap<String, String> map = new HashMap<String, String>();
|
||||||
|
map.putAll(request.getHeaders());
|
||||||
|
map.putAll(additionalHeaders);
|
||||||
|
if (mUrlRewriter != null) {
|
||||||
|
String rewritten = mUrlRewriter.rewriteUrl(url);
|
||||||
|
if (rewritten == null) {
|
||||||
|
throw new IOException("URL blocked by rewriter: " + url);
|
||||||
|
}
|
||||||
|
url = rewritten;
|
||||||
|
}
|
||||||
|
URL parsedUrl = new URL(url);
|
||||||
|
HttpURLConnection connection = openConnection(parsedUrl, request);
|
||||||
|
for (String headerName : map.keySet()) {
|
||||||
|
connection.addRequestProperty(headerName, map.get(headerName));
|
||||||
|
}
|
||||||
|
setConnectionParametersForRequest(connection, request);
|
||||||
|
// Initialize HttpResponse with data from the HttpURLConnection.
|
||||||
|
ProtocolVersion protocolVersion = new ProtocolVersion("HTTP", 1, 1);
|
||||||
|
int responseCode = connection.getResponseCode();
|
||||||
|
if (responseCode == -1) {
|
||||||
|
// -1 is returned by getResponseCode() if the response code could not be retrieved.
|
||||||
|
// Signal to the caller that something was wrong with the connection.
|
||||||
|
throw new IOException("Could not retrieve response code from HttpUrlConnection.");
|
||||||
|
}
|
||||||
|
StatusLine responseStatus = new BasicStatusLine(protocolVersion,
|
||||||
|
connection.getResponseCode(), connection.getResponseMessage());
|
||||||
|
BasicHttpResponse response = new BasicHttpResponse(responseStatus);
|
||||||
|
response.setEntity(entityFromConnection(connection));
|
||||||
|
for (Map.Entry<String, List<String>> header : connection.getHeaderFields().entrySet()) {
|
||||||
|
if (header.getKey() != null) {
|
||||||
|
Header h;
|
||||||
|
if (header.getKey().equals("Set-Cookie")) {
|
||||||
|
Pattern pattern = Pattern.compile("^([^=]+=[^;]+;)");
|
||||||
|
StringBuilder cookieValue = new StringBuilder();
|
||||||
|
for (String value: header.getValue()) {
|
||||||
|
Matcher matcher = pattern.matcher(value);
|
||||||
|
if (matcher.find()) {
|
||||||
|
cookieValue.append(matcher.group(1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
h = new BasicHeader(header.getKey(), cookieValue.toString());
|
||||||
|
} else {
|
||||||
|
h = new BasicHeader(header.getKey(), header.getValue().get(0));
|
||||||
|
}
|
||||||
|
response.addHeader(h);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initializes an {@link HttpEntity} from the given {@link HttpURLConnection}.
|
||||||
|
* @param connection
|
||||||
|
* @return an HttpEntity populated with data from <code>connection</code>.
|
||||||
|
*/
|
||||||
|
private static HttpEntity entityFromConnection(HttpURLConnection connection) {
|
||||||
|
BasicHttpEntity entity = new BasicHttpEntity();
|
||||||
|
InputStream inputStream;
|
||||||
|
try {
|
||||||
|
inputStream = connection.getInputStream();
|
||||||
|
} catch (IOException ioe) {
|
||||||
|
inputStream = connection.getErrorStream();
|
||||||
|
}
|
||||||
|
entity.setContent(inputStream);
|
||||||
|
entity.setContentLength(connection.getContentLength());
|
||||||
|
entity.setContentEncoding(connection.getContentEncoding());
|
||||||
|
entity.setContentType(connection.getContentType());
|
||||||
|
return entity;
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("deprecation")
|
||||||
|
/* package */ static void setConnectionParametersForRequest(HttpURLConnection connection,
|
||||||
|
Request<?> request) throws IOException, AuthFailureError {
|
||||||
|
switch (request.getMethod()) {
|
||||||
|
case Request.Method.DEPRECATED_GET_OR_POST:
|
||||||
|
// This is the deprecated way that needs to be handled for backwards compatibility.
|
||||||
|
// If the request's post body is null, then the assumption is that the request is
|
||||||
|
// GET. Otherwise, it is assumed that the request is a POST.
|
||||||
|
byte[] postBody = request.getPostBody();
|
||||||
|
if (postBody != null) {
|
||||||
|
// Prepare output. There is no need to set Content-Length explicitly,
|
||||||
|
// since this is handled by HttpURLConnection using the size of the prepared
|
||||||
|
// output stream.
|
||||||
|
connection.setDoOutput(true);
|
||||||
|
connection.setRequestMethod("POST");
|
||||||
|
connection.addRequestProperty(HEADER_CONTENT_TYPE,
|
||||||
|
request.getPostBodyContentType());
|
||||||
|
DataOutputStream out = new DataOutputStream(connection.getOutputStream());
|
||||||
|
out.write(postBody);
|
||||||
|
out.close();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case Request.Method.GET:
|
||||||
|
// Not necessary to set the request method because connection defaults to GET but
|
||||||
|
// being explicit here.
|
||||||
|
connection.setRequestMethod("GET");
|
||||||
|
break;
|
||||||
|
case Request.Method.DELETE:
|
||||||
|
connection.setRequestMethod("DELETE");
|
||||||
|
break;
|
||||||
|
case Request.Method.POST:
|
||||||
|
connection.setRequestMethod("POST");
|
||||||
|
addBodyIfExists(connection, request);
|
||||||
|
break;
|
||||||
|
case Request.Method.PUT:
|
||||||
|
connection.setRequestMethod("PUT");
|
||||||
|
addBodyIfExists(connection, request);
|
||||||
|
break;
|
||||||
|
case Request.Method.HEAD:
|
||||||
|
connection.setRequestMethod("HEAD");
|
||||||
|
break;
|
||||||
|
case Request.Method.OPTIONS:
|
||||||
|
connection.setRequestMethod("OPTIONS");
|
||||||
|
break;
|
||||||
|
case Request.Method.TRACE:
|
||||||
|
connection.setRequestMethod("TRACE");
|
||||||
|
break;
|
||||||
|
case Request.Method.PATCH:
|
||||||
|
connection.setRequestMethod("PATCH");
|
||||||
|
addBodyIfExists(connection, request);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new IllegalStateException("Unknown method type.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void addBodyIfExists(HttpURLConnection connection, Request<?> request)
|
||||||
|
throws IOException, AuthFailureError {
|
||||||
|
byte[] body = request.getBody();
|
||||||
|
if (body != null) {
|
||||||
|
connection.setDoOutput(true);
|
||||||
|
connection.addRequestProperty(HEADER_CONTENT_TYPE, request.getBodyContentType());
|
||||||
|
DataOutputStream out = new DataOutputStream(connection.getOutputStream());
|
||||||
|
out.write(body);
|
||||||
|
out.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private HttpURLConnection openConnection(URL url, Request<?> request) throws IOException {
|
||||||
|
HttpURLConnection connection = createConnection(url);
|
||||||
|
int timeoutMs = request.getTimeoutMs();
|
||||||
|
connection.setConnectTimeout(timeoutMs);
|
||||||
|
connection.setReadTimeout(timeoutMs);
|
||||||
|
connection.setUseCaches(false);
|
||||||
|
connection.setDoInput(true);
|
||||||
|
// use caller-provided custom SslSocketFactory, if any, for HTTPS
|
||||||
|
if ("https".equals(url.getProtocol()) && mSslSocketFactory != null) {
|
||||||
|
((HttpsURLConnection)connection).setSSLSocketFactory(mSslSocketFactory);
|
||||||
|
}
|
||||||
|
return connection;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,143 @@
|
|||||||
|
package de.sebse.fuplanner.tools.network;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.support.annotation.Nullable;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import com.android.volley.AuthFailureError;
|
||||||
|
import com.android.volley.Request;
|
||||||
|
import com.android.volley.RequestQueue;
|
||||||
|
import com.android.volley.Response;
|
||||||
|
import com.android.volley.VolleyError;
|
||||||
|
import com.android.volley.toolbox.Volley;
|
||||||
|
|
||||||
|
import java.io.UnsupportedEncodingException;
|
||||||
|
import java.net.URLEncoder;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by sebastian on 24.10.17.
|
||||||
|
*/
|
||||||
|
|
||||||
|
public class HTTPService {
|
||||||
|
protected RequestQueue requestQueue;
|
||||||
|
private boolean followRedirects;
|
||||||
|
|
||||||
|
public HTTPService(Context context) {
|
||||||
|
this(context, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public HTTPService(Context context, boolean followRedirects) {
|
||||||
|
this.followRedirects = followRedirects;
|
||||||
|
requestQueue = Volley.newRequestQueue(context, new BetterHurlStack(followRedirects));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void get(String url, @Nullable final HashMap<String, String> cookies, Response.Listener<Result> response, Response.ErrorListener error) {
|
||||||
|
HttpRequest request = new HttpRequest(Request.Method.GET, url, response, error) {
|
||||||
|
@Override
|
||||||
|
public void deliverError(VolleyError error) {
|
||||||
|
if (error == null) {
|
||||||
|
super.deliverError(null);
|
||||||
|
} else {
|
||||||
|
final int status = error.networkResponse.statusCode;
|
||||||
|
if (status == 302 && !followRedirects) {
|
||||||
|
super.deliverResponse(new Result(null, error.networkResponse.headers));
|
||||||
|
} else {
|
||||||
|
super.deliverError(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Map<String, String> getHeaders() throws AuthFailureError {
|
||||||
|
Map<String, String> params = super.getHeaders();
|
||||||
|
if (cookies != null) {
|
||||||
|
if (params==null)
|
||||||
|
params = new HashMap<>();
|
||||||
|
else
|
||||||
|
params = new HashMap<>(params);
|
||||||
|
StringBuilder newStr = new StringBuilder();
|
||||||
|
for (String key : cookies.keySet())
|
||||||
|
newStr.append(key).append("=").append(cookies.get(key)).append(";");
|
||||||
|
newStr = new StringBuilder(newStr.substring(0, newStr.length() - 1));
|
||||||
|
params.put("Cookie", newStr.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
return params;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
requestQueue.add(request);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void post(String url, @Nullable final HashMap<String, String> cookies, @Nullable final HashMap<String, String> body, Response.Listener<Result> response, Response.ErrorListener error) {
|
||||||
|
HttpRequest request = new HttpRequest(Request.Method.POST, url, response, error) {
|
||||||
|
@Override
|
||||||
|
public String getBodyContentType() {
|
||||||
|
return "application/x-www-form-urlencoded";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public byte[] getBody() throws AuthFailureError {
|
||||||
|
if (body==null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
for(HashMap.Entry<String, String> e: body.entrySet()){
|
||||||
|
if(sb.length() > 0){
|
||||||
|
sb.append('&');
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
Log.e("Superissimo", e.getKey()+"|||"+e.getValue());
|
||||||
|
sb.append(URLEncoder.encode(e.getKey(), "UTF-8")).append('=').append(URLEncoder.encode(e.getValue(), "UTF-8"));
|
||||||
|
} catch (UnsupportedEncodingException ignored) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
String requestBody = sb.toString();
|
||||||
|
try {
|
||||||
|
return requestBody.getBytes("utf-8");
|
||||||
|
} catch (UnsupportedEncodingException e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void deliverError(VolleyError error) {
|
||||||
|
final int status = error.networkResponse.statusCode;
|
||||||
|
if (status == 302) {
|
||||||
|
super.deliverResponse(new Result(null, error.networkResponse.headers));
|
||||||
|
} else {
|
||||||
|
super.deliverError(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Map<String, String> getHeaders() throws AuthFailureError {
|
||||||
|
Map<String, String> params = super.getHeaders();
|
||||||
|
if (cookies != null) {
|
||||||
|
if (params==null)
|
||||||
|
params = new HashMap<>();
|
||||||
|
else
|
||||||
|
params = new HashMap<>(params);
|
||||||
|
StringBuilder newStr = new StringBuilder();
|
||||||
|
for (String key : cookies.keySet())
|
||||||
|
newStr.append(key).append("=").append(cookies.get(key)).append(";");
|
||||||
|
newStr = new StringBuilder(newStr.substring(0, newStr.length() - 1));
|
||||||
|
params.put("Cookie", newStr.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
return params;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
requestQueue.add(request);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void largeLog(String tag, String content) {
|
||||||
|
if (content==null) {
|
||||||
|
Log.d(tag, "null");
|
||||||
|
} else if (content.length() > 4000) {
|
||||||
|
Log.d(tag, content.substring(0, 4000));
|
||||||
|
largeLog(tag, content.substring(4000));
|
||||||
|
} else {
|
||||||
|
Log.d(tag, content);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
package de.sebse.fuplanner.tools.network;
|
||||||
|
|
||||||
|
import com.android.volley.NetworkResponse;
|
||||||
|
import com.android.volley.Request;
|
||||||
|
import com.android.volley.Response;
|
||||||
|
import com.android.volley.toolbox.HttpHeaderParser;
|
||||||
|
|
||||||
|
import java.io.UnsupportedEncodingException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by sebastian on 24.10.17.
|
||||||
|
*/
|
||||||
|
|
||||||
|
public class HttpRequest extends Request<Result> {
|
||||||
|
private final Response.Listener<Result> mListener;
|
||||||
|
|
||||||
|
public HttpRequest(int method, String url, Response.Listener<Result> listener,
|
||||||
|
Response.ErrorListener errorListener) {
|
||||||
|
super(method, url, errorListener);
|
||||||
|
mListener = listener;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Response<Result> parseNetworkResponse(NetworkResponse response) {
|
||||||
|
String parsed;
|
||||||
|
try {
|
||||||
|
parsed = new String(response.data, HttpHeaderParser.parseCharset(response.headers));
|
||||||
|
} catch (UnsupportedEncodingException e) {
|
||||||
|
parsed = new String(response.data);
|
||||||
|
}
|
||||||
|
Result result = new Result(parsed, response.headers);
|
||||||
|
|
||||||
|
return Response.success(result, HttpHeaderParser.parseCacheHeaders(response));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void deliverResponse(Result response) {
|
||||||
|
mListener.onResponse(response);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
package de.sebse.fuplanner.tools.network;
|
||||||
|
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
|
|
||||||
|
public interface NetworkCallback<T> {
|
||||||
|
void onResponse(@NonNull T success);
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
package de.sebse.fuplanner.tools.network;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by sebastian on 24.10.17.
|
||||||
|
*/
|
||||||
|
|
||||||
|
public class NetworkError {
|
||||||
|
private final int code;
|
||||||
|
private final int httpStatus;
|
||||||
|
private final String message;
|
||||||
|
|
||||||
|
public NetworkError(int code, int httpStatus, String message) {
|
||||||
|
this.code = code;
|
||||||
|
this.httpStatus = httpStatus;
|
||||||
|
this.message = message;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getCode() {
|
||||||
|
return code;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getHttpStatus() {
|
||||||
|
return httpStatus;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getMessage() {
|
||||||
|
return message;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String toString() {
|
||||||
|
return String.valueOf(getCode()) + " - " + getHttpStatus() + " - " + getMessage();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
package de.sebse.fuplanner.tools.network;
|
||||||
|
|
||||||
|
public interface NetworkErrorCallback<T> {
|
||||||
|
void onError(NetworkError error);
|
||||||
|
}
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
package de.sebse.fuplanner.tools.network;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by sebastian on 24.10.17.
|
||||||
|
*/
|
||||||
|
public class Result {
|
||||||
|
private final String parsed;
|
||||||
|
private final Map<String, String> headers;
|
||||||
|
|
||||||
|
public Result(String parsed, Map<String, String> headers) {
|
||||||
|
this.parsed = parsed;
|
||||||
|
this.headers = headers;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getParsed() {
|
||||||
|
return parsed;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Map<String, String> getHeaders() {
|
||||||
|
return headers;
|
||||||
|
}
|
||||||
|
}
|
||||||
12
app/src/main/res/drawable-v21/ic_menu_camera.xml
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
<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>
|
||||||
9
app/src/main/res/drawable-v21/ic_menu_gallery.xml
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
<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>
|
||||||
9
app/src/main/res/drawable-v21/ic_menu_manage.xml
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
<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>
|
||||||
9
app/src/main/res/drawable-v21/ic_menu_send.xml
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
<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>
|
||||||
9
app/src/main/res/drawable-v21/ic_menu_share.xml
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
<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>
|
||||||
9
app/src/main/res/drawable-v21/ic_menu_slideshow.xml
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
<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>
|
||||||
9
app/src/main/res/drawable/ic_apps.xml
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
<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="M4,8h4L8,4L4,4v4zM10,20h4v-4h-4v4zM4,20h4v-4L4,16v4zM4,14h4v-4L4,10v4zM10,14h4v-4h-4v4zM16,4v4h4L20,4h-4zM10,8h4L14,4h-4v4zM16,14h4v-4h-4v4zM16,20h4v-4h-4v4z"/>
|
||||||
|
</vector>
|
||||||
9
app/src/main/res/drawable/ic_event.xml
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
<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,12h-5v5h5v-5zM16,1v2L8,3L8,1L6,1v2L5,3c-1.11,0 -1.99,0.9 -1.99,2L3,19c0,1.1 0.89,2 2,2h14c1.1,0 2,-0.9 2,-2L21,5c0,-1.1 -0.9,-2 -2,-2h-1L18,1h-2zM19,19L5,19L5,8h14v11z"/>
|
||||||
|
</vector>
|
||||||
9
app/src/main/res/drawable/ic_exit_to_app.xml
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
<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="M10.09,15.59L11.5,17l5,-5 -5,-5 -1.41,1.41L12.67,11H3v2h9.67l-2.58,2.59zM19,3H5c-1.11,0 -2,0.9 -2,2v4h2V5h14v14H5v-4H3v4c0,1.1 0.89,2 2,2h14c1.1,0 2,-0.9 2,-2V5c0,-1.1 -0.9,-2 -2,-2z"/>
|
||||||
|
</vector>
|
||||||
74
app/src/main/res/drawable/ic_launcher_background.xml
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<vector
|
||||||
|
android:height="108dp"
|
||||||
|
android:width="108dp"
|
||||||
|
android:viewportHeight="108"
|
||||||
|
android:viewportWidth="108"
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<path android:fillColor="#26A69A"
|
||||||
|
android:pathData="M0,0h108v108h-108z"/>
|
||||||
|
<path android:fillColor="#00000000" android:pathData="M9,0L9,108"
|
||||||
|
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||||
|
<path android:fillColor="#00000000" android:pathData="M19,0L19,108"
|
||||||
|
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||||
|
<path android:fillColor="#00000000" android:pathData="M29,0L29,108"
|
||||||
|
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||||
|
<path android:fillColor="#00000000" android:pathData="M39,0L39,108"
|
||||||
|
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||||
|
<path android:fillColor="#00000000" android:pathData="M49,0L49,108"
|
||||||
|
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||||
|
<path android:fillColor="#00000000" android:pathData="M59,0L59,108"
|
||||||
|
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||||
|
<path android:fillColor="#00000000" android:pathData="M69,0L69,108"
|
||||||
|
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||||
|
<path android:fillColor="#00000000" android:pathData="M79,0L79,108"
|
||||||
|
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||||
|
<path android:fillColor="#00000000" android:pathData="M89,0L89,108"
|
||||||
|
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||||
|
<path android:fillColor="#00000000" android:pathData="M99,0L99,108"
|
||||||
|
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||||
|
<path android:fillColor="#00000000" android:pathData="M0,9L108,9"
|
||||||
|
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||||
|
<path android:fillColor="#00000000" android:pathData="M0,19L108,19"
|
||||||
|
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||||
|
<path android:fillColor="#00000000" android:pathData="M0,29L108,29"
|
||||||
|
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||||
|
<path android:fillColor="#00000000" android:pathData="M0,39L108,39"
|
||||||
|
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||||
|
<path android:fillColor="#00000000" android:pathData="M0,49L108,49"
|
||||||
|
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||||
|
<path android:fillColor="#00000000" android:pathData="M0,59L108,59"
|
||||||
|
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||||
|
<path android:fillColor="#00000000" android:pathData="M0,69L108,69"
|
||||||
|
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||||
|
<path android:fillColor="#00000000" android:pathData="M0,79L108,79"
|
||||||
|
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||||
|
<path android:fillColor="#00000000" android:pathData="M0,89L108,89"
|
||||||
|
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||||
|
<path android:fillColor="#00000000" android:pathData="M0,99L108,99"
|
||||||
|
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||||
|
<path android:fillColor="#00000000" android:pathData="M19,29L89,29"
|
||||||
|
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||||
|
<path android:fillColor="#00000000" android:pathData="M19,39L89,39"
|
||||||
|
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||||
|
<path android:fillColor="#00000000" android:pathData="M19,49L89,49"
|
||||||
|
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||||
|
<path android:fillColor="#00000000" android:pathData="M19,59L89,59"
|
||||||
|
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||||
|
<path android:fillColor="#00000000" android:pathData="M19,69L89,69"
|
||||||
|
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||||
|
<path android:fillColor="#00000000" android:pathData="M19,79L89,79"
|
||||||
|
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||||
|
<path android:fillColor="#00000000" android:pathData="M29,19L29,89"
|
||||||
|
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||||
|
<path android:fillColor="#00000000" android:pathData="M39,19L39,89"
|
||||||
|
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||||
|
<path android:fillColor="#00000000" android:pathData="M49,19L49,89"
|
||||||
|
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||||
|
<path android:fillColor="#00000000" android:pathData="M59,19L59,89"
|
||||||
|
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||||
|
<path android:fillColor="#00000000" android:pathData="M69,19L69,89"
|
||||||
|
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||||
|
<path android:fillColor="#00000000" android:pathData="M79,19L79,89"
|
||||||
|
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||||
|
</vector>
|
||||||
9
app/src/main/res/drawable/ic_local_dining.xml
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
<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="M8.1,13.34l2.83,-2.83L3.91,3.5c-1.56,1.56 -1.56,4.09 0,5.66l4.19,4.18zM14.88,11.53c1.53,0.71 3.68,0.21 5.27,-1.38 1.91,-1.91 2.28,-4.65 0.81,-6.12 -1.46,-1.46 -4.2,-1.1 -6.12,0.81 -1.59,1.59 -2.09,3.74 -1.38,5.27L3.7,19.87l1.41,1.41L12,14.41l6.88,6.88 1.41,-1.41L13.41,13l1.47,-1.47z"/>
|
||||||
|
</vector>
|
||||||
9
app/src/main/res/drawable/ic_schedule.xml
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
<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="M11.99,2C6.47,2 2,6.48 2,12s4.47,10 9.99,10C17.52,22 22,17.52 22,12S17.52,2 11.99,2zM12,20c-4.42,0 -8,-3.58 -8,-8s3.58,-8 8,-8 8,3.58 8,8 -3.58,8 -8,8zM12.5,7L11,7v6l5.25,3.15 0.75,-1.23 -4.5,-2.67z"/>
|
||||||
|
</vector>
|
||||||
9
app/src/main/res/drawable/ic_settings.xml
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
<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="M19.43,12.98c0.04,-0.32 0.07,-0.64 0.07,-0.98s-0.03,-0.66 -0.07,-0.98l2.11,-1.65c0.19,-0.15 0.24,-0.42 0.12,-0.64l-2,-3.46c-0.12,-0.22 -0.39,-0.3 -0.61,-0.22l-2.49,1c-0.52,-0.4 -1.08,-0.73 -1.69,-0.98l-0.38,-2.65C14.46,2.18 14.25,2 14,2h-4c-0.25,0 -0.46,0.18 -0.49,0.42l-0.38,2.65c-0.61,0.25 -1.17,0.59 -1.69,0.98l-2.49,-1c-0.23,-0.09 -0.49,0 -0.61,0.22l-2,3.46c-0.13,0.22 -0.07,0.49 0.12,0.64l2.11,1.65c-0.04,0.32 -0.07,0.65 -0.07,0.98s0.03,0.66 0.07,0.98l-2.11,1.65c-0.19,0.15 -0.24,0.42 -0.12,0.64l2,3.46c0.12,0.22 0.39,0.3 0.61,0.22l2.49,-1c0.52,0.4 1.08,0.73 1.69,0.98l0.38,2.65c0.03,0.24 0.24,0.42 0.49,0.42h4c0.25,0 0.46,-0.18 0.49,-0.42l0.38,-2.65c0.61,-0.25 1.17,-0.59 1.69,-0.98l2.49,1c0.23,0.09 0.49,0 0.61,-0.22l2,-3.46c0.12,-0.22 0.07,-0.49 -0.12,-0.64l-2.11,-1.65zM12,15.5c-1.93,0 -3.5,-1.57 -3.5,-3.5s1.57,-3.5 3.5,-3.5 3.5,1.57 3.5,3.5 -1.57,3.5 -3.5,3.5z"/>
|
||||||
|
</vector>
|
||||||
9
app/src/main/res/drawable/side_nav_bar.xml
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:shape="rectangle">
|
||||||
|
<gradient
|
||||||
|
android:angle="135"
|
||||||
|
android:centerColor="#4CAF50"
|
||||||
|
android:endColor="#2E7D32"
|
||||||
|
android:startColor="#81C784"
|
||||||
|
android:type="linear" />
|
||||||
|
</shape>
|
||||||
25
app/src/main/res/layout/activity_main.xml
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
<?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_main"
|
||||||
|
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_main"
|
||||||
|
app:menu="@menu/activity_main_drawer" />
|
||||||
|
|
||||||
|
</android.support.v4.widget.DrawerLayout>
|
||||||
31
app/src/main/res/layout/app_bar_main.xml
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
<?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"
|
||||||
|
tools:context="de.sebse.fuplanner.MainActivity">
|
||||||
|
|
||||||
|
<android.support.design.widget.AppBarLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:id="@+id/app_bar_layout"
|
||||||
|
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="@color/colorFUPrimary"
|
||||||
|
app:popupTheme="@style/AppTheme.PopupOverlay" />
|
||||||
|
|
||||||
|
</android.support.design.widget.AppBarLayout>
|
||||||
|
|
||||||
|
<FrameLayout
|
||||||
|
android:id="@+id/fragcontainer"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
app:layout_behavior="@string/appbar_scrolling_view_behavior">
|
||||||
|
</FrameLayout>
|
||||||
|
|
||||||
|
</android.support.design.widget.CoordinatorLayout>
|
||||||
59
app/src/main/res/layout/fragment_login.xml
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
<ScrollView 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:fitsSystemWindows="true"
|
||||||
|
tools:context="de.sebse.fuplanner.fragments.LoginFragment">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:paddingTop="56dp"
|
||||||
|
android:paddingLeft="24dp"
|
||||||
|
android:paddingRight="24dp">
|
||||||
|
|
||||||
|
<ImageView android:src="@mipmap/ic_launcher"
|
||||||
|
android:layout_width="128dp"
|
||||||
|
android:layout_height="128dp"
|
||||||
|
android:layout_marginBottom="24dp"
|
||||||
|
android:layout_gravity="center_horizontal" />
|
||||||
|
|
||||||
|
<!-- Email Label -->
|
||||||
|
<android.support.design.widget.TextInputLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="8dp"
|
||||||
|
android:layout_marginBottom="8dp">
|
||||||
|
<EditText android:id="@+id/input_username"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:inputType="textEmailAddress"
|
||||||
|
android:hint="Username" />
|
||||||
|
</android.support.design.widget.TextInputLayout>
|
||||||
|
|
||||||
|
<!-- Password Label -->
|
||||||
|
<android.support.design.widget.TextInputLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="8dp"
|
||||||
|
android:layout_marginBottom="8dp">
|
||||||
|
<EditText android:id="@+id/input_password"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:inputType="textPassword"
|
||||||
|
android:hint="Password"/>
|
||||||
|
</android.support.design.widget.TextInputLayout>
|
||||||
|
|
||||||
|
<android.support.v7.widget.AppCompatButton
|
||||||
|
android:id="@+id/btn_login"
|
||||||
|
android:layout_width="fill_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="24dp"
|
||||||
|
android:layout_marginBottom="24dp"
|
||||||
|
android:padding="12dp"
|
||||||
|
android:text="Login"/>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
</ScrollView>
|
||||||
20
app/src/main/res/layout/fragment_modules.xml
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="horizontal">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/id"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_margin="@dimen/text_margin"
|
||||||
|
android:textAppearance="?attr/textAppearanceListItem" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/content"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_margin="@dimen/text_margin"
|
||||||
|
android:textAppearance="?attr/textAppearanceListItem" />
|
||||||
|
</LinearLayout>
|
||||||
13
app/src/main/res/layout/fragment_modules_list.xml
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<android.support.v7.widget.RecyclerView 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/list"
|
||||||
|
android:name="de.sebse.fuplanner.fragments.ModulesFragment"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:layout_marginLeft="16dp"
|
||||||
|
android:layout_marginRight="16dp"
|
||||||
|
app:layoutManager="LinearLayoutManager"
|
||||||
|
tools:context="de.sebse.fuplanner.fragments.ModulesFragment"
|
||||||
|
tools:listitem="@layout/fragment_modules" />
|
||||||
15
app/src/main/res/layout/fragment_startup.xml
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
<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"
|
||||||
|
android:background="@color/colorFUPrimary"
|
||||||
|
android:fitsSystemWindows="true"
|
||||||
|
tools:context="de.sebse.fuplanner.fragments.StartupFragment">
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:layout_width="@android:dimen/thumbnail_width"
|
||||||
|
android:layout_height="@android:dimen/thumbnail_height"
|
||||||
|
android:layout_gravity="center"
|
||||||
|
android:src="@mipmap/ic_launcher" />
|
||||||
|
|
||||||
|
</FrameLayout>
|
||||||
40
app/src/main/res/layout/nav_header_main.xml
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<LinearLayout 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="@dimen/nav_header_height"
|
||||||
|
android:background="@drawable/side_nav_bar"
|
||||||
|
android:gravity="bottom"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:paddingBottom="@dimen/activity_vertical_margin"
|
||||||
|
android:paddingLeft="@dimen/activity_horizontal_margin"
|
||||||
|
android:paddingRight="@dimen/activity_horizontal_margin"
|
||||||
|
android:paddingTop="@dimen/activity_vertical_margin"
|
||||||
|
android:theme="@style/ThemeOverlay.AppCompat.Dark">
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/login_page"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
style="@style/Widget.AppCompat.Button.Colored"
|
||||||
|
android:text="Login"
|
||||||
|
android:visibility="gone" />
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/imageView"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
app:srcCompat="@mipmap/ic_launcher"
|
||||||
|
android:visibility="gone" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/login_name"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:paddingTop="@dimen/nav_header_vertical_spacing"
|
||||||
|
android:text="Android Studio"
|
||||||
|
android:textAppearance="@style/TextAppearance.AppCompat.Body1"
|
||||||
|
android:visibility="gone" />
|
||||||
|
</LinearLayout>
|
||||||
29
app/src/main/res/layout/nav_header_main_login.xml
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<LinearLayout 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="@dimen/nav_header_height"
|
||||||
|
android:background="@drawable/side_nav_bar"
|
||||||
|
android:gravity="bottom"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:paddingBottom="@dimen/activity_vertical_margin"
|
||||||
|
android:paddingLeft="@dimen/activity_horizontal_margin"
|
||||||
|
android:paddingRight="@dimen/activity_horizontal_margin"
|
||||||
|
android:paddingTop="@dimen/activity_vertical_margin"
|
||||||
|
android:theme="@style/ThemeOverlay.AppCompat.Dark">
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/imageView"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
app:srcCompat="@mipmap/ic_launcher" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/login_name"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:paddingTop="@dimen/nav_header_vertical_spacing"
|
||||||
|
android:text="Android Studio"
|
||||||
|
android:textAppearance="@style/TextAppearance.AppCompat.Body1" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
20
app/src/main/res/menu/activity_main_drawer.xml
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<menu xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
|
||||||
|
<group android:checkableBehavior="single">
|
||||||
|
<item
|
||||||
|
android:id="@+id/nav_dining"
|
||||||
|
android:icon="@drawable/ic_local_dining"
|
||||||
|
android:title="Mensaplan" />
|
||||||
|
</group>
|
||||||
|
|
||||||
|
<item android:title="Optionen">
|
||||||
|
<menu>
|
||||||
|
<item
|
||||||
|
android:id="@+id/nav_share"
|
||||||
|
android:icon="@drawable/ic_menu_share"
|
||||||
|
android:title="Teilen"/>
|
||||||
|
</menu>
|
||||||
|
</item>
|
||||||
|
|
||||||
|
</menu>
|
||||||
43
app/src/main/res/menu/activity_main_drawer_login.xml
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<menu xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
|
||||||
|
<group android:checkableBehavior="single">
|
||||||
|
<item
|
||||||
|
android:id="@+id/nav_schedule"
|
||||||
|
android:icon="@drawable/ic_event"
|
||||||
|
android:title="Stundenplan"
|
||||||
|
android:orderInCategory="10"/>
|
||||||
|
<item
|
||||||
|
android:id="@+id/nav_modules"
|
||||||
|
android:icon="@drawable/ic_apps"
|
||||||
|
android:title="Module"
|
||||||
|
android:orderInCategory="100" />
|
||||||
|
<item
|
||||||
|
android:id="@+id/nav_dining"
|
||||||
|
android:icon="@drawable/ic_local_dining"
|
||||||
|
android:title="Mensaplan"
|
||||||
|
android:orderInCategory="200"/>
|
||||||
|
<item
|
||||||
|
android:id="@+id/nav_settings"
|
||||||
|
android:icon="@drawable/ic_settings"
|
||||||
|
android:title="Einstellungen"
|
||||||
|
android:orderInCategory="300"/>
|
||||||
|
</group>
|
||||||
|
|
||||||
|
<item android:title="Optionen"
|
||||||
|
android:orderInCategory="499">
|
||||||
|
<menu>
|
||||||
|
<item
|
||||||
|
android:id="@+id/nav_logout"
|
||||||
|
android:icon="@drawable/ic_exit_to_app"
|
||||||
|
android:title="Logout"
|
||||||
|
android:orderInCategory="500"/>
|
||||||
|
<item
|
||||||
|
android:id="@+id/nav_share"
|
||||||
|
android:icon="@drawable/ic_menu_share"
|
||||||
|
android:title="Teilen"
|
||||||
|
android:orderInCategory="600"/>
|
||||||
|
</menu>
|
||||||
|
</item>
|
||||||
|
|
||||||
|
</menu>
|
||||||
9
app/src/main/res/menu/main.xml
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
<?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>
|
||||||
5
app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<background android:drawable="@drawable/ic_launcher_background"/>
|
||||||
|
<foreground android:drawable="@mipmap/ic_launcher_foreground"/>
|
||||||
|
</adaptive-icon>
|
||||||
5
app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<background android:drawable="@drawable/ic_launcher_background"/>
|
||||||
|
<foreground android:drawable="@mipmap/ic_launcher_foreground"/>
|
||||||
|
</adaptive-icon>
|
||||||
BIN
app/src/main/res/mipmap-hdpi/ic_launcher.png
Normal file
|
After Width: | Height: | Size: 2.5 KiB |
BIN
app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png
Normal file
|
After Width: | Height: | Size: 5.1 KiB |
BIN
app/src/main/res/mipmap-hdpi/ic_launcher_round.png
Normal file
|
After Width: | Height: | Size: 4.5 KiB |
BIN
app/src/main/res/mipmap-mdpi/ic_launcher.png
Normal file
|
After Width: | Height: | Size: 1.7 KiB |
BIN
app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png
Normal file
|
After Width: | Height: | Size: 2.8 KiB |
BIN
app/src/main/res/mipmap-mdpi/ic_launcher_round.png
Normal file
|
After Width: | Height: | Size: 2.6 KiB |
BIN
app/src/main/res/mipmap-xhdpi/ic_launcher.png
Normal file
|
After Width: | Height: | Size: 3.8 KiB |
BIN
app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png
Normal file
|
After Width: | Height: | Size: 8.2 KiB |
BIN
app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
Normal file
|
After Width: | Height: | Size: 6.7 KiB |
BIN
app/src/main/res/mipmap-xxhdpi/ic_launcher.png
Normal file
|
After Width: | Height: | Size: 6.9 KiB |
BIN
app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png
Normal file
|
After Width: | Height: | Size: 16 KiB |
BIN
app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
Normal file
|
After Width: | Height: | Size: 12 KiB |
BIN
app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
Normal file
|
After Width: | Height: | Size: 11 KiB |
BIN
app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png
Normal file
|
After Width: | Height: | Size: 27 KiB |
BIN
app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
Normal file
|
After Width: | Height: | Size: 18 KiB |
20
app/src/main/res/values-v21/styles.xml
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
|
||||||
|
<style name="FUTheme" parent="Theme.AppCompat.Light.NoActionBar" >
|
||||||
|
<item name="android:colorPrimary">@color/colorFUPrimary</item>
|
||||||
|
<item name="android:colorAccent">@color/colorFUSecondary</item>
|
||||||
|
<item name="android:colorPrimaryDark">@color/colorFUPrimaryDark</item>
|
||||||
|
<item name="android:colorBackground">@color/colorFUGray</item>
|
||||||
|
<item name="android:statusBarColor">?android:attr/colorPrimaryDark</item>
|
||||||
|
<item name="colorPrimary">@color/colorFUPrimary</item>
|
||||||
|
<item name="colorPrimaryDark">@color/colorFUPrimaryDark</item>
|
||||||
|
<item name="colorAccent">@color/colorFUSecondary</item>
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<style name="FUTheme_Dialog" parent="android:Theme.Material.Light.Dialog.NoActionBar" >
|
||||||
|
<item name="android:colorPrimary">@color/colorFUPrimary</item>
|
||||||
|
<item name="android:colorPrimaryDark">@color/colorFUPrimaryDark</item>
|
||||||
|
<item name="android:colorAccent">@color/colorFUSecondary</item>
|
||||||
|
</style>
|
||||||
|
</resources>
|
||||||
11
app/src/main/res/values/colors.xml
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
<color name="colorPrimary">#3F51B5</color>
|
||||||
|
<color name="colorPrimaryDark">#303F9F</color>
|
||||||
|
<color name="colorAccent">#FF4081</color>
|
||||||
|
<color name="colorFUPrimary">#98cb00</color>
|
||||||
|
<color name="colorFUPrimaryDark">#2f4000</color>
|
||||||
|
<color name="colorFUWhite">#FDFDFD</color>
|
||||||
|
<color name="colorFUGray">#cdcdcd</color>
|
||||||
|
<color name="colorFUSecondary">#003366</color>
|
||||||
|
</resources>
|
||||||
9
app/src/main/res/values/dimens.xml
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
<resources>
|
||||||
|
<!-- Default screen margins, per the Android Design guidelines. -->
|
||||||
|
<dimen name="activity_horizontal_margin">16dp</dimen>
|
||||||
|
<dimen name="activity_vertical_margin">16dp</dimen>
|
||||||
|
<dimen name="nav_header_vertical_spacing">16dp</dimen>
|
||||||
|
<dimen name="nav_header_height">160dp</dimen>
|
||||||
|
<dimen name="fab_margin">16dp</dimen>
|
||||||
|
<dimen name="text_margin">16dp</dimen>
|
||||||
|
</resources>
|
||||||
8
app/src/main/res/values/drawables.xml
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
<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>
|
||||||
11
app/src/main/res/values/strings.xml
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
<resources>
|
||||||
|
<string name="app_name">FU Planner</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>
|
||||||
|
|
||||||
|
<!-- TODO: Remove or change this placeholder text -->
|
||||||
|
<string name="hello_blank_fragment">Hello blank fragment</string>
|
||||||
|
</resources>
|
||||||
27
app/src/main/res/values/styles.xml
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
<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" />
|
||||||
|
|
||||||
|
<style name="FUTheme" parent="Theme.AppCompat.Light.NoActionBar" >
|
||||||
|
<item name="android:colorBackground">@color/colorFUGray</item>
|
||||||
|
<item name="colorPrimary">@color/colorFUPrimary</item>
|
||||||
|
<item name="colorPrimaryDark">@color/colorFUPrimaryDark</item>
|
||||||
|
<item name="colorAccent">@color/colorFUSecondary</item>
|
||||||
|
</style>
|
||||||
|
|
||||||
|
</resources>
|
||||||
4
app/src/main/res/xml/backup_descriptor.xml
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<full-backup-content>
|
||||||
|
<!-- Exclude specific shared preferences that contain GCM registration Id -->
|
||||||
|
</full-backup-content>
|
||||||
17
app/src/test/java/de/sebse/fuplanner/ExampleUnitTest.java
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
package de.sebse.fuplanner;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Example local unit test, which will execute on the development machine (host).
|
||||||
|
*
|
||||||
|
* @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
|
||||||
|
*/
|
||||||
|
public class ExampleUnitTest {
|
||||||
|
@Test
|
||||||
|
public void addition_isCorrect() throws Exception {
|
||||||
|
assertEquals(4, 2 + 2);
|
||||||
|
}
|
||||||
|
}
|
||||||
25
build.gradle
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
// Top-level build file where you can add configuration options common to all sub-projects/modules.
|
||||||
|
|
||||||
|
buildscript {
|
||||||
|
repositories {
|
||||||
|
jcenter()
|
||||||
|
google()
|
||||||
|
}
|
||||||
|
dependencies {
|
||||||
|
classpath 'com.android.tools.build:gradle:3.1.2'
|
||||||
|
|
||||||
|
// NOTE: Do not place your application dependencies here; they belong
|
||||||
|
// in the individual module build.gradle files
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
allprojects {
|
||||||
|
repositories {
|
||||||
|
jcenter()
|
||||||
|
google()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
task clean(type: Delete) {
|
||||||
|
delete rootProject.buildDir
|
||||||
|
}
|
||||||
180
docs/GoogleAuth-old.java
Normal file
@@ -0,0 +1,180 @@
|
|||||||
|
package de.sebse.fuplanner.services.GoogleAuth;
|
||||||
|
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.content.IntentSender;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
|
import android.support.annotation.Nullable;
|
||||||
|
import android.support.v4.app.FragmentActivity;
|
||||||
|
import android.util.Log;
|
||||||
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
import com.google.android.gms.auth.api.Auth;
|
||||||
|
import com.google.android.gms.auth.api.credentials.Credential;
|
||||||
|
import com.google.android.gms.auth.api.credentials.CredentialRequest;
|
||||||
|
import com.google.android.gms.auth.api.credentials.CredentialRequestResult;
|
||||||
|
import com.google.android.gms.auth.api.credentials.CredentialsClient;
|
||||||
|
import com.google.android.gms.common.ConnectionResult;
|
||||||
|
import com.google.android.gms.common.GoogleApiAvailability;
|
||||||
|
import com.google.android.gms.common.api.GoogleApiClient;
|
||||||
|
import com.google.android.gms.common.api.ResultCallback;
|
||||||
|
import com.google.android.gms.common.api.Status;
|
||||||
|
|
||||||
|
import static android.app.Activity.RESULT_OK;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by Sebastian on 06.11.2017.
|
||||||
|
*/
|
||||||
|
|
||||||
|
public class GoogleAuth {
|
||||||
|
// https://developers.google.com/identity/smartlock-passwords/android/retrieve-credentials
|
||||||
|
|
||||||
|
private static final String TAG = "GoogleAuth";
|
||||||
|
private final FragmentActivity activity;
|
||||||
|
private GoogleApiClient mCredentialsClient;
|
||||||
|
|
||||||
|
public GoogleAuth(FragmentActivity activity) {
|
||||||
|
this.activity = activity;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void connect(final ConnectedListener listener) {
|
||||||
|
if (!this.isAvailable()) {
|
||||||
|
Log.d(TAG, "STATUS: Google auth not available!");
|
||||||
|
listener.connected();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.mCredentialsClient = getClient(new GoogleApiClient.ConnectionCallbacks() {
|
||||||
|
@Override
|
||||||
|
public void onConnected(@Nullable Bundle bundle) {
|
||||||
|
listener.connected();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onConnectionSuspended(int i) {
|
||||||
|
}
|
||||||
|
}, new GoogleApiClient.OnConnectionFailedListener() {
|
||||||
|
@Override
|
||||||
|
public void onConnectionFailed(@NonNull ConnectionResult connectionResult) {
|
||||||
|
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public void getLoginState(final CredentialsListener credentialsListener) {
|
||||||
|
if (!this.isAvailable()) {
|
||||||
|
Log.d(TAG, "STATUS: Google auth not available!");
|
||||||
|
credentialsListener.onCredentials(null);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
CredentialRequest mCredentialRequest = new CredentialRequest.Builder()
|
||||||
|
.setPasswordLoginSupported(true)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
Auth.CredentialsApi.request(this.mCredentialsClient, mCredentialRequest).setResultCallback(
|
||||||
|
new ResultCallback<CredentialRequestResult>() {
|
||||||
|
@Override
|
||||||
|
public void onResult(@NonNull CredentialRequestResult credentialRequestResult) {
|
||||||
|
if (credentialRequestResult.getStatus().isSuccess()) {
|
||||||
|
// See "Handle successful credential requests"
|
||||||
|
Credential credential = credentialRequestResult.getCredential();
|
||||||
|
credentialsListener.onCredentials(new Credentials(credential.getId(), credential.getPassword()));
|
||||||
|
} else {
|
||||||
|
// See "Handle unsuccessful and incomplete credential requests"
|
||||||
|
credentialsListener.onCredentials(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setLoginState(String username, String password) {
|
||||||
|
if (!this.isAvailable()) {
|
||||||
|
Log.d(TAG, "STATUS: Google auth not available!");
|
||||||
|
Toast.makeText(activity, "Google auth not available!", Toast.LENGTH_SHORT).show();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Credential credential = new Credential.Builder(username)
|
||||||
|
.setPassword(password)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Auth.CredentialsApi.save(mCredentialsClient, credential).setResultCallback(
|
||||||
|
new ResultCallback<Status>() {
|
||||||
|
@Override
|
||||||
|
public void onResult(@NonNull Status status) {
|
||||||
|
if (status.isSuccess()) {
|
||||||
|
Log.d(TAG, "SAVE: OK");
|
||||||
|
Toast.makeText(activity, "Credentials saved", Toast.LENGTH_SHORT).show();
|
||||||
|
} else {
|
||||||
|
Log.d(TAG, String.valueOf(status.hasResolution()));
|
||||||
|
Log.d(TAG, String.valueOf(status.getStatus()));
|
||||||
|
if (status.hasResolution()) {
|
||||||
|
// Try to resolve the save request. This will prompt the user if
|
||||||
|
// the credential is new.
|
||||||
|
try {
|
||||||
|
status.startResolutionForResult(activity, RequestCode.RC_SAVE);
|
||||||
|
} catch (IntentSender.SendIntentException e) {
|
||||||
|
// Could not resolve the request
|
||||||
|
Log.e(TAG, "STATUS: Failed to send resolution.", e);
|
||||||
|
Toast.makeText(activity, "Save failed", Toast.LENGTH_SHORT).show();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Request has no resolution
|
||||||
|
Toast.makeText(activity, "Save failed", Toast.LENGTH_SHORT).show();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void deleteLoginState(String username, String password) {
|
||||||
|
if (!this.isAvailable()) {
|
||||||
|
Log.d(TAG, "STATUS: Google auth not available!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Credential credential = new Credential.Builder(username)
|
||||||
|
.setPassword(password)
|
||||||
|
.build();
|
||||||
|
Auth.CredentialsApi.delete(mCredentialsClient, credential).setResultCallback(
|
||||||
|
new ResultCallback<Status>() {
|
||||||
|
@Override
|
||||||
|
public void onResult(Status status) {
|
||||||
|
if (status.isSuccess()) {
|
||||||
|
// Credential was deleted successfully
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private boolean isAvailable() {
|
||||||
|
return GoogleApiAvailability.getInstance().isGooglePlayServicesAvailable(this.activity) == ConnectionResult.SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
private GoogleApiClient getClient(@NonNull GoogleApiClient.ConnectionCallbacks connectionCallbacks, @NonNull GoogleApiClient.OnConnectionFailedListener failedListener) {
|
||||||
|
return new GoogleApiClient.Builder(this.activity)
|
||||||
|
.addConnectionCallbacks(connectionCallbacks)
|
||||||
|
.enableAutoManage(this.activity, failedListener)
|
||||||
|
.addApi(Auth.CREDENTIALS_API)
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||||
|
if (requestCode == RequestCode.RC_SAVE) {
|
||||||
|
if (resultCode == RESULT_OK) {
|
||||||
|
Log.d(TAG, "SAVE: OK");
|
||||||
|
Toast.makeText(activity, "Credentials saved", Toast.LENGTH_SHORT).show();
|
||||||
|
} else {
|
||||||
|
Log.e(TAG, "SAVE: Canceled by user");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
301
docs/GoogleAuth.java
Normal file
@@ -0,0 +1,301 @@
|
|||||||
|
package de.sebse.fuplanner.services.GoogleAuth;
|
||||||
|
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.content.IntentSender;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
|
import android.support.annotation.Nullable;
|
||||||
|
import android.support.v4.app.FragmentActivity;
|
||||||
|
import android.util.Log;
|
||||||
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
import com.google.android.gms.auth.api.Auth;
|
||||||
|
import com.google.android.gms.auth.api.credentials.Credential;
|
||||||
|
import com.google.android.gms.auth.api.credentials.CredentialRequest;
|
||||||
|
import com.google.android.gms.auth.api.credentials.CredentialRequestResponse;
|
||||||
|
import com.google.android.gms.auth.api.credentials.CredentialRequestResult;
|
||||||
|
import com.google.android.gms.auth.api.credentials.CredentialsClient;
|
||||||
|
import com.google.android.gms.auth.api.credentials.CredentialsOptions;
|
||||||
|
import com.google.android.gms.common.ConnectionResult;
|
||||||
|
import com.google.android.gms.common.GoogleApiAvailability;
|
||||||
|
import com.google.android.gms.common.api.ApiException;
|
||||||
|
import com.google.android.gms.common.api.CommonStatusCodes;
|
||||||
|
import com.google.android.gms.common.api.GoogleApiClient;
|
||||||
|
import com.google.android.gms.common.api.ResolvableApiException;
|
||||||
|
import com.google.android.gms.common.api.ResultCallback;
|
||||||
|
import com.google.android.gms.common.api.Status;
|
||||||
|
import com.google.android.gms.tasks.OnCompleteListener;
|
||||||
|
import com.google.android.gms.tasks.Task;
|
||||||
|
|
||||||
|
import static android.app.Activity.RESULT_OK;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by Sebastian on 06.11.2017.
|
||||||
|
*/
|
||||||
|
|
||||||
|
public class GoogleAuth {
|
||||||
|
// https://developers.google.com/identity/smartlock-passwords/android/retrieve-credentials
|
||||||
|
|
||||||
|
private static final String TAG = "GoogleAuth";
|
||||||
|
private final FragmentActivity activity;
|
||||||
|
private CredentialsClient mCredentialsClient;
|
||||||
|
private boolean mIsResolving;
|
||||||
|
|
||||||
|
public GoogleAuth(FragmentActivity activity) {
|
||||||
|
this.activity = activity;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void connect(final ConnectedListener listener) {
|
||||||
|
if (!this.isAvailable()) {
|
||||||
|
Log.d(TAG, "STATUS: Google auth not available!");
|
||||||
|
listener.connected();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.mCredentialsClient = getClient(new GoogleApiClient.ConnectionCallbacks() {
|
||||||
|
@Override
|
||||||
|
public void onConnected(@Nullable Bundle bundle) {
|
||||||
|
listener.connected();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onConnectionSuspended(int i) {
|
||||||
|
}
|
||||||
|
}, new GoogleApiClient.OnConnectionFailedListener() {
|
||||||
|
@Override
|
||||||
|
public void onConnectionFailed(@NonNull ConnectionResult connectionResult) {
|
||||||
|
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public void getLoginState(final CredentialsListener credentialsListener) {
|
||||||
|
if (!this.isAvailable()) {
|
||||||
|
Log.d(TAG, "STATUS: Google auth not available!");
|
||||||
|
credentialsListener.onCredentials(null);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
CredentialRequest request = new CredentialRequest.Builder()
|
||||||
|
.setPasswordLoginSupported(true)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
|
||||||
|
mCredentialsClient.request(request).addOnCompleteListener(
|
||||||
|
new OnCompleteListener<CredentialRequestResponse>() {
|
||||||
|
@Override
|
||||||
|
public void onComplete(@NonNull Task<CredentialRequestResponse> task) {
|
||||||
|
|
||||||
|
if (task.isSuccessful()) {
|
||||||
|
// Successfully read the credential without any user interaction, this
|
||||||
|
// means there was only a single credential and the user has auto
|
||||||
|
// sign-in enabled.
|
||||||
|
Credential credential = task.getResult().getCredential();
|
||||||
|
credentialsListener.onCredentials(new Credentials(credential.getId(), credential.getPassword()));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Exception e = task.getException();
|
||||||
|
if (e instanceof ResolvableApiException) {
|
||||||
|
// This is most likely the case where the user has multiple saved
|
||||||
|
// credentials and needs to pick one. This requires showing UI to
|
||||||
|
// resolve the read request.
|
||||||
|
ResolvableApiException rae = (ResolvableApiException) e;
|
||||||
|
resolveResult(rae, RequestCode.RC_READ);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (e instanceof ApiException) {
|
||||||
|
ApiException ae = (ApiException) e;
|
||||||
|
if (ae.getStatusCode() == CommonStatusCodes.SIGN_IN_REQUIRED) {
|
||||||
|
// This means only a hint is available, but we are handling that
|
||||||
|
// elsewhere so no need to act here.
|
||||||
|
} else {
|
||||||
|
Log.w(TAG, "Unexpected status code: " + ae.getStatusCode());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*Auth.CredentialsApi.request(this.mCredentialsClient, mCredentialRequest).setResultCallback(
|
||||||
|
new ResultCallback<CredentialRequestResult>() {
|
||||||
|
@Override
|
||||||
|
public void onResult(@NonNull CredentialRequestResult credentialRequestResult) {
|
||||||
|
if (credentialRequestResult.getStatus().isSuccess()) {
|
||||||
|
// See "Handle successful credential requests"
|
||||||
|
Credential credential = credentialRequestResult.getCredential();
|
||||||
|
credentialsListener.onCredentials(new Credentials(credential.getId(), credential.getPassword()));
|
||||||
|
} else {
|
||||||
|
// See "Handle unsuccessful and incomplete credential requests"
|
||||||
|
credentialsListener.onCredentials(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);*/
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setLoginState(String username, String password) {
|
||||||
|
if (!this.isAvailable()) {
|
||||||
|
Log.d(TAG, "STATUS: Google auth not available!");
|
||||||
|
Toast.makeText(activity, "Google auth not available!", Toast.LENGTH_SHORT).show();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Credential credential = new Credential.Builder(username)
|
||||||
|
.setPassword(password)
|
||||||
|
.build();
|
||||||
|
mCredentialsClient.save(credential).addOnCompleteListener(new OnCompleteListener<Void>() {
|
||||||
|
@Override
|
||||||
|
public void onComplete(@NonNull Task<Void> task) {
|
||||||
|
if (task.isSuccessful()) {
|
||||||
|
showToast("Credential saved.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Exception e = task.getException();
|
||||||
|
if (e instanceof ResolvableApiException) {
|
||||||
|
// The first time a credential is saved, the user is shown UI
|
||||||
|
// to confirm the action. This requires resolution.
|
||||||
|
ResolvableApiException rae = (ResolvableApiException) e;
|
||||||
|
resolveResult(rae, RequestCode.RC_SAVE);
|
||||||
|
} else {
|
||||||
|
// Save failure cannot be resolved.
|
||||||
|
Log.w(TAG, "Save failed.", e);
|
||||||
|
showToast("Credential Save Failed");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*Auth.CredentialsApi.save(mCredentialsClient, credential).setResultCallback(
|
||||||
|
new ResultCallback<Status>() {
|
||||||
|
@Override
|
||||||
|
public void onResult(@NonNull Status status) {
|
||||||
|
if (status.isSuccess()) {
|
||||||
|
Log.d(TAG, "SAVE: OK");
|
||||||
|
Toast.makeText(activity, "Credentials saved", Toast.LENGTH_SHORT).show();
|
||||||
|
} else {
|
||||||
|
Log.d(TAG, String.valueOf(status.hasResolution()));
|
||||||
|
Log.d(TAG, String.valueOf(status.getStatus()));
|
||||||
|
if (status.hasResolution()) {
|
||||||
|
// Try to resolve the save request. This will prompt the user if
|
||||||
|
// the credential is new.
|
||||||
|
try {
|
||||||
|
status.startResolutionForResult(activity, RC_SAVE);
|
||||||
|
} catch (IntentSender.SendIntentException e) {
|
||||||
|
// Could not resolve the request
|
||||||
|
Log.e(TAG, "STATUS: Failed to send resolution.", e);
|
||||||
|
Toast.makeText(activity, "Save failed", Toast.LENGTH_SHORT).show();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Request has no resolution
|
||||||
|
Toast.makeText(activity, "Save failed", Toast.LENGTH_SHORT).show();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);*/
|
||||||
|
}
|
||||||
|
|
||||||
|
public void deleteLoginState(String username, String password) {
|
||||||
|
if (!this.isAvailable()) {
|
||||||
|
Log.d(TAG, "STATUS: Google auth not available!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Credential credential = new Credential.Builder(username)
|
||||||
|
.setPassword(password)
|
||||||
|
.build();
|
||||||
|
Auth.CredentialsApi.delete(mCredentialsClient, credential).setResultCallback(
|
||||||
|
new ResultCallback<Status>() {
|
||||||
|
@Override
|
||||||
|
public void onResult(Status status) {
|
||||||
|
if (status.isSuccess()) {
|
||||||
|
// Credential was deleted successfully
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private boolean isAvailable() {
|
||||||
|
return GoogleApiAvailability.getInstance().isGooglePlayServicesAvailable(this.activity) == ConnectionResult.SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
private CredentialsClient getClient(@NonNull GoogleApiClient.ConnectionCallbacks connectionCallbacks, @NonNull GoogleApiClient.OnConnectionFailedListener failedListener) {
|
||||||
|
CredentialsOptions options = new CredentialsOptions.Builder()
|
||||||
|
.forceEnableSaveDialog()
|
||||||
|
.build();
|
||||||
|
return com.google.android.gms.auth.api.credentials.Credentials.getClient(this.activity, options);
|
||||||
|
|
||||||
|
|
||||||
|
/*return new GoogleApiClient.Builder(this.activity)
|
||||||
|
.addConnectionCallbacks(connectionCallbacks)
|
||||||
|
.enableAutoManage(this.activity, failedListener)
|
||||||
|
.addApi(Auth.CREDENTIALS_API)
|
||||||
|
.build();*/
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||||
|
Log.d(TAG, "onActivityResult:" + requestCode + ":" + resultCode + ":" + data);
|
||||||
|
|
||||||
|
switch (requestCode) {
|
||||||
|
case RequestCode.RC_HINT:
|
||||||
|
// Drop into handling for RC_READ
|
||||||
|
case RequestCode.RC_READ:
|
||||||
|
if (resultCode == RESULT_OK) {
|
||||||
|
boolean isHint = (requestCode == RequestCode.RC_HINT);
|
||||||
|
Credential credential = data.getParcelableExtra(Credential.EXTRA_KEY);
|
||||||
|
|
||||||
|
processRetrievedCredential(credential, isHint);
|
||||||
|
} else {
|
||||||
|
Log.e(TAG, "Credential Read: NOT OK");
|
||||||
|
showToast("Credential Read Failed");
|
||||||
|
}
|
||||||
|
|
||||||
|
mIsResolving = false;
|
||||||
|
break;
|
||||||
|
case RequestCode.RC_SAVE:
|
||||||
|
if (resultCode == RESULT_OK) {
|
||||||
|
Log.d(TAG, "Credential Save: OK");
|
||||||
|
showToast("Credential Save Success");
|
||||||
|
} else {
|
||||||
|
Log.e(TAG, "Credential Save: NOT OK");
|
||||||
|
showToast("Credential Save Failed");
|
||||||
|
}
|
||||||
|
|
||||||
|
mIsResolving = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private void resolveResult(ResolvableApiException rae, int requestCode) {
|
||||||
|
// We don't want to fire multiple resolutions at once since that can result
|
||||||
|
// in stacked dialogs after rotation or another similar event.
|
||||||
|
if (mIsResolving) {
|
||||||
|
Log.w(TAG, "resolveResult: already resolving.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Log.d(TAG, "Resolving: " + rae);
|
||||||
|
try {
|
||||||
|
rae.startResolutionForResult(this.activity, requestCode);
|
||||||
|
mIsResolving = true;
|
||||||
|
} catch (IntentSender.SendIntentException e) {
|
||||||
|
Log.e(TAG, "STATUS: Failed to send resolution.", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Display a short Toast message **/
|
||||||
|
private void showToast(String msg) {
|
||||||
|
Toast.makeText(this.activity, msg, Toast.LENGTH_SHORT).show();
|
||||||
|
}
|
||||||
|
}
|
||||||
77
docs/kvvlogin.txt
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
-----------------------------------------------------------------------------
|
||||||
|
KVV-Login
|
||||||
|
-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
GET https://kvv.imp.fu-berlin.de/portal/login
|
||||||
|
-> JSESSIONID 5c10406f-588c-4c16-96e9-c80d115417de.tomcat1
|
||||||
|
|
||||||
|
|
||||||
|
GET https://kvv.imp.fu-berlin.de/sakai-login-tool/container
|
||||||
|
<- JSESSIONID
|
||||||
|
-> (Location-Header) https://identity.fu-berlin.de/idp-fub/profile/SAML2/Redirect/SSO
|
||||||
|
?SAMLRequest=fZLLb.....Q8yre3X1IHwkJKE0Mnpy/V9TH4A
|
||||||
|
&RelayState=ss:mem:7ea01e29157b8bd906f7002176.....0d1a505f2c8bf
|
||||||
|
|
||||||
|
|
||||||
|
GET https://identity.fu-berlin.de/idp-fub/profile/SAML2/Redirect/SSO
|
||||||
|
?SAMLRequest=fZLLbsIwEEV/JfI+cWJAUIsgpbAoEi2IpF10UznxUKw6dupxaPn7hkdb2LD29bkzRzNGUeuGZ63fmjV8toA++K61QX58SEnrDLcCFXIjakDuK55njwvOopg3znpbWU2CDBGcV9ZMrcG2BpeD26kKnteLlGy9b5BT+rHbRapuok0bluC0MpEEmm9VWVoNfhshWnpgM7pa5gUJZt0wyogD9h+iJBiv/P6aomQTbtqSdhNtlIYzZg1SOag8zfMlCeazlLyNqpHsy1gO2V1fVsNBMuqJoUyAJaxXDUaiiyG2MDfohfEpYXEyDJM4ZKxgCe/FPI5fSbA6L36vjFTm/bal8hRC/lAUq/C02gs4PK7VBchkfHDNj8Xuwv5trPhVTiY3BeOf4DG96DmVNvypA89nK6tVtQ8yre3X1IHwkJKE0Mnpy/V9TH4A
|
||||||
|
&RelayState=ss:mem:7ea01e29157b8bd906f7002176213b6db5e1f45ebb88716a9820d1a505f2c8bf
|
||||||
|
-> JSESSIONID C4B6A428BA1F50746235D03F5D107A57
|
||||||
|
-> _idp_authn_lc_key 57a6ae26067f374cc3d0ccfc47e27b04b47752d2a3d4eb2782af0d3994535395
|
||||||
|
-> ROUTEID .1
|
||||||
|
|
||||||
|
|
||||||
|
POST https://identity.fu-berlin.de/idp-fub/Authn/UserPassword
|
||||||
|
------ 2x
|
||||||
|
<- j_username seedorf96
|
||||||
|
<- j_password neinhieristpatrick
|
||||||
|
<- (Header-"Content-Type") application/x-www-form-urlencoded
|
||||||
|
<- JSESSIONID
|
||||||
|
<- _idp_authn_lc_key
|
||||||
|
<- ROUTEID
|
||||||
|
-> _idp_session OTMuMTkzLjg1LjMz|LQ==|OGYxOWI4MjA2NTQ4YWUwYzJkOWM4Mjk4YzcwZDMwZmJiZjBmMTdmMzkyZGU2OWIwY2JkNmZlNjlmNTRmNzBlMQ==|wLlzQal7VqyntmG2vLNn06wt8wQ=
|
||||||
|
|
||||||
|
|
||||||
|
GET https://identity.fu-berlin.de/idp-fub/profile/SAML2/Redirect/SSO
|
||||||
|
<- JSESSIONID
|
||||||
|
<- _idp_authn_lc_key
|
||||||
|
<- ROUTEID
|
||||||
|
<- _idp_session
|
||||||
|
-> (BODY) RelayState 7ea01e29157b8bd906f7002176213b6db5e1f45ebb88716a9820d1a505f2c8bf
|
||||||
|
-> (BODY) SAMLResponse PD94bWwgdmVyc2lvbj0...........wvc2FtbDJwOlJlc3BvbnNlPg==
|
||||||
|
|
||||||
|
|
||||||
|
POST https://kvv.imp.fu-berlin.de/Shibboleth.sso/SAML2/POST
|
||||||
|
<- RelayState 7ea01e29157b8bd906f7002176213b6db5e1f45ebb88716a9820d1a505f2c8bf
|
||||||
|
<- SAMLResponse PD94bWwgdmVyc2lvbj0...........wvc2FtbDJwOlJlc3BvbnNlPg==
|
||||||
|
<- JSESSIONID
|
||||||
|
-> _shibsession_64656661756c7468747470733a2f2f6b76762e696d702e66752d6265726c696e2e64652f73686962626f6c657468
|
||||||
|
_b1912c5a03d733a80bd3fee772bf68d4
|
||||||
|
|
||||||
|
|
||||||
|
GET https://kvv.imp.fu-berlin.de/
|
||||||
|
<- JSESSIONID
|
||||||
|
<- _shibsession_64656661756c7468747470733a2f2f6b76762e696d702e66752d6265726c696e2e64652f73686962626f6c657468
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
-----------------------------------------------------------------------------
|
||||||
|
KVV-Login (Relogin)
|
||||||
|
-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
GET https://kvv.imp.fu-berlin.de/portal/login
|
||||||
|
<- JSESSIONID
|
||||||
|
<- _shibsession_64656661756c7468747470733a2f2f6b76762e696d702e66752d6265726c696e2e64652f73686962626f6c657468
|
||||||
|
<- pasystem_timezone_ok true
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<tbody>(.|\n)*</tbody>
|
||||||
1482
docs/module_details.txt
Normal file
17
gradle.properties
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
# 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.
|
||||||
|
org.gradle.jvmargs=-Xmx1536m
|
||||||
|
|
||||||
|
# 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
|
||||||
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
Normal file
6
gradle/wrapper/gradle-wrapper.properties
vendored
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
#Thu Apr 19 17:52:36 CEST 2018
|
||||||
|
distributionBase=GRADLE_USER_HOME
|
||||||
|
distributionPath=wrapper/dists
|
||||||
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
|
zipStorePath=wrapper/dists
|
||||||
|
distributionUrl=https\://services.gradle.org/distributions/gradle-4.4-all.zip
|
||||||
160
gradlew
vendored
Normal file
@@ -0,0 +1,160 @@
|
|||||||
|
#!/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 "$@"
|
||||||