Initial commit

This commit is contained in:
Caesar2011
2018-04-24 09:48:17 +02:00
commit 3d02129cb8
102 changed files with 5710 additions and 0 deletions

9
.gitignore vendored Normal file
View 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

Binary file not shown.

29
.idea/codeStyles/Project.xml generated Normal file
View 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
View 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
View File

@@ -0,0 +1,3 @@
<component name="CopyrightManager">
<settings default="" />
</component>

18
.idea/gradle.xml generated Normal file
View 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
View 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
View 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
View 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
View 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
View File

@@ -0,0 +1 @@
/build

41
app/build.gradle Normal file
View 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
View 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

View File

@@ -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());
}
}

View 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>

Binary file not shown.

After

Width:  |  Height:  |  Size: 54 KiB

View 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
}
});
}
}

View File

@@ -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);
}
}

View File

@@ -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);
}
}

View File

@@ -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() + "'";
}
}
}

View File

@@ -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);
}
}

View File

@@ -0,0 +1,9 @@
package de.sebse.fuplanner.services.GoogleAuth;
/**
* Created by sebastian on 07.11.17.
*/
public interface ConnectedListener {
public void connected();
}

View File

@@ -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;
}
}

View File

@@ -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);
}

View File

@@ -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();
}
}

View File

@@ -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;
}

View 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);
}
}
}

View 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&#x3a;mem&#x3a;([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;
}
}

View File

@@ -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(); }
};
}
};
}
}

View File

@@ -0,0 +1,9 @@
package de.sebse.fuplanner.services.KVV;
/**
* Created by sebastian on 31.01.18.
*/
interface LastTokenCallback {
void onReceived(LoginToken token);
}

View File

@@ -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);
}
}

View 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;
}
}
}

View 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);
}
}

View File

@@ -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));
}
}

View File

@@ -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;
}
}

View File

@@ -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);
}
}
}

View File

@@ -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);
}
}

View File

@@ -0,0 +1,8 @@
package de.sebse.fuplanner.tools.network;
import android.support.annotation.NonNull;
public interface NetworkCallback<T> {
void onResponse(@NonNull T success);
}

View File

@@ -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();
}
}

View File

@@ -0,0 +1,5 @@
package de.sebse.fuplanner.tools.network;
public interface NetworkErrorCallback<T> {
void onError(NetworkError error);
}

View File

@@ -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;
}
}

View 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>

View 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>

View 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>

View 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>

View 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>

View 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>

View 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>

View 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>

View 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>

View 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>

View 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>

View 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>

View 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>

View 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>

View 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>

View 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>

View 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>

View 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>

View 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" />

View 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>

View 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>

View 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>

View 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>

View 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>

View 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>

View 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>

View 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>

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

View 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>

View 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>

View 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>

View 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>

View 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>

View 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>

View 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>

View 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
View 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
View 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
View 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
View 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

File diff suppressed because it is too large Load Diff

17
gradle.properties Normal file
View 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

Binary file not shown.

View 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
View 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 "$@"

Some files were not shown because too many files have changed in this diff Show More