Compare commits
186 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6d09d8e885 | ||
|
|
16a7d8ae13 | ||
|
|
3af8b55e7b | ||
|
|
9f060227c0 | ||
|
|
7c0623ea60 | ||
|
|
abdc4695e9 | ||
|
|
e70daa33cc | ||
|
|
6e45f1f660 | ||
|
|
4f2cb4be8c | ||
|
|
a6f7e3c7fd | ||
|
|
6b1205bde8 | ||
|
|
ae10bff497 | ||
|
|
03df7c10be | ||
|
|
ed0ace8f6f | ||
|
|
bfabcb2672 | ||
|
|
67138dee8b | ||
|
|
40443cd4c2 | ||
|
|
4e8e205a20 | ||
|
|
f86ee2ddf3 | ||
|
|
ff1845d7aa | ||
|
|
c0b4268f0e | ||
|
|
7ba31b8617 | ||
|
|
71f5bb735c | ||
|
|
18ce71cb87 | ||
|
|
f7730f5ed6 | ||
|
|
9be7a18827 | ||
|
|
06a430f7fd | ||
|
|
b5fc82c138 | ||
|
|
a9a86c743e | ||
|
|
ba3b07b8b4 | ||
|
|
f70e774896 | ||
|
|
75e53601c9 | ||
|
|
124b34dc2f | ||
|
|
1fa442dbd9 | ||
|
|
e1de122001 | ||
|
|
1130b57022 | ||
|
|
b244778c4a | ||
|
|
f97e293ef3 | ||
|
|
b67f7edd04 | ||
|
|
34a104a8a3 | ||
|
|
59027b59fe | ||
|
|
11c65b6292 | ||
|
|
7ad374dd5c | ||
|
|
834b259f61 | ||
|
|
4cecb7a1c7 | ||
|
|
c79977140f | ||
|
|
e21b2e99f2 | ||
|
|
95cef20bb8 | ||
|
|
7b4fd9dfab | ||
|
|
662f9bc240 | ||
|
|
14fa440237 | ||
|
|
ac123abb5f | ||
|
|
68f6a32aa8 | ||
|
|
2210138785 | ||
|
|
e04fe4ec5f | ||
|
|
230376f785 | ||
|
|
4a1fc3e28b | ||
|
|
f0bd6d1454 | ||
|
|
1094298761 | ||
|
|
8acac1ee8b | ||
|
|
652e07cf9c | ||
|
|
28fe6951f2 | ||
|
|
85bc13ab0a | ||
|
|
2bf56d4c1e | ||
|
|
d8d98e0082 | ||
|
|
ecf14c5f21 | ||
|
|
b5f642bd36 | ||
|
|
2d84c87695 | ||
|
|
66b6d82a83 | ||
|
|
bdae34a9d3 | ||
|
|
05d9e76900 | ||
|
|
cabb78ab87 | ||
|
|
7a07577cd4 | ||
|
|
b3fadb4110 | ||
|
|
d8e3a68344 | ||
|
|
2598e11ac5 | ||
|
|
ef404dd816 | ||
|
|
601759ebb1 | ||
|
|
279594bf7d | ||
|
|
f0d1826b1f | ||
|
|
640b0b63b4 | ||
|
|
8175239cff | ||
|
|
7b67834ed8 | ||
|
|
04755b8829 | ||
|
|
5b6c2f3705 | ||
|
|
f0f8fdcb5a | ||
|
|
d3b41508bb | ||
|
|
cb2c2daa14 | ||
|
|
a827977d6c | ||
|
|
386894f919 | ||
|
|
28bd628efb | ||
|
|
1f1ba082d2 | ||
|
|
500edb1a4c | ||
|
|
24e72bf760 | ||
|
|
67b248e2e8 | ||
|
|
728d715041 | ||
|
|
9939d6542a | ||
|
|
f1d1e1c5dc | ||
|
|
f529f7aca5 | ||
|
|
b34e4d5664 | ||
|
|
2e0190b378 | ||
|
|
d8cad9d52f | ||
|
|
2cae890a01 | ||
|
|
bc72127273 | ||
|
|
e7089e7b79 | ||
|
|
5cd5bc6a1a | ||
|
|
2e8ba9f179 | ||
|
|
d0f54fc2d7 | ||
|
|
3f93f77c46 | ||
|
|
119ec3874c | ||
|
|
223a418526 | ||
|
|
6f3816e469 | ||
|
|
792807154e | ||
|
|
eb72dcf8c1 | ||
|
|
f052e18d63 | ||
|
|
c74fd9b3fc | ||
|
|
c9cd4592ce | ||
|
|
023a90ec4f | ||
|
|
f850bdad31 | ||
|
|
6c297bda9c | ||
|
|
75683960ca | ||
|
|
e40743df93 | ||
|
|
dea643da75 | ||
|
|
22f0fb5930 | ||
|
|
a4940481fa | ||
|
|
8a8db8fb4c | ||
|
|
6b2b7e623b | ||
|
|
3acc27c6f1 | ||
|
|
a0a76109a1 | ||
|
|
8ed48e40bb | ||
|
|
a40b7d7d52 | ||
|
|
3448551ad1 | ||
|
|
616d1e1b33 | ||
|
|
7b98e9b643 | ||
|
|
5073d9fd87 | ||
|
|
41b0a88f25 | ||
|
|
0550659ecf | ||
|
|
f864065a65 | ||
|
|
2cde3e83d5 | ||
|
|
31f4342726 | ||
|
|
33a08dd47d | ||
|
|
692da14a38 | ||
|
|
0f0efa4d57 | ||
|
|
cdcbdd74b6 | ||
|
|
d353353d3a | ||
|
|
b49a3d01da | ||
|
|
d492d13842 | ||
|
|
4ca7654f94 | ||
|
|
264240b35d | ||
|
|
21aab66d9a | ||
|
|
be953ddfa3 | ||
|
|
6cdb61e2bb | ||
|
|
1d1b1e7db9 | ||
|
|
c91ec96578 | ||
|
|
e334461a1c | ||
|
|
c186e85533 | ||
|
|
b924c8be2b | ||
|
|
a7044c2408 | ||
|
|
45d436c5fb | ||
|
|
b342e79e11 | ||
|
|
11c3f9157b | ||
|
|
2b34167724 | ||
|
|
b82f0fd3f2 | ||
|
|
3bf87dccc2 | ||
|
|
abb9f754a9 | ||
|
|
c997667596 | ||
|
|
281722b404 | ||
|
|
d72c87d0b1 | ||
|
|
6fb1c013e8 | ||
|
|
da5f969c4a | ||
|
|
9795c8e148 | ||
|
|
9b405fcd3f | ||
|
|
80adac818f | ||
|
|
3dc54200af | ||
|
|
ff4dea25e3 | ||
|
|
ca97103f9a | ||
|
|
fd06ec4c11 | ||
|
|
cc2aa4e591 | ||
|
|
2a4fe8d2e1 | ||
|
|
5c4bd50379 | ||
|
|
c45d859a51 | ||
|
|
7a1b6b66d7 | ||
|
|
55f06830b0 | ||
|
|
c9a5f9972d | ||
|
|
b573312710 | ||
|
|
3d02129cb8 |
4
.gitignore
vendored
@@ -1,8 +1,8 @@
|
|||||||
*.iml
|
*.iml
|
||||||
.gradle
|
.gradle
|
||||||
/local.properties
|
/local.properties
|
||||||
/.idea/workspace.xml
|
/.idea/*
|
||||||
/.idea/libraries
|
/.idea - PC/*
|
||||||
.DS_Store
|
.DS_Store
|
||||||
/build
|
/build
|
||||||
/captures
|
/captures
|
||||||
|
|||||||
29
.idea/codeStyles/Project.xml
generated
@@ -1,29 +0,0 @@
|
|||||||
<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>
|
|
||||||
18
.idea/gradle.xml
generated
@@ -1,18 +0,0 @@
|
|||||||
<?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>
|
|
||||||
29
.idea/misc.xml
generated
@@ -1,29 +0,0 @@
|
|||||||
<?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="5">
|
|
||||||
<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="javax.annotation.CheckForNull" />
|
|
||||||
<item index="3" class="java.lang.String" itemvalue="edu.umd.cs.findbugs.annotations.Nullable" />
|
|
||||||
<item index="4" 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_8" default="true" project-jdk-name="1.8" project-jdk-type="JavaSDK" />
|
|
||||||
</project>
|
|
||||||
9
.idea/modules.xml
generated
@@ -1,9 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<project version="4">
|
|
||||||
<component name="ProjectModuleManager">
|
|
||||||
<modules>
|
|
||||||
<module fileurl="file://$PROJECT_DIR$/.idea/FUPlanner.iml" filepath="$PROJECT_DIR$/.idea/FUPlanner.iml" />
|
|
||||||
<module fileurl="file://$PROJECT_DIR$/app/app.iml" filepath="$PROJECT_DIR$/app/app.iml" />
|
|
||||||
</modules>
|
|
||||||
</component>
|
|
||||||
</project>
|
|
||||||
12
.idea/runConfigurations.xml
generated
@@ -1,12 +0,0 @@
|
|||||||
<?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>
|
|
||||||
7
.idea/vcs.xml
generated
@@ -1,7 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<project version="4">
|
|
||||||
<component name="VcsDirectoryMappings">
|
|
||||||
<mapping directory="" vcs="Git" />
|
|
||||||
<mapping directory="$PROJECT_DIR$" vcs="Git" />
|
|
||||||
</component>
|
|
||||||
</project>
|
|
||||||
@@ -2,14 +2,14 @@ apply plugin: 'com.android.application'
|
|||||||
|
|
||||||
android {
|
android {
|
||||||
compileSdkVersion 28
|
compileSdkVersion 28
|
||||||
buildToolsVersion '27.0.3'
|
buildToolsVersion '28.0.3'
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
applicationId "de.sebse.fuplanner"
|
applicationId "de.sebse.fuplanner"
|
||||||
minSdkVersion 15
|
minSdkVersion 15
|
||||||
targetSdkVersion 28
|
targetSdkVersion 28
|
||||||
versionCode 1
|
versionCode 7
|
||||||
versionName "1.0"
|
versionName "1.1.5"
|
||||||
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
|
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||||
}
|
}
|
||||||
buildTypes {
|
buildTypes {
|
||||||
release {
|
release {
|
||||||
@@ -27,24 +27,24 @@ android {
|
|||||||
|
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation 'com.android.support:recyclerview-v7:28.0.0-rc01'
|
implementation 'androidx.recyclerview:recyclerview:1.0.0'
|
||||||
implementation fileTree(include: ['*.jar'], dir: 'libs')
|
implementation fileTree(include: ['*.jar'], dir: 'libs')
|
||||||
androidTestImplementation('com.android.support.test.espresso:espresso-core:2.2.2', {
|
androidTestImplementation('androidx.test.espresso:espresso-core:3.1.0-beta02', {
|
||||||
exclude group: 'com.android.support', module: 'support-annotations'
|
exclude group: 'com.android.support', module: 'support-annotations'
|
||||||
})
|
})
|
||||||
implementation 'com.android.support:appcompat-v7:28.0.0-rc01'
|
implementation 'androidx.appcompat:appcompat:1.0.0'
|
||||||
implementation 'com.android.support:preference-v7:28.0.0-rc01'
|
implementation 'androidx.preference:preference:1.0.0'
|
||||||
implementation 'com.android.support:design:28.0.0-rc01'
|
implementation 'com.google.android.material:material:1.0.0'
|
||||||
implementation 'com.android.support.constraint:constraint-layout:1.1.2'
|
implementation 'androidx.constraintlayout:constraintlayout:2.0.0-alpha2'
|
||||||
implementation 'com.android.volley:volley:1.0.0'
|
implementation 'com.android.volley:volley:1.1.0'
|
||||||
//noinspection GradleDependency
|
//noinspection GradleDependency
|
||||||
implementation 'com.google.android.gms:play-services-auth:15.0.0'
|
implementation 'com.google.android.gms:play-services-auth:15.0.0'
|
||||||
implementation 'com.android.support:support-v4:28.0.0-rc01'
|
implementation 'androidx.legacy:legacy-support-v4:1.0.0'
|
||||||
testImplementation 'junit:junit:4.12'
|
testImplementation 'junit:junit:4.12'
|
||||||
implementation fileTree(include: ['*.jar'], dir: 'libs')
|
implementation fileTree(include: ['*.jar'], dir: 'libs')
|
||||||
implementation 'com.android.support:appcompat-v7:28.0.0-rc01'
|
implementation 'androidx.appcompat:appcompat:1.0.0'
|
||||||
implementation 'org.jbundle.util.osgi.wrapped:org.jbundle.util.osgi.wrapped.org.apache.http.client:4.1.2'
|
implementation 'org.jbundle.util.osgi.wrapped:org.jbundle.util.osgi.wrapped.org.apache.http.client:4.1.2'
|
||||||
implementation 'com.android.support:support-v4:28.0.0-rc01'
|
implementation 'androidx.legacy:legacy-support-v4:1.0.0'
|
||||||
implementation 'org.jetbrains:annotations-java5:15.0'
|
implementation 'org.jetbrains:annotations-java5:15.0'
|
||||||
//implementation 'com.github.quivr:android-week-view:2.0.2'//com.github.alamkanak:android-week-view:1.2.6
|
//implementation 'com.github.quivr:android-week-view:2.0.2'//com.github.alamkanak:android-week-view:1.2.6
|
||||||
implementation 'com.ms-square:expandableTextView:0.1.4'
|
implementation 'com.ms-square:expandableTextView:0.1.4'
|
||||||
|
|||||||
@@ -1,12 +1,13 @@
|
|||||||
package de.sebse.fuplanner;
|
package de.sebse.fuplanner;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.support.test.InstrumentationRegistry;
|
|
||||||
import android.support.test.runner.AndroidJUnit4;
|
|
||||||
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
|
|
||||||
|
import androidx.test.InstrumentationRegistry;
|
||||||
|
import androidx.test.runner.AndroidJUnit4;
|
||||||
|
|
||||||
import static org.junit.Assert.*;
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -4,6 +4,9 @@
|
|||||||
|
|
||||||
<uses-permission android:name="android.permission.INTERNET" />
|
<uses-permission android:name="android.permission.INTERNET" />
|
||||||
|
|
||||||
|
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
||||||
|
|
||||||
|
|
||||||
<application
|
<application
|
||||||
android:allowBackup="true"
|
android:allowBackup="true"
|
||||||
android:icon="@mipmap/ic_launcher"
|
android:icon="@mipmap/ic_launcher"
|
||||||
|
|||||||
BIN
app/src/main/ic_bio-web.png
Normal file
|
After Width: | Height: | Size: 30 KiB |
BIN
app/src/main/ic_msc-web.png
Normal file
|
After Width: | Height: | Size: 37 KiB |
BIN
app/src/main/ic_vegan-web.png
Normal file
|
After Width: | Height: | Size: 44 KiB |
BIN
app/src/main/ic_vegetarian-web.png
Normal file
|
After Width: | Height: | Size: 33 KiB |
@@ -2,24 +2,27 @@ package de.sebse.fuplanner;
|
|||||||
|
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.support.annotation.NonNull;
|
|
||||||
import android.support.annotation.StringRes;
|
|
||||||
import android.support.design.widget.NavigationView;
|
|
||||||
import android.support.v4.app.Fragment;
|
|
||||||
import android.support.v4.app.FragmentManager;
|
|
||||||
import android.support.v4.app.FragmentTransaction;
|
|
||||||
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.Menu;
|
||||||
import android.view.MenuItem;
|
import android.view.MenuItem;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
import com.google.android.material.navigation.NavigationView;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.annotation.StringRes;
|
||||||
|
import androidx.appcompat.app.ActionBarDrawerToggle;
|
||||||
|
import androidx.appcompat.app.AppCompatActivity;
|
||||||
|
import androidx.appcompat.widget.Toolbar;
|
||||||
|
import androidx.core.view.GravityCompat;
|
||||||
|
import androidx.drawerlayout.widget.DrawerLayout;
|
||||||
|
import androidx.fragment.app.Fragment;
|
||||||
|
import androidx.fragment.app.FragmentManager;
|
||||||
|
import androidx.fragment.app.FragmentTransaction;
|
||||||
import de.sebse.fuplanner.fragments.CanteensFragment;
|
import de.sebse.fuplanner.fragments.CanteensFragment;
|
||||||
import de.sebse.fuplanner.fragments.LoginFragment;
|
import de.sebse.fuplanner.fragments.LoginFragment;
|
||||||
import de.sebse.fuplanner.fragments.ModulesFragment;
|
import de.sebse.fuplanner.fragments.ModulesFragment;
|
||||||
@@ -34,11 +37,12 @@ import de.sebse.fuplanner.services.GoogleAuth.GoogleAuth;
|
|||||||
import de.sebse.fuplanner.services.KVV.KVV;
|
import de.sebse.fuplanner.services.KVV.KVV;
|
||||||
import de.sebse.fuplanner.services.KVV.types.LoginToken;
|
import de.sebse.fuplanner.services.KVV.types.LoginToken;
|
||||||
import de.sebse.fuplanner.services.KVV.types.Modules;
|
import de.sebse.fuplanner.services.KVV.types.Modules;
|
||||||
import de.sebse.fuplanner.tools.MainAcitivityListener;
|
import de.sebse.fuplanner.tools.MainActivityListener;
|
||||||
|
import de.sebse.fuplanner.tools.RequestPermissionsResultListener;
|
||||||
import de.sebse.fuplanner.tools.logging.Logger;
|
import de.sebse.fuplanner.tools.logging.Logger;
|
||||||
|
|
||||||
public class MainActivity extends AppCompatActivity
|
public class MainActivity extends AppCompatActivity
|
||||||
implements MainAcitivityListener,
|
implements MainActivityListener,
|
||||||
NavigationView.OnNavigationItemSelectedListener,
|
NavigationView.OnNavigationItemSelectedListener,
|
||||||
LoginFragment.OnLoginFragmentInteractionListener,
|
LoginFragment.OnLoginFragmentInteractionListener,
|
||||||
ModulesFragment.OnModulesFragmentInteractionListener,
|
ModulesFragment.OnModulesFragmentInteractionListener,
|
||||||
@@ -64,9 +68,11 @@ public class MainActivity extends AppCompatActivity
|
|||||||
private NavigationView mNavigationView;
|
private NavigationView mNavigationView;
|
||||||
|
|
||||||
private int fragmentPage = FRAGMENT_NONE;
|
private int fragmentPage = FRAGMENT_NONE;
|
||||||
|
private int currentPage = FRAGMENT_NONE;
|
||||||
private String fragmentData = "";
|
private String fragmentData = "";
|
||||||
private CanteenBrowser mCanteenBrowser;
|
private CanteenBrowser mCanteenBrowser;
|
||||||
private boolean mOfflineMode = false;
|
private boolean mOfflineMode = false;
|
||||||
|
private HashMap<String, RequestPermissionsResultListener> permissionListeners = new HashMap<>();
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onCreate(Bundle savedInstanceState) {
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
@@ -93,24 +99,14 @@ public class MainActivity extends AppCompatActivity
|
|||||||
mFragmentManager = getSupportFragmentManager();
|
mFragmentManager = getSupportFragmentManager();
|
||||||
|
|
||||||
LoginToken loginToken = getKVV().easyLogin();
|
LoginToken loginToken = getKVV().easyLogin();
|
||||||
if (newFragmentPage != FRAGMENT_LOGIN && newFragmentPage != FRAGMENT_STARTUP && newFragmentPage != FRAGMENT_NONE) {
|
if (loginToken == null) {
|
||||||
if (loginToken != null)
|
|
||||||
toLoginState(loginToken, newFragmentPage, newFragmentData);
|
|
||||||
else
|
|
||||||
checkAndDoLogin();
|
checkAndDoLogin();
|
||||||
} else {
|
} else {
|
||||||
if (loginToken != null)
|
if (newFragmentPage != FRAGMENT_LOGIN && newFragmentPage != FRAGMENT_STARTUP && newFragmentPage != FRAGMENT_NONE)
|
||||||
toLoginState(loginToken, getDefaultFragmentAfterLogin(), "");
|
toLoginState(loginToken, newFragmentPage, newFragmentData);
|
||||||
else
|
else
|
||||||
checkAndDoLogin();
|
toLoginState(loginToken, getDefaultFragmentAfterLogin(), "");
|
||||||
}
|
}
|
||||||
|
|
||||||
/*this.getCanteenBrowser().getCanteens(success -> {
|
|
||||||
Canteen canteen = success.get(0);
|
|
||||||
this.getCanteenBrowser().getCanteen(canteen, success1 -> {
|
|
||||||
this.getCanteenBrowser().getDay(canteen.get(0), log::d, log::e, true);
|
|
||||||
}, log::e, true);
|
|
||||||
}, log::e, true);*/
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -126,7 +122,7 @@ public class MainActivity extends AppCompatActivity
|
|||||||
@Override
|
@Override
|
||||||
public boolean onCreateOptionsMenu(Menu menu) {
|
public boolean onCreateOptionsMenu(Menu menu) {
|
||||||
// Inflate the menu; this adds items to the action bar if it is present.
|
// Inflate the menu; this adds items to the action bar if it is present.
|
||||||
if (fragmentPage == FRAGMENT_SCHEDULE) {
|
if (currentPage == FRAGMENT_SCHEDULE) {
|
||||||
getMenuInflater().inflate(R.menu.options_schedule, menu);
|
getMenuInflater().inflate(R.menu.options_schedule, menu);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -162,7 +158,6 @@ public class MainActivity extends AppCompatActivity
|
|||||||
public boolean onNavigationItemSelected(@NonNull MenuItem item) {
|
public boolean onNavigationItemSelected(@NonNull MenuItem item) {
|
||||||
// Handle navigation view item clicks here.
|
// Handle navigation view item clicks here.
|
||||||
int id = item.getItemId();
|
int id = item.getItemId();
|
||||||
|
|
||||||
switch (id) {
|
switch (id) {
|
||||||
case R.id.nav_modules:
|
case R.id.nav_modules:
|
||||||
changeFragment(FRAGMENT_MODULES);
|
changeFragment(FRAGMENT_MODULES);
|
||||||
@@ -192,8 +187,8 @@ public class MainActivity extends AppCompatActivity
|
|||||||
this.toLogoutState();
|
this.toLogoutState();
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
}
|
|
||||||
|
|
||||||
|
}
|
||||||
DrawerLayout drawer = findViewById(R.id.drawer_layout);
|
DrawerLayout drawer = findViewById(R.id.drawer_layout);
|
||||||
drawer.closeDrawer(GravityCompat.START);
|
drawer.closeDrawer(GravityCompat.START);
|
||||||
|
|
||||||
@@ -214,11 +209,24 @@ public class MainActivity extends AppCompatActivity
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onSaveInstanceState(Bundle savedInstanceState) {
|
protected void onSaveInstanceState(Bundle savedInstanceState) {
|
||||||
|
Fragment fragment = mFragmentManager.findFragmentByTag(String.valueOf(fragmentPage));
|
||||||
savedInstanceState.putInt(ARG_FRAGMENT_PAGE, fragmentPage);
|
savedInstanceState.putInt(ARG_FRAGMENT_PAGE, fragmentPage);
|
||||||
savedInstanceState.putString(ARG_FRAGMENT_STATUS, fragmentData);
|
savedInstanceState.putString(ARG_FRAGMENT_STATUS, fragmentData);
|
||||||
|
if (fragment instanceof ModDetailFragment) {
|
||||||
|
savedInstanceState.putString(ARG_FRAGMENT_STATUS, ((ModDetailFragment) fragment).getData());
|
||||||
|
} else {
|
||||||
|
savedInstanceState.putString(ARG_FRAGMENT_STATUS, fragmentData);
|
||||||
|
}
|
||||||
super.onSaveInstanceState(savedInstanceState);
|
super.onSaveInstanceState(savedInstanceState);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
|
||||||
|
for (RequestPermissionsResultListener listener: permissionListeners.values()) {
|
||||||
|
listener.callback(requestCode, permissions, grantResults);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* --------------------------------------------*/
|
/* --------------------------------------------*/
|
||||||
/* --------------------------------------------*/
|
/* --------------------------------------------*/
|
||||||
/* --------------------------------------------*/
|
/* --------------------------------------------*/
|
||||||
@@ -245,8 +253,19 @@ public class MainActivity extends AppCompatActivity
|
|||||||
}
|
}
|
||||||
|
|
||||||
private int getDefaultFragmentAfterLogin() {
|
private int getDefaultFragmentAfterLogin() {
|
||||||
|
return getDefaultFragmentAfterLogin(new String[1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
private int getDefaultFragmentAfterLogin(String[] id) {
|
||||||
|
if (fragmentPage == FRAGMENT_NONE){
|
||||||
|
id[0] = "";
|
||||||
return FRAGMENT_MODULES;
|
return FRAGMENT_MODULES;
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
|
id[0] = fragmentData;
|
||||||
|
return fragmentPage;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void toLogoutState() {
|
private void toLogoutState() {
|
||||||
changeFragment(FRAGMENT_LOGIN);
|
changeFragment(FRAGMENT_LOGIN);
|
||||||
@@ -257,15 +276,16 @@ public class MainActivity extends AppCompatActivity
|
|||||||
if (loginToken == null) {
|
if (loginToken == null) {
|
||||||
toLogoutState();
|
toLogoutState();
|
||||||
} else {
|
} else {
|
||||||
toLoginState(loginToken.getFullname(), loginToken.getEmail(), newFragment, newData, true);
|
toLoginState(loginToken.getFullName(), loginToken.getEmail(), newFragment, newData, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void toLoginState(String fullname, String email, int newFragment, String newData, boolean onlineMode) {
|
private void toLoginState(String fullName, String email, int newFragment, String newData, boolean onlineMode) {
|
||||||
|
log.d(currentPage, newFragment);
|
||||||
changeFragment(newFragment, newData);
|
changeFragment(newFragment, newData);
|
||||||
|
|
||||||
View header = mNavigationView.getHeaderView(0);
|
View header = mNavigationView.getHeaderView(0);
|
||||||
((TextView) header.findViewById(R.id.login_name)).setText(fullname);
|
((TextView) header.findViewById(R.id.login_name)).setText(fullName);
|
||||||
((TextView) header.findViewById(R.id.login_mail)).setText(email);
|
((TextView) header.findViewById(R.id.login_mail)).setText(email);
|
||||||
setOfflineBanner(onlineMode);
|
setOfflineBanner(onlineMode);
|
||||||
}
|
}
|
||||||
@@ -277,7 +297,10 @@ public class MainActivity extends AppCompatActivity
|
|||||||
toLogoutState();
|
toLogoutState();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.getKVV().login(credentials.getUsername(), credentials.getPassword(), success -> toLoginState(success, getDefaultFragmentAfterLogin(), ""),
|
String[] id = {""};
|
||||||
|
|
||||||
|
int fragment = getDefaultFragmentAfterLogin(id);
|
||||||
|
this.getKVV().login(credentials.getUsername(), credentials.getPassword(), success -> toLoginState(success, fragment , id[0]),
|
||||||
error -> {
|
error -> {
|
||||||
log.e(error);
|
log.e(error);
|
||||||
toLogoutState();
|
toLogoutState();
|
||||||
@@ -290,27 +313,35 @@ public class MainActivity extends AppCompatActivity
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void changeFragment(int newFragment, String newData) {
|
private void changeFragment(int newFragment, String newData) {
|
||||||
onTitleTextChange(R.string.courses);
|
if (mFragmentManager.isStateSaved())
|
||||||
|
return;
|
||||||
|
if (newFragment == FRAGMENT_CANTEENS_DETAILS && newData.equals(""))
|
||||||
|
newFragment = FRAGMENT_CANTEENS;
|
||||||
|
if (newFragment == FRAGMENT_MODULES_DETAILS && newData.equals(""))
|
||||||
|
newFragment = FRAGMENT_MODULES;
|
||||||
|
|
||||||
|
//log.d("changeFragment: ", newFragment, newData);
|
||||||
|
onTitleTextChange(R.string.app_name);
|
||||||
Fragment fragment;
|
Fragment fragment;
|
||||||
switch (newFragment) {
|
switch (newFragment) {
|
||||||
case FRAGMENT_MODULES:
|
|
||||||
fragment = ModulesFragment.newInstance();
|
|
||||||
break;
|
|
||||||
case FRAGMENT_MODULES_DETAILS:
|
case FRAGMENT_MODULES_DETAILS:
|
||||||
fragment = ModDetailFragment.newInstance(newData);
|
fragment = ModDetailFragment.newInstance(newData);
|
||||||
break;
|
break;
|
||||||
|
case FRAGMENT_MODULES:
|
||||||
|
fragment = ModulesFragment.newInstance();
|
||||||
|
break;
|
||||||
case FRAGMENT_LOGIN:
|
case FRAGMENT_LOGIN:
|
||||||
fragment = LoginFragment.newInstance();
|
fragment = LoginFragment.newInstance();
|
||||||
break;
|
break;
|
||||||
case FRAGMENT_SCHEDULE:
|
case FRAGMENT_SCHEDULE:
|
||||||
fragment = ScheduleFragment.newInstance();
|
fragment = ScheduleFragment.newInstance();
|
||||||
break;
|
break;
|
||||||
case FRAGMENT_CANTEENS:
|
|
||||||
fragment = CanteensFragment.newInstance();
|
|
||||||
break;
|
|
||||||
case FRAGMENT_CANTEENS_DETAILS:
|
case FRAGMENT_CANTEENS_DETAILS:
|
||||||
fragment = DaySwitcherFragment.newInstance(Integer.parseInt(newData));
|
fragment = DaySwitcherFragment.newInstance(Integer.parseInt(newData));
|
||||||
break;
|
break;
|
||||||
|
case FRAGMENT_CANTEENS:
|
||||||
|
fragment = CanteensFragment.newInstance();
|
||||||
|
break;
|
||||||
case FRAGMENT_PREFERENCES:
|
case FRAGMENT_PREFERENCES:
|
||||||
fragment = PrefsFragment.newInstance();
|
fragment = PrefsFragment.newInstance();
|
||||||
break;
|
break;
|
||||||
@@ -329,7 +360,7 @@ public class MainActivity extends AppCompatActivity
|
|||||||
findViewById(R.id.app_bar_layout).setVisibility(View.VISIBLE);
|
findViewById(R.id.app_bar_layout).setVisibility(View.VISIBLE);
|
||||||
}
|
}
|
||||||
// switch to logout
|
// switch to logout
|
||||||
if ((fragmentPage != FRAGMENT_STARTUP && fragmentPage != FRAGMENT_LOGIN) && (newFragment == FRAGMENT_STARTUP || newFragment == FRAGMENT_LOGIN)) {
|
if ((currentPage != FRAGMENT_STARTUP && currentPage != FRAGMENT_LOGIN) && (newFragment == FRAGMENT_STARTUP || newFragment == FRAGMENT_LOGIN)) {
|
||||||
View header = mNavigationView.getHeaderView(0);
|
View header = mNavigationView.getHeaderView(0);
|
||||||
//header.findViewById(R.id.imageView).setVisibility(View.GONE);
|
//header.findViewById(R.id.imageView).setVisibility(View.GONE);
|
||||||
header.findViewById(R.id.login_name).setVisibility(View.GONE);
|
header.findViewById(R.id.login_name).setVisibility(View.GONE);
|
||||||
@@ -346,7 +377,10 @@ public class MainActivity extends AppCompatActivity
|
|||||||
mNavigationView.inflateMenu(R.menu.activity_main_drawer);
|
mNavigationView.inflateMenu(R.menu.activity_main_drawer);
|
||||||
afterAnyMenuInflate();
|
afterAnyMenuInflate();
|
||||||
} // switch to login
|
} // switch to login
|
||||||
else if ((fragmentPage == FRAGMENT_STARTUP || fragmentPage == FRAGMENT_LOGIN || fragmentPage == FRAGMENT_NONE) && (newFragment != FRAGMENT_STARTUP && newFragment != FRAGMENT_LOGIN && newFragment != FRAGMENT_CANTEENS && newFragment != FRAGMENT_CANTEENS_DETAILS)) {
|
else if (
|
||||||
|
(currentPage == FRAGMENT_STARTUP || currentPage == FRAGMENT_LOGIN || currentPage == FRAGMENT_NONE) &&
|
||||||
|
(newFragment != FRAGMENT_STARTUP && newFragment != FRAGMENT_LOGIN && getKVV().isLoggedIn())
|
||||||
|
) {
|
||||||
View header = mNavigationView.getHeaderView(0);
|
View header = mNavigationView.getHeaderView(0);
|
||||||
//header.findViewById(R.id.imageView).setVisibility(View.VISIBLE);
|
//header.findViewById(R.id.imageView).setVisibility(View.VISIBLE);
|
||||||
header.findViewById(R.id.login_name).setVisibility(View.VISIBLE);
|
header.findViewById(R.id.login_name).setVisibility(View.VISIBLE);
|
||||||
@@ -391,8 +425,11 @@ public class MainActivity extends AppCompatActivity
|
|||||||
}, log::e);
|
}, log::e);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (newFragment != FRAGMENT_STARTUP && newFragment != FRAGMENT_NONE && newFragment != FRAGMENT_LOGIN) {
|
||||||
this.fragmentPage = newFragment;
|
this.fragmentPage = newFragment;
|
||||||
this.fragmentData = newData;
|
this.fragmentData = newData;
|
||||||
|
}
|
||||||
|
this.currentPage = newFragment;
|
||||||
invalidateOptionsMenu();
|
invalidateOptionsMenu();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -436,31 +473,68 @@ public class MainActivity extends AppCompatActivity
|
|||||||
|
|
||||||
|
|
||||||
public void onLoginFragmentInteraction(LoginToken loginToken, boolean onlineMode) {
|
public void onLoginFragmentInteraction(LoginToken loginToken, boolean onlineMode) {
|
||||||
toLoginState(loginToken.getFullname(), loginToken.getEmail(), getDefaultFragmentAfterLogin(), "", onlineMode);
|
String[] id = {""};
|
||||||
|
int fragment = getDefaultFragmentAfterLogin(id);
|
||||||
|
toLoginState(loginToken.getFullName(), loginToken.getEmail(), fragment, id[0], onlineMode);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void onModulesFragmentInteraction(final String itemID) {
|
public void onModulesFragmentInteraction(final String itemID) {
|
||||||
changeFragment(FRAGMENT_MODULES_DETAILS, itemID);
|
changeFragment(FRAGMENT_MODULES_DETAILS, itemID);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void onCanteensFragmentInteraction(final int itemID) {
|
public void onCanteensFragmentInteraction(final int itemID) {
|
||||||
changeFragment(FRAGMENT_CANTEENS_DETAILS, String.valueOf(itemID));
|
changeFragment(FRAGMENT_CANTEENS_DETAILS, String.valueOf(itemID));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void onTitleTextChange(String newTitle) {
|
public void onTitleTextChange(String newTitle) {
|
||||||
setTitle(newTitle);
|
setTitle(newTitle);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void onTitleTextChange(@StringRes int titleId) {
|
public void onTitleTextChange(@StringRes int titleId) {
|
||||||
setTitle(titleId);
|
setTitle(titleId);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void loginTokenInvalid(boolean doPrecheck) {
|
@Override
|
||||||
|
public void loginTokenInvalid(boolean doLoginCheck) {
|
||||||
|
if (doLoginCheck) {
|
||||||
|
getKVV().testLogin(isSuccess -> {
|
||||||
|
if (!isSuccess) {
|
||||||
getKVV().invalidate();
|
getKVV().invalidate();
|
||||||
checkAndDoLogin();
|
checkAndDoLogin();
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
getKVV().invalidate();
|
||||||
|
checkAndDoLogin();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void refreshFailed(boolean isFailed) {
|
@Override
|
||||||
|
public void onRefreshCompleted(boolean isFailed) {
|
||||||
setRefreshFailedBanner(isFailed);
|
setRefreshFailedBanner(isFailed);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addRequestPermissionsResultListener(RequestPermissionsResultListener listener, String id) {
|
||||||
|
permissionListeners.put(id, listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void removeRequestPermissionsResultListener(String id) {
|
||||||
|
permissionListeners.remove(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void showToast(@StringRes int msgStringRes) {
|
||||||
|
showToast(getString(msgStringRes));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void showToast(String message) {
|
||||||
|
Toast.makeText(this, message, Toast.LENGTH_SHORT).show();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
package de.sebse.fuplanner.fragments;
|
package de.sebse.fuplanner.fragments;
|
||||||
|
|
||||||
import android.support.annotation.NonNull;
|
|
||||||
import android.support.v7.widget.RecyclerView;
|
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
import de.sebse.fuplanner.R;
|
import de.sebse.fuplanner.R;
|
||||||
import de.sebse.fuplanner.fragments.CanteensFragment.OnCanteensFragmentInteractionListener;
|
import de.sebse.fuplanner.fragments.CanteensFragment.OnCanteensFragmentInteractionListener;
|
||||||
import de.sebse.fuplanner.services.Canteen.types.Canteen;
|
import de.sebse.fuplanner.services.Canteen.types.Canteen;
|
||||||
|
|||||||
@@ -2,19 +2,19 @@ package de.sebse.fuplanner.fragments;
|
|||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.support.annotation.NonNull;
|
|
||||||
import android.support.v4.app.Fragment;
|
|
||||||
import android.support.v4.widget.SwipeRefreshLayout;
|
|
||||||
import android.support.v7.widget.LinearLayoutManager;
|
|
||||||
import android.support.v7.widget.RecyclerView;
|
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.fragment.app.Fragment;
|
||||||
|
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||||
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
|
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
|
||||||
import de.sebse.fuplanner.MainActivity;
|
import de.sebse.fuplanner.MainActivity;
|
||||||
import de.sebse.fuplanner.R;
|
import de.sebse.fuplanner.R;
|
||||||
import de.sebse.fuplanner.services.Canteen.CanteenBrowser;
|
import de.sebse.fuplanner.services.Canteen.CanteenBrowser;
|
||||||
import de.sebse.fuplanner.tools.MainAcitivityListener;
|
import de.sebse.fuplanner.tools.MainActivityListener;
|
||||||
import de.sebse.fuplanner.tools.logging.Logger;
|
import de.sebse.fuplanner.tools.logging.Logger;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -87,8 +87,8 @@ public class CanteensFragment extends Fragment {
|
|||||||
throw new RuntimeException(context.toString()
|
throw new RuntimeException(context.toString()
|
||||||
+ " must implement OnCanteensFragmentInteractionListener");
|
+ " must implement OnCanteensFragmentInteractionListener");
|
||||||
}
|
}
|
||||||
if (context instanceof MainAcitivityListener)
|
if (context instanceof MainActivityListener)
|
||||||
((MainAcitivityListener) context).onTitleTextChange(R.string.canteens);
|
((MainActivityListener) context).onTitleTextChange(R.string.canteens);
|
||||||
else
|
else
|
||||||
throw new RuntimeException(context.toString() + "must implement MainActivityListener");
|
throw new RuntimeException(context.toString() + "must implement MainActivityListener");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,8 +3,6 @@ package de.sebse.fuplanner.fragments;
|
|||||||
import android.app.ProgressDialog;
|
import android.app.ProgressDialog;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.support.annotation.NonNull;
|
|
||||||
import android.support.v4.app.Fragment;
|
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
@@ -13,13 +11,15 @@ import android.widget.EditText;
|
|||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.fragment.app.Fragment;
|
||||||
import de.sebse.fuplanner.MainActivity;
|
import de.sebse.fuplanner.MainActivity;
|
||||||
import de.sebse.fuplanner.R;
|
import de.sebse.fuplanner.R;
|
||||||
import de.sebse.fuplanner.services.GoogleAuth.GoogleAuth;
|
import de.sebse.fuplanner.services.GoogleAuth.GoogleAuth;
|
||||||
import de.sebse.fuplanner.services.KVV.KVV;
|
import de.sebse.fuplanner.services.KVV.KVV;
|
||||||
import de.sebse.fuplanner.services.KVV.types.LoginToken;
|
import de.sebse.fuplanner.services.KVV.types.LoginToken;
|
||||||
import de.sebse.fuplanner.services.KVV.types.Modules;
|
import de.sebse.fuplanner.services.KVV.types.Modules;
|
||||||
import de.sebse.fuplanner.tools.MainAcitivityListener;
|
import de.sebse.fuplanner.tools.MainActivityListener;
|
||||||
import de.sebse.fuplanner.tools.logging.Logger;
|
import de.sebse.fuplanner.tools.logging.Logger;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -33,6 +33,7 @@ import de.sebse.fuplanner.tools.logging.Logger;
|
|||||||
public class LoginFragment extends Fragment {
|
public class LoginFragment extends Fragment {
|
||||||
private OnLoginFragmentInteractionListener mListener;
|
private OnLoginFragmentInteractionListener mListener;
|
||||||
private final Logger log = new Logger(this);
|
private final Logger log = new Logger(this);
|
||||||
|
private MainActivityListener mActivityListener;
|
||||||
|
|
||||||
public LoginFragment() {
|
public LoginFragment() {
|
||||||
// Required empty public constructor
|
// Required empty public constructor
|
||||||
@@ -96,10 +97,23 @@ public class LoginFragment extends Fragment {
|
|||||||
kvv.login(username, password, success -> {
|
kvv.login(username, password, success -> {
|
||||||
progressDialog.dismiss();
|
progressDialog.dismiss();
|
||||||
gauth.setLoginState(username, password);
|
gauth.setLoginState(username, password);
|
||||||
if (mListener != null)
|
if (mListener != null) {
|
||||||
|
input_usr.setError(null);
|
||||||
|
input_pwd.setError(null);
|
||||||
mListener.onLoginFragmentInteraction(success, true);
|
mListener.onLoginFragmentInteraction(success, true);
|
||||||
|
}
|
||||||
}, error -> {
|
}, error -> {
|
||||||
progressDialog.dismiss();
|
progressDialog.dismiss();
|
||||||
|
// Invalid password
|
||||||
|
if (mActivityListener != null) {
|
||||||
|
if (error.getCode() == 100131) {
|
||||||
|
mActivityListener.showToast(R.string.invalid_credentials);
|
||||||
|
input_usr.setError(input_usr.getResources().getString(R.string.invalid_credentials));
|
||||||
|
input_pwd.setError(input_pwd.getResources().getString(R.string.invalid_credentials));
|
||||||
|
} else {
|
||||||
|
mActivityListener.showToast(v.getResources().getString(R.string.error_occurred_code, error.getCode()));
|
||||||
|
}
|
||||||
|
}
|
||||||
log.e("Error on KVV login!", error);
|
log.e("Error on KVV login!", error);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -120,9 +134,10 @@ public class LoginFragment extends Fragment {
|
|||||||
throw new RuntimeException(context.toString()
|
throw new RuntimeException(context.toString()
|
||||||
+ " must implement OnLoginFragmentInteractionListener");
|
+ " must implement OnLoginFragmentInteractionListener");
|
||||||
}
|
}
|
||||||
if (context instanceof MainAcitivityListener)
|
if (context instanceof MainActivityListener) {
|
||||||
((MainAcitivityListener) context).onTitleTextChange(R.string.courses);
|
mActivityListener = (MainActivityListener) context;
|
||||||
else
|
mActivityListener.onTitleTextChange(R.string.log_in);
|
||||||
|
} else
|
||||||
throw new RuntimeException(context.toString() + "must implement MainActivityListener");
|
throw new RuntimeException(context.toString() + "must implement MainActivityListener");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
package de.sebse.fuplanner.fragments;
|
package de.sebse.fuplanner.fragments;
|
||||||
|
|
||||||
import android.support.annotation.NonNull;
|
|
||||||
import android.support.v7.widget.RecyclerView;
|
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
import de.sebse.fuplanner.R;
|
import de.sebse.fuplanner.R;
|
||||||
import de.sebse.fuplanner.fragments.ModulesFragment.OnModulesFragmentInteractionListener;
|
import de.sebse.fuplanner.fragments.ModulesFragment.OnModulesFragmentInteractionListener;
|
||||||
import de.sebse.fuplanner.services.KVV.types.Modules;
|
import de.sebse.fuplanner.services.KVV.types.Modules;
|
||||||
|
|||||||
@@ -2,19 +2,19 @@ package de.sebse.fuplanner.fragments;
|
|||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.support.annotation.NonNull;
|
|
||||||
import android.support.v4.app.Fragment;
|
|
||||||
import android.support.v4.widget.SwipeRefreshLayout;
|
|
||||||
import android.support.v7.widget.LinearLayoutManager;
|
|
||||||
import android.support.v7.widget.RecyclerView;
|
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.fragment.app.Fragment;
|
||||||
|
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||||
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
|
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
|
||||||
import de.sebse.fuplanner.MainActivity;
|
import de.sebse.fuplanner.MainActivity;
|
||||||
import de.sebse.fuplanner.R;
|
import de.sebse.fuplanner.R;
|
||||||
import de.sebse.fuplanner.services.KVV.KVV;
|
import de.sebse.fuplanner.services.KVV.KVV;
|
||||||
import de.sebse.fuplanner.tools.MainAcitivityListener;
|
import de.sebse.fuplanner.tools.MainActivityListener;
|
||||||
import de.sebse.fuplanner.tools.logging.Logger;
|
import de.sebse.fuplanner.tools.logging.Logger;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -87,8 +87,8 @@ public class ModulesFragment extends Fragment {
|
|||||||
throw new RuntimeException(context.toString()
|
throw new RuntimeException(context.toString()
|
||||||
+ " must implement OnModulesFragmentInteractionListener");
|
+ " must implement OnModulesFragmentInteractionListener");
|
||||||
}
|
}
|
||||||
if (context instanceof MainAcitivityListener)
|
if (context instanceof MainActivityListener)
|
||||||
((MainAcitivityListener) context).onTitleTextChange(R.string.courses);
|
((MainActivityListener) context).onTitleTextChange(R.string.courses);
|
||||||
else
|
else
|
||||||
throw new RuntimeException(context.toString() + "must implement MainActivityListener");
|
throw new RuntimeException(context.toString() + "must implement MainActivityListener");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,9 +2,9 @@ package de.sebse.fuplanner.fragments;
|
|||||||
|
|
||||||
import android.content.SharedPreferences;
|
import android.content.SharedPreferences;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.support.v7.preference.ListPreference;
|
import androidx.preference.ListPreference;
|
||||||
import android.support.v7.preference.Preference;
|
import androidx.preference.Preference;
|
||||||
import android.support.v7.preference.PreferenceFragmentCompat;
|
import androidx.preference.PreferenceFragmentCompat;
|
||||||
|
|
||||||
import de.sebse.fuplanner.R;
|
import de.sebse.fuplanner.R;
|
||||||
import de.sebse.fuplanner.tools.logging.Logger;
|
import de.sebse.fuplanner.tools.logging.Logger;
|
||||||
|
|||||||
@@ -4,8 +4,6 @@ import android.app.AlertDialog;
|
|||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.graphics.RectF;
|
import android.graphics.RectF;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.support.annotation.NonNull;
|
|
||||||
import android.support.v4.app.Fragment;
|
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
@@ -15,12 +13,14 @@ import java.util.Calendar;
|
|||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.fragment.app.Fragment;
|
||||||
import de.sebse.fuplanner.R;
|
import de.sebse.fuplanner.R;
|
||||||
import de.sebse.fuplanner.services.KVV.KVV;
|
import de.sebse.fuplanner.services.KVV.KVV;
|
||||||
import de.sebse.fuplanner.services.KVV.types.Event;
|
import de.sebse.fuplanner.services.KVV.types.Event;
|
||||||
import de.sebse.fuplanner.services.KVV.types.Modules;
|
import de.sebse.fuplanner.services.KVV.types.Modules;
|
||||||
import de.sebse.fuplanner.tools.DateUtils;
|
import de.sebse.fuplanner.tools.UtilsDate;
|
||||||
import de.sebse.fuplanner.tools.MainAcitivityListener;
|
import de.sebse.fuplanner.tools.MainActivityListener;
|
||||||
import de.sebse.fuplanner.tools.logging.Logger;
|
import de.sebse.fuplanner.tools.logging.Logger;
|
||||||
import de.sebse.fuplanner.tools.ui.weekview.MonthLoader;
|
import de.sebse.fuplanner.tools.ui.weekview.MonthLoader;
|
||||||
import de.sebse.fuplanner.tools.ui.weekview.WeekView;
|
import de.sebse.fuplanner.tools.ui.weekview.WeekView;
|
||||||
@@ -29,14 +29,14 @@ import de.sebse.fuplanner.tools.ui.weekview.WeekViewEvent;
|
|||||||
/**
|
/**
|
||||||
* A simple {@link Fragment} subclass.
|
* A simple {@link Fragment} subclass.
|
||||||
* Activities that contain this fragment must implement the
|
* Activities that contain this fragment must implement the
|
||||||
* {@link MainAcitivityListener} interface
|
* {@link MainActivityListener} interface
|
||||||
* to handle interaction events.
|
* to handle interaction events.
|
||||||
* Use the {@link ScheduleFragment#newInstance} factory method to
|
* Use the {@link ScheduleFragment#newInstance} factory method to
|
||||||
* create an instance of this fragment.
|
* create an instance of this fragment.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public class ScheduleFragment extends Fragment implements MonthLoader.MonthChangeListener, WeekView.ScrollListener, WeekView.DoubleTapListener, WeekView.EventClickListener {
|
public class ScheduleFragment extends Fragment implements MonthLoader.MonthChangeListener, WeekView.ScrollListener, WeekView.DoubleTapListener, WeekView.EventClickListener {
|
||||||
private MainAcitivityListener mListener;
|
private MainActivityListener mListener;
|
||||||
private WeekView mWeekView;
|
private WeekView mWeekView;
|
||||||
private final Logger log = new Logger(this);
|
private final Logger log = new Logger(this);
|
||||||
private Modules mModules = null;
|
private Modules mModules = null;
|
||||||
@@ -99,8 +99,8 @@ public class ScheduleFragment extends Fragment implements MonthLoader.MonthChang
|
|||||||
@Override
|
@Override
|
||||||
public void onAttach(Context context) {
|
public void onAttach(Context context) {
|
||||||
super.onAttach(context);
|
super.onAttach(context);
|
||||||
if (context instanceof MainAcitivityListener) {
|
if (context instanceof MainActivityListener) {
|
||||||
mListener = (MainAcitivityListener) context;
|
mListener = (MainActivityListener) context;
|
||||||
} else {
|
} else {
|
||||||
throw new RuntimeException(context.toString()
|
throw new RuntimeException(context.toString()
|
||||||
+ " must implement OnFragmentInteractionListener");
|
+ " must implement OnFragmentInteractionListener");
|
||||||
@@ -144,7 +144,7 @@ public class ScheduleFragment extends Fragment implements MonthLoader.MonthChang
|
|||||||
public void onFirstVisibleDayChanged(Calendar newFirstVisibleDay, Calendar oldFirstVisibleDay) {
|
public void onFirstVisibleDayChanged(Calendar newFirstVisibleDay, Calendar oldFirstVisibleDay) {
|
||||||
Calendar newLastVisibleDay = (Calendar) newFirstVisibleDay.clone();
|
Calendar newLastVisibleDay = (Calendar) newFirstVisibleDay.clone();
|
||||||
newLastVisibleDay.add(Calendar.HOUR, 24*mWeekView.getNumberOfVisibleDays());
|
newLastVisibleDay.add(Calendar.HOUR, 24*mWeekView.getNumberOfVisibleDays());
|
||||||
mListener.onTitleTextChange(getResources().getString(R.string.date_scale, DateUtils.getModifiedDate(newFirstVisibleDay.getTimeInMillis()), DateUtils.getModifiedDate(newLastVisibleDay.getTimeInMillis())));
|
mListener.onTitleTextChange(getResources().getString(R.string.date_scale, UtilsDate.getModifiedDate(newFirstVisibleDay.getTimeInMillis()), UtilsDate.getModifiedDate(newLastVisibleDay.getTimeInMillis())));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -157,9 +157,11 @@ public class ScheduleFragment extends Fragment implements MonthLoader.MonthChang
|
|||||||
Calendar firstVisibleDay = mWeekView.getFirstVisibleDay();
|
Calendar firstVisibleDay = mWeekView.getFirstVisibleDay();
|
||||||
Calendar c = Calendar.getInstance();
|
Calendar c = Calendar.getInstance();
|
||||||
c.set(firstVisibleDay.get(Calendar.YEAR), firstVisibleDay.get(Calendar.MONTH), firstVisibleDay.get(Calendar.DAY_OF_MONTH), 0,0 );
|
c.set(firstVisibleDay.get(Calendar.YEAR), firstVisibleDay.get(Calendar.MONTH), firstVisibleDay.get(Calendar.DAY_OF_MONTH), 0,0 );
|
||||||
c.add(Calendar.DATE, 2); // die Grenze beim tippen, wann ver nach vorne und wann nach hinten springt
|
// Limit when to go a week backwards and when to the next week
|
||||||
|
c.add(Calendar.DATE, 2);
|
||||||
|
|
||||||
if (c.getTimeInMillis() > time.getTimeInMillis()){//nach links blättern
|
// skip to next week
|
||||||
|
if (c.getTimeInMillis() > time.getTimeInMillis()){
|
||||||
firstVisibleDay.add(Calendar.DATE, -1);
|
firstVisibleDay.add(Calendar.DATE, -1);
|
||||||
while (firstVisibleDay.get(Calendar.DAY_OF_WEEK) != Calendar.MONDAY) {
|
while (firstVisibleDay.get(Calendar.DAY_OF_WEEK) != Calendar.MONDAY) {
|
||||||
firstVisibleDay.add(Calendar.DATE, -1);
|
firstVisibleDay.add(Calendar.DATE, -1);
|
||||||
@@ -172,6 +174,7 @@ public class ScheduleFragment extends Fragment implements MonthLoader.MonthChang
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
mWeekView.goToDate(firstVisibleDay);
|
mWeekView.goToDate(firstVisibleDay);
|
||||||
|
onFirstVisibleDayChanged(firstVisibleDay, mWeekView.getLastVisibleDay());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -190,7 +193,7 @@ public class ScheduleFragment extends Fragment implements MonthLoader.MonthChang
|
|||||||
.setMessage(
|
.setMessage(
|
||||||
getResources().getString(R.string.module_name, moduleName) + "\n" +
|
getResources().getString(R.string.module_name, moduleName) + "\n" +
|
||||||
getResources().getString(R.string.location_name, event.getLocation()) + "\n" +
|
getResources().getString(R.string.location_name, event.getLocation()) + "\n" +
|
||||||
getResources().getString(R.string.date_scale, DateUtils.getModifiedTime(getContext(), event.getStartTime().getTimeInMillis()), DateUtils.getModifiedTime(getContext(), event.getEndTime().getTimeInMillis()+1))
|
getResources().getString(R.string.date_scale, UtilsDate.getModifiedTime(getContext(), event.getStartTime().getTimeInMillis()), UtilsDate.getModifiedTime(getContext(), event.getEndTime().getTimeInMillis()+1))
|
||||||
)
|
)
|
||||||
.setCancelable(true)
|
.setCancelable(true)
|
||||||
.setNeutralButton(R.string.close, (dialog, id) -> dialog.cancel());
|
.setNeutralButton(R.string.close, (dialog, id) -> dialog.cancel());
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
package de.sebse.fuplanner.fragments;
|
package de.sebse.fuplanner.fragments;
|
||||||
|
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.support.annotation.NonNull;
|
|
||||||
import android.support.v4.app.Fragment;
|
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.fragment.app.Fragment;
|
||||||
import de.sebse.fuplanner.R;
|
import de.sebse.fuplanner.R;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
package de.sebse.fuplanner.fragments.canteen;
|
package de.sebse.fuplanner.fragments.canteen;
|
||||||
|
|
||||||
import android.support.v4.app.Fragment;
|
import androidx.fragment.app.Fragment;
|
||||||
import android.support.v4.app.FragmentManager;
|
import androidx.fragment.app.FragmentManager;
|
||||||
import android.support.v4.app.FragmentStatePagerAdapter;
|
import androidx.fragment.app.FragmentStatePagerAdapter;
|
||||||
|
|
||||||
import de.sebse.fuplanner.services.Canteen.types.Canteen;
|
import de.sebse.fuplanner.services.Canteen.types.Canteen;
|
||||||
import de.sebse.fuplanner.tools.DateUtils;
|
import de.sebse.fuplanner.tools.UtilsDate;
|
||||||
|
|
||||||
class DaySwitcherAdapter extends FragmentStatePagerAdapter {
|
class DaySwitcherAdapter extends FragmentStatePagerAdapter {
|
||||||
private Canteen mCanteen = null;
|
private Canteen mCanteen = null;
|
||||||
@@ -39,7 +39,7 @@ class DaySwitcherAdapter extends FragmentStatePagerAdapter {
|
|||||||
// Returns the page title for the top indicator
|
// Returns the page title for the top indicator
|
||||||
@Override
|
@Override
|
||||||
public CharSequence getPageTitle(int position) {
|
public CharSequence getPageTitle(int position) {
|
||||||
return DateUtils.getModifiedDate(mCanteen.get(position).getCalendar().getTimeInMillis());
|
return UtilsDate.getModifiedDate(mCanteen.get(position).getCalendar().getTimeInMillis());
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,18 +2,18 @@ package de.sebse.fuplanner.fragments.canteen;
|
|||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.support.annotation.NonNull;
|
|
||||||
import android.support.v4.app.Fragment;
|
|
||||||
import android.support.v4.view.ViewPager;
|
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.fragment.app.Fragment;
|
||||||
|
import androidx.viewpager.widget.ViewPager;
|
||||||
import de.sebse.fuplanner.MainActivity;
|
import de.sebse.fuplanner.MainActivity;
|
||||||
import de.sebse.fuplanner.R;
|
import de.sebse.fuplanner.R;
|
||||||
import de.sebse.fuplanner.services.Canteen.CanteenBrowser;
|
import de.sebse.fuplanner.services.Canteen.CanteenBrowser;
|
||||||
import de.sebse.fuplanner.services.Canteen.types.Canteen;
|
import de.sebse.fuplanner.services.Canteen.types.Canteen;
|
||||||
import de.sebse.fuplanner.tools.MainAcitivityListener;
|
import de.sebse.fuplanner.tools.MainActivityListener;
|
||||||
import de.sebse.fuplanner.tools.logging.Logger;
|
import de.sebse.fuplanner.tools.logging.Logger;
|
||||||
import de.sebse.fuplanner.tools.network.NetworkCallback;
|
import de.sebse.fuplanner.tools.network.NetworkCallback;
|
||||||
import de.sebse.fuplanner.tools.network.NetworkErrorCallback;
|
import de.sebse.fuplanner.tools.network.NetworkErrorCallback;
|
||||||
@@ -21,7 +21,7 @@ import de.sebse.fuplanner.tools.network.NetworkErrorCallback;
|
|||||||
/**
|
/**
|
||||||
* A simple {@link Fragment} subclass.
|
* A simple {@link Fragment} subclass.
|
||||||
* Activities that contain this fragment must implement the
|
* Activities that contain this fragment must implement the
|
||||||
* {@link MainAcitivityListener} interface
|
* {@link MainActivityListener} interface
|
||||||
* to handle interaction events.
|
* to handle interaction events.
|
||||||
* Use the {@link DaySwitcherFragment#newInstance} factory method to
|
* Use the {@link DaySwitcherFragment#newInstance} factory method to
|
||||||
* create an instance of this fragment.
|
* create an instance of this fragment.
|
||||||
@@ -32,9 +32,8 @@ public class DaySwitcherFragment extends Fragment implements DaySwitcherListener
|
|||||||
// Parameters
|
// Parameters
|
||||||
private int mCanteenId;
|
private int mCanteenId;
|
||||||
|
|
||||||
private MainAcitivityListener mListener;
|
private MainActivityListener mListener;
|
||||||
private final Logger log = new Logger(this);
|
private final Logger log = new Logger(this);
|
||||||
private ViewPager mViewPager;
|
|
||||||
private DaySwitcherAdapter adapterViewPager;
|
private DaySwitcherAdapter adapterViewPager;
|
||||||
|
|
||||||
public DaySwitcherFragment() {
|
public DaySwitcherFragment() {
|
||||||
@@ -78,11 +77,11 @@ public class DaySwitcherFragment extends Fragment implements DaySwitcherListener
|
|||||||
// Inflate the layout for this fragment
|
// Inflate the layout for this fragment
|
||||||
View v = inflater.inflate(R.layout.fragment_mod_detail, container, false);
|
View v = inflater.inflate(R.layout.fragment_mod_detail, container, false);
|
||||||
|
|
||||||
mViewPager = v.findViewById(R.id.vpPager);
|
ViewPager mViewPager = v.findViewById(R.id.vpPager);
|
||||||
adapterViewPager = new DaySwitcherAdapter(getChildFragmentManager());
|
adapterViewPager = new DaySwitcherAdapter(getChildFragmentManager());
|
||||||
mViewPager.setAdapter(adapterViewPager);
|
mViewPager.setAdapter(adapterViewPager);
|
||||||
|
|
||||||
refresh(false);
|
refresh();
|
||||||
|
|
||||||
return v;
|
return v;
|
||||||
}
|
}
|
||||||
@@ -90,8 +89,8 @@ public class DaySwitcherFragment extends Fragment implements DaySwitcherListener
|
|||||||
@Override
|
@Override
|
||||||
public void onAttach(Context context) {
|
public void onAttach(Context context) {
|
||||||
super.onAttach(context);
|
super.onAttach(context);
|
||||||
if (context instanceof MainAcitivityListener) {
|
if (context instanceof MainActivityListener) {
|
||||||
mListener = (MainAcitivityListener) context;
|
mListener = (MainActivityListener) context;
|
||||||
} else {
|
} else {
|
||||||
throw new RuntimeException(context.toString()
|
throw new RuntimeException(context.toString()
|
||||||
+ " must implement MainActivityListener");
|
+ " must implement MainActivityListener");
|
||||||
@@ -104,8 +103,8 @@ public class DaySwitcherFragment extends Fragment implements DaySwitcherListener
|
|||||||
mListener = null;
|
mListener = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void refresh(boolean forceRefresh) {
|
private void refresh() {
|
||||||
refresh(forceRefresh, null, null);
|
refresh(false, null, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void refresh(boolean forceRefresh, NetworkCallback<Canteen> callback, NetworkErrorCallback errorCallback) {
|
private void refresh(boolean forceRefresh, NetworkCallback<Canteen> callback, NetworkErrorCallback errorCallback) {
|
||||||
@@ -113,6 +112,7 @@ public class DaySwitcherFragment extends Fragment implements DaySwitcherListener
|
|||||||
CanteenBrowser browser = ((MainActivity) getActivity()).getCanteenBrowser();
|
CanteenBrowser browser = ((MainActivity) getActivity()).getCanteenBrowser();
|
||||||
browser.getCanteens(canteens -> {
|
browser.getCanteens(canteens -> {
|
||||||
Canteen canteen = canteens.getCanteen(mCanteenId);
|
Canteen canteen = canteens.getCanteen(mCanteenId);
|
||||||
|
canteen.cleanUpDays();
|
||||||
adapterViewPager.setModule(canteen);
|
adapterViewPager.setModule(canteen);
|
||||||
browser.getCanteen(canteen, success -> {
|
browser.getCanteen(canteen, success -> {
|
||||||
adapterViewPager.setModule();
|
adapterViewPager.setModule();
|
||||||
|
|||||||
@@ -4,16 +4,28 @@ import android.content.Context;
|
|||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
import android.widget.BaseExpandableListAdapter;
|
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.annotation.StringRes;
|
||||||
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
import de.sebse.fuplanner.R;
|
import de.sebse.fuplanner.R;
|
||||||
import de.sebse.fuplanner.services.Canteen.types.Day;
|
import de.sebse.fuplanner.services.Canteen.types.Day;
|
||||||
import de.sebse.fuplanner.services.Canteen.types.Meal;
|
import de.sebse.fuplanner.services.Canteen.types.Meal;
|
||||||
import de.sebse.fuplanner.tools.Preferences;
|
import de.sebse.fuplanner.tools.Preferences;
|
||||||
import de.sebse.fuplanner.tools.ui.ItemViewHolder;
|
import de.sebse.fuplanner.tools.ui.MealViewHolder;
|
||||||
import de.sebse.fuplanner.tools.ui.StringViewHolder;
|
import de.sebse.fuplanner.tools.ui.StringViewHolder;
|
||||||
|
|
||||||
class MealAdapter extends BaseExpandableListAdapter {
|
class MealAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
|
||||||
|
private String[] CATEGORY_KEYS = new String[]{"Essen", "Aktionen", "Beilagen", "Desserts", "Salate", "Suppen", "Vorspeisen"};
|
||||||
|
@StringRes
|
||||||
|
private int[] CATEGORY_VALS = new int[]{R.string.meals, R.string.special_meals, R.string.side_dishes, R.string.desserts, R.string.salads, R.string.soups, R.string.starters};
|
||||||
|
@StringRes
|
||||||
|
private int CATEGORY_OTHER = R.string.others;
|
||||||
|
private ArrayList<Object> matches = new ArrayList<>();
|
||||||
|
|
||||||
private Day mDay = null;
|
private Day mDay = null;
|
||||||
private final Context mContext;
|
private final Context mContext;
|
||||||
@@ -22,105 +34,105 @@ class MealAdapter extends BaseExpandableListAdapter {
|
|||||||
mContext = context;
|
mContext = context;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
@Override
|
@Override
|
||||||
public String getChild(int groupPosition, int childPosititon) {
|
public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, int viewType) {
|
||||||
StringBuilder sb = new StringBuilder();
|
if (viewType == 0) {
|
||||||
sb.append("\n\n");
|
View view = LayoutInflater.from(viewGroup.getContext())
|
||||||
for (String s : this.getGroup(groupPosition).getNotes())
|
.inflate(R.layout.list_canteen_items, viewGroup, false);
|
||||||
{
|
return new MealViewHolder(view);
|
||||||
sb.append(s);
|
} else {
|
||||||
sb.append("\n\n");
|
View view = LayoutInflater.from(viewGroup.getContext())
|
||||||
|
.inflate(R.layout.list_all_caption, viewGroup, false);
|
||||||
|
return new StringViewHolder(view);
|
||||||
}
|
}
|
||||||
return sb.toString();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public long getChildId(int groupPosition, int childPosition) {
|
public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {
|
||||||
return childPosition;
|
if (holder.getItemViewType() == 0) {
|
||||||
|
MealViewHolder viewHolder = ((MealViewHolder) holder);
|
||||||
|
viewHolder.reset();
|
||||||
|
Meal meal = getMeal(position);
|
||||||
|
viewHolder.mTitle.setText(meal.getName());
|
||||||
|
String value;
|
||||||
|
switch (Preferences.getString(mContext, R.array.pref_price_group)) {
|
||||||
|
case "student":
|
||||||
|
if (meal.getPriceStdnt() < 0)
|
||||||
|
value = mContext.getResources().getString(R.string.no_price_available);
|
||||||
|
else
|
||||||
|
value = mContext.getString(R.string.price, meal.getPriceStdnt());
|
||||||
|
break;
|
||||||
|
case "employee":
|
||||||
|
if (meal.getPriceEmply() < 0)
|
||||||
|
value = mContext.getResources().getString(R.string.no_price_available);
|
||||||
|
else
|
||||||
|
value = mContext.getString(R.string.price, meal.getPriceEmply());
|
||||||
|
break;
|
||||||
|
case "other":
|
||||||
|
if (meal.getPriceOther() < 0)
|
||||||
|
value = mContext.getResources().getString(R.string.no_price_available);
|
||||||
|
else
|
||||||
|
value = mContext.getString(R.string.price, meal.getPriceOther());
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
String value1;
|
||||||
|
if (meal.getPriceStdnt() < 0)
|
||||||
|
value1 = " -/- ";
|
||||||
|
else
|
||||||
|
value1 = mContext.getString(R.string.price, meal.getPriceStdnt());
|
||||||
|
String value2;
|
||||||
|
if (meal.getPriceEmply() < 0)
|
||||||
|
value2 = " -/- ";
|
||||||
|
else
|
||||||
|
value2 = mContext.getString(R.string.price, meal.getPriceEmply());
|
||||||
|
String value3;
|
||||||
|
if (meal.getPriceOther() < 0)
|
||||||
|
value3 = " -/- ";
|
||||||
|
else
|
||||||
|
value3 = mContext.getString(R.string.price, meal.getPriceOther());
|
||||||
|
value = mContext.getString(R.string.prices, value1, value2, value3);
|
||||||
|
}
|
||||||
|
viewHolder.mSubTitle.setText(value);
|
||||||
|
StringBuilder string = new StringBuilder();
|
||||||
|
List<String> notes = meal.getNotes();
|
||||||
|
for (int i1 = 0, notesSize = notes.size(); i1 < notesSize; i1++) {
|
||||||
|
if (i1 != 0)
|
||||||
|
string.append("\n");
|
||||||
|
String s = notes.get(i1);
|
||||||
|
string.append(" - ").append(s);
|
||||||
|
}
|
||||||
|
viewHolder.mNotes.setText(string.toString());
|
||||||
|
viewHolder.mCategory.setText(meal.getCategory());
|
||||||
|
viewHolder.mIconVegan.setVisibility(meal.getVegan() == Meal.VEGAN_VEGAN ? View.VISIBLE : View.GONE);
|
||||||
|
viewHolder.mIconVegetarian.setVisibility(meal.getVegan() == Meal.VEGAN_VEGETARIAN ? View.VISIBLE : View.GONE);
|
||||||
|
viewHolder.mIconBio.setVisibility((meal.getCertificates() & Meal.CERT_BIO) != 0 ? View.VISIBLE : View.GONE);
|
||||||
|
viewHolder.mIconMsc.setVisibility((meal.getCertificates() & Meal.CERT_MSC) != 0 ? View.VISIBLE : View.GONE);
|
||||||
|
} else {
|
||||||
|
StringViewHolder viewHolder = ((StringViewHolder) holder);
|
||||||
|
viewHolder.mString.setText(getHeading(position));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public View getChildView(int groupPosition, final int childPosition,
|
public int getItemCount() {
|
||||||
boolean isLastChild, View convertView, ViewGroup parent) {
|
return matches.size();
|
||||||
|
|
||||||
final String childText = getChild(groupPosition, childPosition);
|
|
||||||
|
|
||||||
if (convertView == null) {
|
|
||||||
convertView = LayoutInflater.from(parent.getContext())
|
|
||||||
.inflate(R.layout.list_all_string, parent, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
StringViewHolder itemHolder = new StringViewHolder(convertView);
|
|
||||||
itemHolder.mString.setText(childText);
|
|
||||||
|
|
||||||
return convertView;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getChildrenCount(int groupPosition) {
|
public int getItemViewType(int position) {
|
||||||
return 1;
|
return matches.get(position) instanceof String ? 1 : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
private Meal getMeal(int position) {
|
||||||
public Meal getGroup(int groupPosition) {
|
|
||||||
if (this.mDay != null)
|
if (this.mDay != null)
|
||||||
return this.mDay.get(groupPosition);
|
return this.mDay.get((Integer) matches.get(position));
|
||||||
else
|
else
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
private String getHeading(int position) {
|
||||||
public int getGroupCount() {
|
return (String) matches.get(position);
|
||||||
if (this.mDay != null)
|
|
||||||
return this.mDay.size();
|
|
||||||
else
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public long getGroupId(int groupPosition) {
|
|
||||||
return groupPosition;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public View getGroupView(int groupPosition, boolean isExpanded,
|
|
||||||
View convertView, ViewGroup parent) {
|
|
||||||
Meal meal = getGroup(groupPosition);
|
|
||||||
if (convertView == null) {
|
|
||||||
convertView = LayoutInflater.from(parent.getContext())
|
|
||||||
.inflate(R.layout.list_all_items, parent, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
ItemViewHolder itemHolder = new ItemViewHolder(convertView);
|
|
||||||
itemHolder.mTitle.setText(meal.getName());
|
|
||||||
itemHolder.mSubLeft.setText(meal.getCategory());
|
|
||||||
String value;
|
|
||||||
switch (Preferences.getString(mContext, R.array.pref_price_group)) {
|
|
||||||
case "student":
|
|
||||||
value = mContext.getString(R.string.price, meal.getPriceStdnt());
|
|
||||||
break;
|
|
||||||
case "employee":
|
|
||||||
value = mContext.getString(R.string.price, meal.getPriceEmply());
|
|
||||||
break;
|
|
||||||
case "other":
|
|
||||||
value = mContext.getString(R.string.price, meal.getPriceOther());
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
value = mContext.getString(R.string.prices, meal.getPriceStdnt(), meal.getPriceEmply(), meal.getPriceOther());
|
|
||||||
}
|
|
||||||
itemHolder.mSubRight.setText(value);
|
|
||||||
|
|
||||||
return convertView;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean hasStableIds() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isChildSelectable(int groupPosition, int childPosition) {
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setDay(Day day) {
|
public void setDay(Day day) {
|
||||||
@@ -129,6 +141,37 @@ class MealAdapter extends BaseExpandableListAdapter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void setDay() {
|
public void setDay() {
|
||||||
|
HashMap<String, ArrayList<Integer>> map = new HashMap<>();
|
||||||
|
for (int i = 0; i < this.mDay.size(); i++) {
|
||||||
|
String category = this.mDay.get(i).getCategory();
|
||||||
|
boolean found = false;
|
||||||
|
for (String category_key : CATEGORY_KEYS) {
|
||||||
|
if (category_key.equals(category)) {
|
||||||
|
found = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!found) category = "---";
|
||||||
|
ArrayList<Integer> list = map.get(category);
|
||||||
|
if (list == null) {
|
||||||
|
list = new ArrayList<>();
|
||||||
|
map.put(category, list);
|
||||||
|
}
|
||||||
|
list.add(i);
|
||||||
|
}
|
||||||
|
matches.clear();
|
||||||
|
for (int i = 0; i < CATEGORY_KEYS.length; i++) {
|
||||||
|
ArrayList<Integer> list = map.get(CATEGORY_KEYS[i]);
|
||||||
|
if (list != null) {
|
||||||
|
matches.add(mContext.getString(CATEGORY_VALS[i]));
|
||||||
|
matches.addAll(list);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ArrayList<Integer> list = map.get("---");
|
||||||
|
if (list != null) {
|
||||||
|
matches.add(mContext.getString(CATEGORY_OTHER));
|
||||||
|
matches.addAll(list);
|
||||||
|
}
|
||||||
this.notifyDataSetChanged();
|
this.notifyDataSetChanged();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -3,14 +3,14 @@ package de.sebse.fuplanner.fragments.canteen;
|
|||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.support.annotation.NonNull;
|
|
||||||
import android.support.v4.app.Fragment;
|
|
||||||
import android.support.v4.widget.SwipeRefreshLayout;
|
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
import android.widget.ExpandableListView;
|
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.fragment.app.Fragment;
|
||||||
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
|
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
|
||||||
import de.sebse.fuplanner.MainActivity;
|
import de.sebse.fuplanner.MainActivity;
|
||||||
import de.sebse.fuplanner.R;
|
import de.sebse.fuplanner.R;
|
||||||
import de.sebse.fuplanner.services.Canteen.CanteenBrowser;
|
import de.sebse.fuplanner.services.Canteen.CanteenBrowser;
|
||||||
@@ -43,9 +43,9 @@ public class MealFragment extends Fragment {
|
|||||||
* Use this factory method to create a new instance of
|
* Use this factory method to create a new instance of
|
||||||
* this fragment using the provided parameters.
|
* this fragment using the provided parameters.
|
||||||
*
|
*
|
||||||
* @param canteenId Item position in module list.
|
* @param canteenId ID od current canteen.
|
||||||
* @param dayPosition Item position in module list.
|
* @param dayPosition day to show.
|
||||||
* @return A new instance of fragment ModDetailAnnounceFragment.
|
* @return A new instance of fragment MealFragment.
|
||||||
*/
|
*/
|
||||||
public static MealFragment newInstance(int canteenId, int dayPosition) {
|
public static MealFragment newInstance(int canteenId, int dayPosition) {
|
||||||
MealFragment fragment = new MealFragment();
|
MealFragment fragment = new MealFragment();
|
||||||
@@ -69,9 +69,9 @@ public class MealFragment extends Fragment {
|
|||||||
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container,
|
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container,
|
||||||
Bundle savedInstanceState) {
|
Bundle savedInstanceState) {
|
||||||
// Inflate the layout for this fragment
|
// Inflate the layout for this fragment
|
||||||
View view = inflater.inflate(R.layout.fragment_expandable_list_view, container, false);
|
View view = inflater.inflate(R.layout.fragment_recycler_view, container, false);
|
||||||
// Set the adapter
|
// Set the adapter
|
||||||
ExpandableListView expandableListView = view.findViewById(R.id.list);
|
RecyclerView expandableListView = view.findViewById(R.id.list);
|
||||||
adapter = new MealAdapter(getContext());
|
adapter = new MealAdapter(getContext());
|
||||||
expandableListView.setAdapter(adapter);
|
expandableListView.setAdapter(adapter);
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
package de.sebse.fuplanner.fragments.moddetails;
|
package de.sebse.fuplanner.fragments.moddetails;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.support.v4.app.Fragment;
|
import androidx.fragment.app.Fragment;
|
||||||
import android.support.v4.app.FragmentManager;
|
import androidx.fragment.app.FragmentManager;
|
||||||
import android.support.v4.app.FragmentStatePagerAdapter;
|
import androidx.fragment.app.FragmentStatePagerAdapter;
|
||||||
|
|
||||||
import de.sebse.fuplanner.R;
|
import de.sebse.fuplanner.R;
|
||||||
|
|
||||||
@@ -38,6 +38,8 @@ class ModDetailAdapter extends FragmentStatePagerAdapter {
|
|||||||
return ModDetailEventFragment.newInstance(mItemPos);
|
return ModDetailEventFragment.newInstance(mItemPos);
|
||||||
case ModulePart.GRADEBOOK:
|
case ModulePart.GRADEBOOK:
|
||||||
return ModDetailGradebookFragment.newInstance(mItemPos);
|
return ModDetailGradebookFragment.newInstance(mItemPos);
|
||||||
|
case ModulePart.RESOURCES:
|
||||||
|
return ModDetailResourceFragment.newInstance(mItemPos);
|
||||||
default:
|
default:
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@@ -57,6 +59,8 @@ class ModDetailAdapter extends FragmentStatePagerAdapter {
|
|||||||
return this.mContext.getResources().getString(R.string.events);
|
return this.mContext.getResources().getString(R.string.events);
|
||||||
case ModulePart.GRADEBOOK:
|
case ModulePart.GRADEBOOK:
|
||||||
return this.mContext.getResources().getString(R.string.gradebook);
|
return this.mContext.getResources().getString(R.string.gradebook);
|
||||||
|
case ModulePart.RESOURCES:
|
||||||
|
return this.mContext.getResources().getString(R.string.resources);
|
||||||
default:
|
default:
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ import java.util.ArrayList;
|
|||||||
import de.sebse.fuplanner.R;
|
import de.sebse.fuplanner.R;
|
||||||
import de.sebse.fuplanner.services.KVV.types.Announcement;
|
import de.sebse.fuplanner.services.KVV.types.Announcement;
|
||||||
import de.sebse.fuplanner.services.KVV.types.Modules;
|
import de.sebse.fuplanner.services.KVV.types.Modules;
|
||||||
import de.sebse.fuplanner.tools.DateUtils;
|
import de.sebse.fuplanner.tools.UtilsDate;
|
||||||
import de.sebse.fuplanner.tools.ui.ItemViewHolder;
|
import de.sebse.fuplanner.tools.ui.ItemViewHolder;
|
||||||
import de.sebse.fuplanner.tools.ui.StringViewHolder;
|
import de.sebse.fuplanner.tools.ui.StringViewHolder;
|
||||||
|
|
||||||
@@ -19,7 +19,7 @@ class ModDetailAnnounceAdapter extends BaseExpandableListAdapter {
|
|||||||
private Modules.Module mModule = null;
|
private Modules.Module mModule = null;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getChild(int groupPosition, int childPosititon) {
|
public String getChild(int groupPosition, int childPosition) {
|
||||||
StringBuilder s = new StringBuilder(this.getGroup(groupPosition).getBody());
|
StringBuilder s = new StringBuilder(this.getGroup(groupPosition).getBody());
|
||||||
ArrayList<String> urls = this.getGroup(groupPosition).getUrls();
|
ArrayList<String> urls = this.getGroup(groupPosition).getUrls();
|
||||||
for (int j =0; j<urls.size(); j++){
|
for (int j =0; j<urls.size(); j++){
|
||||||
@@ -89,7 +89,7 @@ class ModDetailAnnounceAdapter extends BaseExpandableListAdapter {
|
|||||||
ItemViewHolder itemHolder = new ItemViewHolder(convertView);
|
ItemViewHolder itemHolder = new ItemViewHolder(convertView);
|
||||||
itemHolder.mTitle.setText(announce.getTitle());
|
itemHolder.mTitle.setText(announce.getTitle());
|
||||||
itemHolder.mSubLeft.setText(announce.getCreatedBy());
|
itemHolder.mSubLeft.setText(announce.getCreatedBy());
|
||||||
itemHolder.mSubRight.setText(DateUtils.getModifiedDateTime(parent.getContext(), announce.getCreatedOn()));
|
itemHolder.mSubRight.setText(UtilsDate.getModifiedDateTime(parent.getContext(), announce.getCreatedOn()));
|
||||||
|
|
||||||
return convertView;
|
return convertView;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,14 +2,14 @@ package de.sebse.fuplanner.fragments.moddetails;
|
|||||||
|
|
||||||
|
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.support.annotation.NonNull;
|
|
||||||
import android.support.v4.app.Fragment;
|
|
||||||
import android.support.v4.widget.SwipeRefreshLayout;
|
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
import android.widget.ExpandableListView;
|
import android.widget.ExpandableListView;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.fragment.app.Fragment;
|
||||||
|
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
|
||||||
import de.sebse.fuplanner.MainActivity;
|
import de.sebse.fuplanner.MainActivity;
|
||||||
import de.sebse.fuplanner.R;
|
import de.sebse.fuplanner.R;
|
||||||
import de.sebse.fuplanner.services.KVV.KVV;
|
import de.sebse.fuplanner.services.KVV.KVV;
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import android.widget.BaseExpandableListAdapter;
|
|||||||
import de.sebse.fuplanner.R;
|
import de.sebse.fuplanner.R;
|
||||||
import de.sebse.fuplanner.services.KVV.types.Assignment;
|
import de.sebse.fuplanner.services.KVV.types.Assignment;
|
||||||
import de.sebse.fuplanner.services.KVV.types.Modules;
|
import de.sebse.fuplanner.services.KVV.types.Modules;
|
||||||
import de.sebse.fuplanner.tools.DateUtils;
|
import de.sebse.fuplanner.tools.UtilsDate;
|
||||||
import de.sebse.fuplanner.tools.ui.ItemViewHolder;
|
import de.sebse.fuplanner.tools.ui.ItemViewHolder;
|
||||||
import de.sebse.fuplanner.tools.ui.StringViewHolder;
|
import de.sebse.fuplanner.tools.ui.StringViewHolder;
|
||||||
|
|
||||||
@@ -17,7 +17,7 @@ class ModDetailAssignmentAdapter extends BaseExpandableListAdapter {
|
|||||||
private Modules.Module mModule = null;
|
private Modules.Module mModule = null;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getChild(int groupPosition, int childPosititon) {
|
public String getChild(int groupPosition, int childPosition) {
|
||||||
StringBuilder sb = new StringBuilder();
|
StringBuilder sb = new StringBuilder();
|
||||||
sb.append(this.getGroup(groupPosition).getInstructions());
|
sb.append(this.getGroup(groupPosition).getInstructions());
|
||||||
sb.append("\n\n");
|
sb.append("\n\n");
|
||||||
@@ -92,7 +92,7 @@ class ModDetailAssignmentAdapter extends BaseExpandableListAdapter {
|
|||||||
itemHolder.mSubLeft.setText(itemHolder.mView.getResources().getText(R.string.open));
|
itemHolder.mSubLeft.setText(itemHolder.mView.getResources().getText(R.string.open));
|
||||||
else
|
else
|
||||||
itemHolder.mSubLeft.setText(itemHolder.mView.getResources().getText(R.string.closed));
|
itemHolder.mSubLeft.setText(itemHolder.mView.getResources().getText(R.string.closed));
|
||||||
itemHolder.mSubRight.setText(DateUtils.getModifiedDateTime(parent.getContext(), assignment.getDueDate()));
|
itemHolder.mSubRight.setText(UtilsDate.getModifiedDateTime(parent.getContext(), assignment.getDueDate()));
|
||||||
|
|
||||||
return convertView;
|
return convertView;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,14 +2,14 @@ package de.sebse.fuplanner.fragments.moddetails;
|
|||||||
|
|
||||||
|
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.support.annotation.NonNull;
|
|
||||||
import android.support.v4.app.Fragment;
|
|
||||||
import android.support.v4.widget.SwipeRefreshLayout;
|
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
import android.widget.ExpandableListView;
|
import android.widget.ExpandableListView;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.fragment.app.Fragment;
|
||||||
|
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
|
||||||
import de.sebse.fuplanner.MainActivity;
|
import de.sebse.fuplanner.MainActivity;
|
||||||
import de.sebse.fuplanner.R;
|
import de.sebse.fuplanner.R;
|
||||||
import de.sebse.fuplanner.services.KVV.KVV;
|
import de.sebse.fuplanner.services.KVV.KVV;
|
||||||
|
|||||||
@@ -1,21 +1,21 @@
|
|||||||
package de.sebse.fuplanner.fragments.moddetails;
|
package de.sebse.fuplanner.fragments.moddetails;
|
||||||
|
|
||||||
import android.support.annotation.NonNull;
|
|
||||||
import android.support.v7.widget.RecyclerView;
|
|
||||||
import android.util.Pair;
|
import android.util.Pair;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
import android.widget.TextView;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
import de.sebse.fuplanner.R;
|
import de.sebse.fuplanner.R;
|
||||||
import de.sebse.fuplanner.services.KVV.types.Event;
|
import de.sebse.fuplanner.services.KVV.types.Event;
|
||||||
import de.sebse.fuplanner.services.KVV.types.Modules;
|
import de.sebse.fuplanner.services.KVV.types.Modules;
|
||||||
import de.sebse.fuplanner.tools.DateUtils;
|
import de.sebse.fuplanner.tools.UtilsDate;
|
||||||
import de.sebse.fuplanner.tools.ui.CustomViewHolder;
|
import de.sebse.fuplanner.tools.logging.Logger;
|
||||||
import de.sebse.fuplanner.tools.ui.ItemViewHolder;
|
import de.sebse.fuplanner.tools.ui.ItemViewHolder;
|
||||||
|
import de.sebse.fuplanner.tools.ui.StringViewHolder;
|
||||||
|
|
||||||
class ModDetailEventAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
|
class ModDetailEventAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
|
||||||
private static final int TYPE_HEADER = 0;
|
private static final int TYPE_HEADER = 0;
|
||||||
@@ -27,6 +27,8 @@ class ModDetailEventAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder
|
|||||||
private Modules.Module mValue;
|
private Modules.Module mValue;
|
||||||
private final ArrayList<Pair<Integer, Integer>> mPositionalData;
|
private final ArrayList<Pair<Integer, Integer>> mPositionalData;
|
||||||
|
|
||||||
|
private final Logger log = new Logger(this);
|
||||||
|
|
||||||
ModDetailEventAdapter() {
|
ModDetailEventAdapter() {
|
||||||
mValue = null;
|
mValue = null;
|
||||||
mPositionalData = new ArrayList<>();
|
mPositionalData = new ArrayList<>();
|
||||||
@@ -61,7 +63,7 @@ class ModDetailEventAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder
|
|||||||
case TYPE_HEADER:
|
case TYPE_HEADER:
|
||||||
view = LayoutInflater.from(parent.getContext())
|
view = LayoutInflater.from(parent.getContext())
|
||||||
.inflate(R.layout.list_all_caption, parent, false);
|
.inflate(R.layout.list_all_caption, parent, false);
|
||||||
return new HeaderViewHolder(view);
|
return new StringViewHolder(view);
|
||||||
case TYPE_ITEM:
|
case TYPE_ITEM:
|
||||||
view = LayoutInflater.from(parent.getContext())
|
view = LayoutInflater.from(parent.getContext())
|
||||||
.inflate(R.layout.list_all_items, parent, false);
|
.inflate(R.layout.list_all_items, parent, false);
|
||||||
@@ -87,13 +89,13 @@ class ModDetailEventAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder
|
|||||||
Pair<Integer, Integer> data = mPositionalData.get(position);
|
Pair<Integer, Integer> data = mPositionalData.get(position);
|
||||||
switch (data.first) {
|
switch (data.first) {
|
||||||
case TYPE_HEADER:
|
case TYPE_HEADER:
|
||||||
HeaderViewHolder h = (HeaderViewHolder) holder;
|
StringViewHolder h = (StringViewHolder) holder;
|
||||||
switch (data.second) {
|
switch (data.second) {
|
||||||
case SECTION_PAST:
|
case SECTION_PAST:
|
||||||
h.mCaption.setText(R.string.past_events);
|
h.mString.setText(R.string.past_events);
|
||||||
break;
|
break;
|
||||||
case SECTION_UPCOMING:
|
case SECTION_UPCOMING:
|
||||||
h.mCaption.setText(R.string.upcoming_events);
|
h.mString.setText(R.string.upcoming_events);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@@ -114,14 +116,14 @@ class ModDetailEventAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder
|
|||||||
i.mTitle.setText(event.getTitle());
|
i.mTitle.setText(event.getTitle());
|
||||||
i.mSubLeft.setText(event.getType());
|
i.mSubLeft.setText(event.getType());
|
||||||
String start, end;
|
String start, end;
|
||||||
if (DateUtils.dateEquals(event.getStartDate(), System.currentTimeMillis()))
|
if (UtilsDate.dateEquals(event.getStartDate(), System.currentTimeMillis()))
|
||||||
start = DateUtils.getModifiedTime(i.mView.getContext(), event.getStartDate());
|
start = UtilsDate.getModifiedTime(i.mView.getContext(), event.getStartDate());
|
||||||
else
|
else
|
||||||
start = DateUtils.getModifiedDateTime(i.mView.getContext(), event.getStartDate());
|
start = UtilsDate.getModifiedDateTime(i.mView.getContext(), event.getStartDate());
|
||||||
if (DateUtils.dateEquals(event.getStartDate(), event.getEndDate()))
|
if (UtilsDate.dateEquals(event.getStartDate(), event.getEndDate()))
|
||||||
end = DateUtils.getModifiedTime(i.mView.getContext(), event.getEndDate());
|
end = UtilsDate.getModifiedTime(i.mView.getContext(), event.getEndDate());
|
||||||
else
|
else
|
||||||
end = DateUtils.getModifiedDateTime(i.mView.getContext(), event.getEndDate());
|
end = UtilsDate.getModifiedDateTime(i.mView.getContext(), event.getEndDate());
|
||||||
i.mSubRight.setText(i.mView.getResources().getString(R.string.date_scale,
|
i.mSubRight.setText(i.mView.getResources().getString(R.string.date_scale,
|
||||||
start, end
|
start, end
|
||||||
));
|
));
|
||||||
@@ -145,27 +147,4 @@ class ModDetailEventAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder
|
|||||||
return mValue.events.sizePast();
|
return mValue.events.sizePast();
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class HeaderViewHolder extends CustomViewHolder {
|
|
||||||
final TextView mCaption;
|
|
||||||
|
|
||||||
HeaderViewHolder(View view) {
|
|
||||||
super(view);
|
|
||||||
mCaption = view.findViewById(R.id.caption);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return super.toString() + " '" + mCaption.getText() + "'";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,15 +3,15 @@ package de.sebse.fuplanner.fragments.moddetails;
|
|||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.support.annotation.NonNull;
|
|
||||||
import android.support.v4.app.Fragment;
|
|
||||||
import android.support.v4.widget.SwipeRefreshLayout;
|
|
||||||
import android.support.v7.widget.LinearLayoutManager;
|
|
||||||
import android.support.v7.widget.RecyclerView;
|
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.fragment.app.Fragment;
|
||||||
|
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||||
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
|
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
|
||||||
import de.sebse.fuplanner.MainActivity;
|
import de.sebse.fuplanner.MainActivity;
|
||||||
import de.sebse.fuplanner.R;
|
import de.sebse.fuplanner.R;
|
||||||
import de.sebse.fuplanner.services.KVV.KVV;
|
import de.sebse.fuplanner.services.KVV.KVV;
|
||||||
|
|||||||
@@ -2,22 +2,22 @@ package de.sebse.fuplanner.fragments.moddetails;
|
|||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.support.annotation.NonNull;
|
|
||||||
import android.support.v4.app.Fragment;
|
|
||||||
import android.support.v4.view.ViewPager;
|
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.fragment.app.Fragment;
|
||||||
|
import androidx.viewpager.widget.ViewPager;
|
||||||
import de.sebse.fuplanner.R;
|
import de.sebse.fuplanner.R;
|
||||||
import de.sebse.fuplanner.services.KVV.types.Modules;
|
import de.sebse.fuplanner.services.KVV.types.Modules;
|
||||||
import de.sebse.fuplanner.tools.MainAcitivityListener;
|
import de.sebse.fuplanner.tools.MainActivityListener;
|
||||||
import de.sebse.fuplanner.tools.logging.Logger;
|
import de.sebse.fuplanner.tools.logging.Logger;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A simple {@link Fragment} subclass.
|
* A simple {@link Fragment} subclass.
|
||||||
* Activities that contain this fragment must implement the
|
* Activities that contain this fragment must implement the
|
||||||
* {@link MainAcitivityListener} interface
|
* {@link MainActivityListener} interface
|
||||||
* to handle interaction events.
|
* to handle interaction events.
|
||||||
* Use the {@link ModDetailFragment#newInstance} factory method to
|
* Use the {@link ModDetailFragment#newInstance} factory method to
|
||||||
* create an instance of this fragment.
|
* create an instance of this fragment.
|
||||||
@@ -28,9 +28,10 @@ public class ModDetailFragment extends Fragment implements ModDetailListener {
|
|||||||
// Parameters
|
// Parameters
|
||||||
private String mItemPos;
|
private String mItemPos;
|
||||||
|
|
||||||
private MainAcitivityListener mListener;
|
private MainActivityListener mListener;
|
||||||
private final Logger log = new Logger(this);
|
private final Logger log = new Logger(this);
|
||||||
private ViewPager mViewPager;
|
private ViewPager mViewPager;
|
||||||
|
private String mPageRestoreRequest = null;
|
||||||
|
|
||||||
public ModDetailFragment() {
|
public ModDetailFragment() {
|
||||||
// Required empty public constructor
|
// Required empty public constructor
|
||||||
@@ -46,7 +47,11 @@ public class ModDetailFragment extends Fragment implements ModDetailListener {
|
|||||||
public static Fragment newInstance(String itemPosition) {
|
public static Fragment newInstance(String itemPosition) {
|
||||||
ModDetailFragment fragment = new ModDetailFragment();
|
ModDetailFragment fragment = new ModDetailFragment();
|
||||||
Bundle args = new Bundle();
|
Bundle args = new Bundle();
|
||||||
|
if (!itemPosition.contains("."))
|
||||||
|
args.putString(ARG_POSITION, itemPosition+"."+0);
|
||||||
|
else
|
||||||
args.putString(ARG_POSITION, itemPosition);
|
args.putString(ARG_POSITION, itemPosition);
|
||||||
|
|
||||||
fragment.setArguments(args);
|
fragment.setArguments(args);
|
||||||
return fragment;
|
return fragment;
|
||||||
}
|
}
|
||||||
@@ -55,7 +60,15 @@ public class ModDetailFragment extends Fragment implements ModDetailListener {
|
|||||||
public void onCreate(Bundle savedInstanceState) {
|
public void onCreate(Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
if (getArguments() != null) {
|
if (getArguments() != null) {
|
||||||
mItemPos = getArguments().getString(ARG_POSITION);
|
String itemPosition = getArguments().getString(ARG_POSITION);
|
||||||
|
if (!itemPosition.contains(".")) {
|
||||||
|
mItemPos = itemPosition;
|
||||||
|
mPageRestoreRequest = null;
|
||||||
|
} else {
|
||||||
|
String[] split = itemPosition.split("\\.", 2);
|
||||||
|
mItemPos = split[0];
|
||||||
|
mPageRestoreRequest = split[1];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (mListener != null) {
|
if (mListener != null) {
|
||||||
mListener.onTitleTextChange(R.string.courses);
|
mListener.onTitleTextChange(R.string.courses);
|
||||||
@@ -76,14 +89,16 @@ public class ModDetailFragment extends Fragment implements ModDetailListener {
|
|||||||
mViewPager = v.findViewById(R.id.vpPager);
|
mViewPager = v.findViewById(R.id.vpPager);
|
||||||
ModDetailAdapter adapterViewPager = new ModDetailAdapter(getChildFragmentManager(), mItemPos, getContext());
|
ModDetailAdapter adapterViewPager = new ModDetailAdapter(getChildFragmentManager(), mItemPos, getContext());
|
||||||
mViewPager.setAdapter(adapterViewPager);
|
mViewPager.setAdapter(adapterViewPager);
|
||||||
|
if (mPageRestoreRequest != null)
|
||||||
|
mViewPager.setCurrentItem(Integer.parseInt(mPageRestoreRequest));
|
||||||
return v;
|
return v;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onAttach(Context context) {
|
public void onAttach(Context context) {
|
||||||
super.onAttach(context);
|
super.onAttach(context);
|
||||||
if (context instanceof MainAcitivityListener) {
|
if (context instanceof MainActivityListener) {
|
||||||
mListener = (MainAcitivityListener) context;
|
mListener = (MainActivityListener) context;
|
||||||
} else {
|
} else {
|
||||||
throw new RuntimeException(context.toString()
|
throw new RuntimeException(context.toString()
|
||||||
+ " must implement MainActivityListener");
|
+ " must implement MainActivityListener");
|
||||||
@@ -100,4 +115,8 @@ public class ModDetailFragment extends Fragment implements ModDetailListener {
|
|||||||
public void gotoFragmentPart(int part, int index) {
|
public void gotoFragmentPart(int part, int index) {
|
||||||
mViewPager.setCurrentItem(ModulePart.getPageByPart(part), true);
|
mViewPager.setCurrentItem(ModulePart.getPageByPart(part), true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getData() {
|
||||||
|
return mItemPos+"."+mViewPager.getCurrentItem();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,6 @@
|
|||||||
package de.sebse.fuplanner.fragments.moddetails;
|
package de.sebse.fuplanner.fragments.moddetails;
|
||||||
|
|
||||||
import android.annotation.SuppressLint;
|
import android.annotation.SuppressLint;
|
||||||
import android.support.annotation.NonNull;
|
|
||||||
import android.support.v7.widget.RecyclerView;
|
|
||||||
import android.util.Pair;
|
import android.util.Pair;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
@@ -11,10 +9,12 @@ import android.widget.TextView;
|
|||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
import de.sebse.fuplanner.R;
|
import de.sebse.fuplanner.R;
|
||||||
import de.sebse.fuplanner.services.KVV.types.Gradebook;
|
import de.sebse.fuplanner.services.KVV.types.Gradebook;
|
||||||
import de.sebse.fuplanner.services.KVV.types.Modules;
|
import de.sebse.fuplanner.services.KVV.types.Modules;
|
||||||
import de.sebse.fuplanner.tools.ui.CustomViewHolder;
|
import de.sebse.fuplanner.tools.ui.StringViewHolder;
|
||||||
|
|
||||||
class ModDetailGradebookAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
|
class ModDetailGradebookAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
|
||||||
private static final int TYPE_TOTAL = 0;
|
private static final int TYPE_TOTAL = 0;
|
||||||
@@ -58,10 +58,10 @@ class ModDetailGradebookAdapter extends RecyclerView.Adapter<RecyclerView.ViewHo
|
|||||||
case TYPE_TOTAL:
|
case TYPE_TOTAL:
|
||||||
view = LayoutInflater.from(parent.getContext())
|
view = LayoutInflater.from(parent.getContext())
|
||||||
.inflate(R.layout.list_all_caption, parent, false);
|
.inflate(R.layout.list_all_caption, parent, false);
|
||||||
return new HeaderViewHolder(view);
|
return new StringViewHolder(view);
|
||||||
case TYPE_GRADE:
|
case TYPE_GRADE:
|
||||||
view = LayoutInflater.from(parent.getContext())
|
view = LayoutInflater.from(parent.getContext())
|
||||||
.inflate(R.layout.list_modetails_gradebook, parent, false);
|
.inflate(R.layout.list_moddetails_gradebook, parent, false);
|
||||||
return new GradebookViewHolder(view);
|
return new GradebookViewHolder(view);
|
||||||
default:
|
default:
|
||||||
//noinspection ConstantConditions
|
//noinspection ConstantConditions
|
||||||
@@ -85,8 +85,8 @@ class ModDetailGradebookAdapter extends RecyclerView.Adapter<RecyclerView.ViewHo
|
|||||||
Pair<Integer, Integer> data = mPositionalData.get(position);
|
Pair<Integer, Integer> data = mPositionalData.get(position);
|
||||||
switch (data.first) {
|
switch (data.first) {
|
||||||
case TYPE_TOTAL:
|
case TYPE_TOTAL:
|
||||||
HeaderViewHolder h = (HeaderViewHolder) holder;
|
StringViewHolder h = (StringViewHolder) holder;
|
||||||
h.mCaption.setText(h.mView.getResources().getString(R.string.current_percentage, mValue.getGradebookPercent()*100));
|
h.mString.setText(h.mView.getResources().getString(R.string.current_percentage, mValue.getGradebookPercent()*100));
|
||||||
break;
|
break;
|
||||||
case TYPE_GRADE:
|
case TYPE_GRADE:
|
||||||
int index = data.second / 1024;
|
int index = data.second / 1024;
|
||||||
@@ -118,20 +118,6 @@ class ModDetailGradebookAdapter extends RecyclerView.Adapter<RecyclerView.ViewHo
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
class HeaderViewHolder extends CustomViewHolder {
|
|
||||||
final TextView mCaption;
|
|
||||||
|
|
||||||
HeaderViewHolder(View view) {
|
|
||||||
super(view);
|
|
||||||
mCaption = view.findViewById(R.id.caption);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return super.toString() + " '" + mCaption.getText() + "'";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private class GradebookViewHolder extends RecyclerView.ViewHolder {
|
private class GradebookViewHolder extends RecyclerView.ViewHolder {
|
||||||
private final TextView mGrade;
|
private final TextView mGrade;
|
||||||
|
|||||||
@@ -3,15 +3,15 @@ package de.sebse.fuplanner.fragments.moddetails;
|
|||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.support.annotation.NonNull;
|
|
||||||
import android.support.v4.app.Fragment;
|
|
||||||
import android.support.v4.widget.SwipeRefreshLayout;
|
|
||||||
import android.support.v7.widget.LinearLayoutManager;
|
|
||||||
import android.support.v7.widget.RecyclerView;
|
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.fragment.app.Fragment;
|
||||||
|
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||||
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
|
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
|
||||||
import de.sebse.fuplanner.MainActivity;
|
import de.sebse.fuplanner.MainActivity;
|
||||||
import de.sebse.fuplanner.R;
|
import de.sebse.fuplanner.R;
|
||||||
import de.sebse.fuplanner.services.KVV.KVV;
|
import de.sebse.fuplanner.services.KVV.KVV;
|
||||||
|
|||||||
@@ -1,27 +1,27 @@
|
|||||||
package de.sebse.fuplanner.fragments.moddetails;
|
package de.sebse.fuplanner.fragments.moddetails;
|
||||||
|
|
||||||
import android.support.annotation.NonNull;
|
|
||||||
import android.support.annotation.Nullable;
|
|
||||||
import android.support.v7.widget.RecyclerView;
|
|
||||||
import android.util.Pair;
|
import android.util.Pair;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
import android.widget.TextView;
|
|
||||||
|
|
||||||
import com.ms.square.android.expandabletextview.ExpandableTextView;
|
import com.ms.square.android.expandabletextview.ExpandableTextView;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
import de.sebse.fuplanner.R;
|
import de.sebse.fuplanner.R;
|
||||||
import de.sebse.fuplanner.services.KVV.types.Announcement;
|
import de.sebse.fuplanner.services.KVV.types.Announcement;
|
||||||
import de.sebse.fuplanner.services.KVV.types.Assignment;
|
import de.sebse.fuplanner.services.KVV.types.Assignment;
|
||||||
import de.sebse.fuplanner.services.KVV.types.Event;
|
import de.sebse.fuplanner.services.KVV.types.Event;
|
||||||
import de.sebse.fuplanner.services.KVV.types.Modules;
|
import de.sebse.fuplanner.services.KVV.types.Modules;
|
||||||
import de.sebse.fuplanner.tools.DateUtils;
|
import de.sebse.fuplanner.tools.UtilsDate;
|
||||||
import de.sebse.fuplanner.tools.logging.Logger;
|
import de.sebse.fuplanner.tools.logging.Logger;
|
||||||
import de.sebse.fuplanner.tools.ui.CustomViewHolder;
|
import de.sebse.fuplanner.tools.ui.CustomViewHolder;
|
||||||
import de.sebse.fuplanner.tools.ui.ItemViewHolder;
|
import de.sebse.fuplanner.tools.ui.ItemViewHolder;
|
||||||
|
import de.sebse.fuplanner.tools.ui.StringViewHolder;
|
||||||
|
|
||||||
class ModDetailOverviewAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
|
class ModDetailOverviewAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
|
||||||
private static final int MAX_ITEMS_PER_PREVIEW = 2;
|
private static final int MAX_ITEMS_PER_PREVIEW = 2;
|
||||||
@@ -77,7 +77,7 @@ class ModDetailOverviewAdapter extends RecyclerView.Adapter<RecyclerView.ViewHol
|
|||||||
case TYPE_HEADER:
|
case TYPE_HEADER:
|
||||||
view = LayoutInflater.from(parent.getContext())
|
view = LayoutInflater.from(parent.getContext())
|
||||||
.inflate(R.layout.list_all_caption, parent, false);
|
.inflate(R.layout.list_all_caption, parent, false);
|
||||||
return new HeaderViewHolder(view);
|
return new StringViewHolder(view);
|
||||||
case TYPE_DESCRIPTION:
|
case TYPE_DESCRIPTION:
|
||||||
view = LayoutInflater.from(parent.getContext())
|
view = LayoutInflater.from(parent.getContext())
|
||||||
.inflate(R.layout.list_moddetails_description, parent, false);
|
.inflate(R.layout.list_moddetails_description, parent, false);
|
||||||
@@ -111,19 +111,19 @@ class ModDetailOverviewAdapter extends RecyclerView.Adapter<RecyclerView.ViewHol
|
|||||||
Pair<Integer, Object> data = mPositionalData.get(position);
|
Pair<Integer, Object> data = mPositionalData.get(position);
|
||||||
switch (data.first) {
|
switch (data.first) {
|
||||||
case TYPE_HEADER:
|
case TYPE_HEADER:
|
||||||
HeaderViewHolder h = (HeaderViewHolder) holder;
|
StringViewHolder h = (StringViewHolder) holder;
|
||||||
switch ((Integer) data.second) {
|
switch ((Integer) data.second) {
|
||||||
case ModulePart.DESCRIPTION:
|
case ModulePart.DESCRIPTION:
|
||||||
h.mCaption.setText(R.string.description);
|
h.mString.setText(R.string.description);
|
||||||
break;
|
break;
|
||||||
case ModulePart.ANNOUNCEMENT:
|
case ModulePart.ANNOUNCEMENT:
|
||||||
h.mCaption.setText(h.mView.getResources().getString(R.string.announcements_count, getAnnounceCount()));
|
h.mString.setText(h.mView.getResources().getString(R.string.announcements_count, getAnnounceCount()));
|
||||||
break;
|
break;
|
||||||
case ModulePart.ASSIGNMENT:
|
case ModulePart.ASSIGNMENT:
|
||||||
h.mCaption.setText(h.mView.getResources().getString(R.string.assignments_count, getAssignmentCount()));
|
h.mString.setText(h.mView.getResources().getString(R.string.assignments_count, getAssignmentCount()));
|
||||||
break;
|
break;
|
||||||
case ModulePart.EVENT:
|
case ModulePart.EVENT:
|
||||||
h.mCaption.setText(h.mView.getResources().getString(R.string.upcoming_events_count, getEventsCount()));
|
h.mString.setText(h.mView.getResources().getString(R.string.upcoming_events_count, getEventsCount()));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@@ -141,7 +141,7 @@ class ModDetailOverviewAdapter extends RecyclerView.Adapter<RecyclerView.ViewHol
|
|||||||
Announcement announce = mValue.announcements.get(index);
|
Announcement announce = mValue.announcements.get(index);
|
||||||
i.mTitle.setText(announce.getTitle());
|
i.mTitle.setText(announce.getTitle());
|
||||||
i.mSubLeft.setText(announce.getCreatedBy());
|
i.mSubLeft.setText(announce.getCreatedBy());
|
||||||
i.mSubRight.setText(DateUtils.getModifiedDateTime(i.mView.getContext(), announce.getCreatedOn()));
|
i.mSubRight.setText(UtilsDate.getModifiedDateTime(i.mView.getContext(), announce.getCreatedOn()));
|
||||||
i.mView.setOnClickListener(view -> {
|
i.mView.setOnClickListener(view -> {
|
||||||
if (mListener != null) mListener.gotoFragmentPart(section, index);
|
if (mListener != null) mListener.gotoFragmentPart(section, index);
|
||||||
});
|
});
|
||||||
@@ -154,7 +154,7 @@ class ModDetailOverviewAdapter extends RecyclerView.Adapter<RecyclerView.ViewHol
|
|||||||
i.mSubLeft.setText(i.mView.getResources().getText(R.string.open));
|
i.mSubLeft.setText(i.mView.getResources().getText(R.string.open));
|
||||||
else
|
else
|
||||||
i.mSubLeft.setText(i.mView.getResources().getText(R.string.closed));
|
i.mSubLeft.setText(i.mView.getResources().getText(R.string.closed));
|
||||||
i.mSubRight.setText(DateUtils.getModifiedDateTime(i.mView.getContext(), assignment.getDueDate()));
|
i.mSubRight.setText(UtilsDate.getModifiedDateTime(i.mView.getContext(), assignment.getDueDate()));
|
||||||
i.mView.setOnClickListener(view -> {
|
i.mView.setOnClickListener(view -> {
|
||||||
if (mListener != null) mListener.gotoFragmentPart(section, index);
|
if (mListener != null) mListener.gotoFragmentPart(section, index);
|
||||||
});
|
});
|
||||||
@@ -165,14 +165,14 @@ class ModDetailOverviewAdapter extends RecyclerView.Adapter<RecyclerView.ViewHol
|
|||||||
i.mTitle.setText(event.getTitle());
|
i.mTitle.setText(event.getTitle());
|
||||||
i.mSubLeft.setText(event.getType());
|
i.mSubLeft.setText(event.getType());
|
||||||
String start, end;
|
String start, end;
|
||||||
if (DateUtils.dateEquals(event.getStartDate(), System.currentTimeMillis()))
|
if (UtilsDate.dateEquals(event.getStartDate(), System.currentTimeMillis()))
|
||||||
start = DateUtils.getModifiedTime(i.mView.getContext(), event.getStartDate());
|
start = UtilsDate.getModifiedTime(i.mView.getContext(), event.getStartDate());
|
||||||
else
|
else
|
||||||
start = DateUtils.getModifiedDateTime(i.mView.getContext(), event.getStartDate());
|
start = UtilsDate.getModifiedDateTime(i.mView.getContext(), event.getStartDate());
|
||||||
if (DateUtils.dateEquals(event.getStartDate(), event.getEndDate()))
|
if (UtilsDate.dateEquals(event.getStartDate(), event.getEndDate()))
|
||||||
end = DateUtils.getModifiedTime(i.mView.getContext(), event.getEndDate());
|
end = UtilsDate.getModifiedTime(i.mView.getContext(), event.getEndDate());
|
||||||
else
|
else
|
||||||
end = DateUtils.getModifiedDateTime(i.mView.getContext(), event.getEndDate());
|
end = UtilsDate.getModifiedDateTime(i.mView.getContext(), event.getEndDate());
|
||||||
i.mSubRight.setText(i.mView.getResources().getString(R.string.date_scale,
|
i.mSubRight.setText(i.mView.getResources().getString(R.string.date_scale,
|
||||||
start, end
|
start, end
|
||||||
));
|
));
|
||||||
@@ -221,19 +221,7 @@ class ModDetailOverviewAdapter extends RecyclerView.Adapter<RecyclerView.ViewHol
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
class HeaderViewHolder extends CustomViewHolder {
|
|
||||||
final TextView mCaption;
|
|
||||||
|
|
||||||
HeaderViewHolder(View view) {
|
|
||||||
super(view);
|
|
||||||
mCaption = view.findViewById(R.id.caption);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return super.toString() + " '" + mCaption.getText() + "'";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class DescriptionViewHolder extends CustomViewHolder {
|
class DescriptionViewHolder extends CustomViewHolder {
|
||||||
final ExpandableTextView mText;
|
final ExpandableTextView mText;
|
||||||
|
|||||||
@@ -3,16 +3,16 @@ package de.sebse.fuplanner.fragments.moddetails;
|
|||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.support.annotation.NonNull;
|
|
||||||
import android.support.annotation.Nullable;
|
|
||||||
import android.support.v4.app.Fragment;
|
|
||||||
import android.support.v4.widget.SwipeRefreshLayout;
|
|
||||||
import android.support.v7.widget.LinearLayoutManager;
|
|
||||||
import android.support.v7.widget.RecyclerView;
|
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
import androidx.fragment.app.Fragment;
|
||||||
|
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||||
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
|
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
|
||||||
import de.sebse.fuplanner.MainActivity;
|
import de.sebse.fuplanner.MainActivity;
|
||||||
import de.sebse.fuplanner.R;
|
import de.sebse.fuplanner.R;
|
||||||
import de.sebse.fuplanner.services.KVV.KVV;
|
import de.sebse.fuplanner.services.KVV.KVV;
|
||||||
|
|||||||
@@ -0,0 +1,34 @@
|
|||||||
|
package de.sebse.fuplanner.fragments.moddetails;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import de.sebse.fuplanner.services.KVV.types.Modules;
|
||||||
|
import de.sebse.fuplanner.services.KVV.types.Resource;
|
||||||
|
import de.sebse.fuplanner.tools.ui.treeview.TreeNode;
|
||||||
|
import de.sebse.fuplanner.tools.ui.treeview.TreeViewAdapter;
|
||||||
|
import de.sebse.fuplanner.tools.ui.treeview.TreeViewBinder;
|
||||||
|
|
||||||
|
class ModDetailResourceAdapter extends TreeViewAdapter {
|
||||||
|
private Modules.Module mValue;
|
||||||
|
public ModDetailResourceAdapter(List<? extends TreeViewBinder> viewBinders) {
|
||||||
|
super(viewBinders);
|
||||||
|
mValue = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setModule(Modules.Module module) {
|
||||||
|
mValue = module;
|
||||||
|
this.setModule();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setModule() {
|
||||||
|
if (mValue == null || mValue.resources == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
List<TreeNode> nodes = new ArrayList<>();
|
||||||
|
for (Resource res: mValue.resources) {
|
||||||
|
nodes.add(res.getTreeNode());
|
||||||
|
}
|
||||||
|
refresh(nodes);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,344 @@
|
|||||||
|
package de.sebse.fuplanner.fragments.moddetails;
|
||||||
|
|
||||||
|
|
||||||
|
import android.Manifest;
|
||||||
|
import android.app.AlertDialog;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.content.pm.PackageManager;
|
||||||
|
import android.net.Uri;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.os.Environment;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import android.widget.ImageView;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.core.app.ActivityCompat;
|
||||||
|
import androidx.fragment.app.Fragment;
|
||||||
|
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||||
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
|
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
|
||||||
|
import de.sebse.fuplanner.MainActivity;
|
||||||
|
import de.sebse.fuplanner.R;
|
||||||
|
import de.sebse.fuplanner.services.KVV.KVV;
|
||||||
|
import de.sebse.fuplanner.services.KVV.types.Modules;
|
||||||
|
import de.sebse.fuplanner.services.KVV.types.Resource;
|
||||||
|
import de.sebse.fuplanner.tools.MainActivityListener;
|
||||||
|
import de.sebse.fuplanner.tools.Regex;
|
||||||
|
import de.sebse.fuplanner.tools.UtilsDate;
|
||||||
|
import de.sebse.fuplanner.tools.logging.Logger;
|
||||||
|
import de.sebse.fuplanner.tools.ui.treeview.DirectoryNodeBinder;
|
||||||
|
import de.sebse.fuplanner.tools.ui.treeview.FileNodeBinder;
|
||||||
|
import de.sebse.fuplanner.tools.ui.treeview.TreeNode;
|
||||||
|
import de.sebse.fuplanner.tools.ui.treeview.TreeViewAdapter;
|
||||||
|
|
||||||
|
import static androidx.core.content.ContextCompat.checkSelfPermission;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A simple {@link Fragment} subclass.
|
||||||
|
* Use the {@link ModDetailResourceFragment#newInstance} factory method to
|
||||||
|
* create an instance of this fragment.
|
||||||
|
*/
|
||||||
|
public class ModDetailResourceFragment extends Fragment {
|
||||||
|
private static final String ARG_POSITION = "itemPosition";
|
||||||
|
|
||||||
|
private String mItemPos;
|
||||||
|
private final Logger log = new Logger(this);
|
||||||
|
private ModDetailResourceAdapter adapter;
|
||||||
|
private SwipeRefreshLayout swipeLayout;
|
||||||
|
private MainActivityListener context;
|
||||||
|
private RequestedDownload requestedDownload;
|
||||||
|
|
||||||
|
|
||||||
|
public ModDetailResourceFragment() {
|
||||||
|
// Required empty public constructor
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use this factory method to create a new instance of
|
||||||
|
* this fragment using the provided parameters.
|
||||||
|
*
|
||||||
|
* @param itemPosition Item position in module list.
|
||||||
|
* @return A new instance of fragment ModDetailAnnounceFragment.
|
||||||
|
*/
|
||||||
|
public static ModDetailResourceFragment newInstance(String itemPosition) {
|
||||||
|
ModDetailResourceFragment fragment = new ModDetailResourceFragment();
|
||||||
|
Bundle args = new Bundle();
|
||||||
|
args.putString(ARG_POSITION, itemPosition);
|
||||||
|
fragment.setArguments(args);
|
||||||
|
return fragment;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCreate(Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
if (getArguments() != null) {
|
||||||
|
mItemPos = getArguments().getString(ARG_POSITION);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container,
|
||||||
|
Bundle savedInstanceState) {
|
||||||
|
// Inflate the layout for this fragment
|
||||||
|
View view = inflater.inflate(R.layout.fragment_recycler_view, container, false);
|
||||||
|
// Set the adapter
|
||||||
|
Context context = view.getContext();
|
||||||
|
RecyclerView recyclerView = view.findViewById(R.id.list);
|
||||||
|
recyclerView.setLayoutManager(new LinearLayoutManager(context));
|
||||||
|
|
||||||
|
adapter = new ModDetailResourceAdapter(Arrays.asList(new FileNodeBinder(), new DirectoryNodeBinder()));
|
||||||
|
adapter.setOnTreeNodeListener(new TreeViewAdapter.OnTreeNodeListener() {
|
||||||
|
@Override
|
||||||
|
public boolean onClick(TreeNode node, RecyclerView.ViewHolder holder) {
|
||||||
|
if (!node.isLeaf()) {
|
||||||
|
// Update and toggle the node.
|
||||||
|
onToggle(!node.isExpand(), holder);
|
||||||
|
} else if(node.getContent() instanceof Resource.File) { // if leaf is file
|
||||||
|
KVV kvv = ModDetailResourceFragment.this.context.getKVV();
|
||||||
|
kvv.getModule(mItemPos, (Modules.Module module) -> {
|
||||||
|
if (getContext() == null)
|
||||||
|
return;
|
||||||
|
AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(getContext());
|
||||||
|
Resource.File file = (Resource.File) node.getContent();
|
||||||
|
String folderName = "FU-"+module.title.replaceAll("[:*<>|/\"\\\\]", "-");
|
||||||
|
File f = new File(Environment.getExternalStoragePublicDirectory(
|
||||||
|
Environment.DIRECTORY_DOWNLOADS)+"/"+folderName+"/"+file.getTitle());
|
||||||
|
alertDialogBuilder
|
||||||
|
.setTitle(file.getTitle())
|
||||||
|
.setMessage(
|
||||||
|
getResources().getString(R.string.creator_name, file.getAuthor()) + "\n" +
|
||||||
|
getResources().getString(R.string.last_modified_on, UtilsDate.getModifiedDateTime(context, file.getModifiedDate()))
|
||||||
|
)
|
||||||
|
.setCancelable(true)
|
||||||
|
.setNeutralButton(R.string.close, (dialog, id) -> dialog.cancel())
|
||||||
|
.setPositiveButton(R.string.download, (dialog, id) -> download(file, folderName, true));
|
||||||
|
// if already downloaded, show open button
|
||||||
|
if (f.exists()) {
|
||||||
|
alertDialogBuilder
|
||||||
|
.setNegativeButton(R.string.openFile, (dialog, id) -> download(file, folderName, false));
|
||||||
|
}
|
||||||
|
AlertDialog alertDialog = alertDialogBuilder.create();
|
||||||
|
alertDialog.show();
|
||||||
|
|
||||||
|
}, log::e);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onToggle(boolean isExpand, RecyclerView.ViewHolder holder) {
|
||||||
|
DirectoryNodeBinder.ViewHolder dirViewHolder = (DirectoryNodeBinder.ViewHolder) holder;
|
||||||
|
final ImageView ivArrow = dirViewHolder.getIvArrow();
|
||||||
|
int rotateDegree = isExpand ? 90 : -90;
|
||||||
|
ivArrow.animate().rotationBy(rotateDegree).start();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
recyclerView.setAdapter(adapter);
|
||||||
|
|
||||||
|
// Getting SwipeContainerLayout
|
||||||
|
swipeLayout = view.findViewById(R.id.swipe_container);
|
||||||
|
// Adding Listener
|
||||||
|
swipeLayout.setOnRefreshListener(() -> refresh(true));
|
||||||
|
refresh(false);
|
||||||
|
|
||||||
|
return view;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void download(Resource.File file, String folderName, boolean downloadNew){
|
||||||
|
if (getActivity() == null) {
|
||||||
|
showDownloadError();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (checkSelfPermission(getActivity(), android.Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED) {
|
||||||
|
// Access granted
|
||||||
|
downloadOrOpen(file, folderName, downloadNew);
|
||||||
|
} else {
|
||||||
|
this.requestedDownload = new RequestedDownload(file, folderName, downloadNew);
|
||||||
|
ActivityCompat.requestPermissions(ModDetailResourceFragment.super.getActivity(),
|
||||||
|
new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},
|
||||||
|
1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void downloadOrOpen(Resource.File file, String folderName, boolean downloadNew) {
|
||||||
|
KVV kvv = this.context.getKVV();
|
||||||
|
if(isExternalStorageWritable()){
|
||||||
|
kvv.getResourceFile(success1 -> {
|
||||||
|
// Downloading file failed
|
||||||
|
if (success1.equals("")){
|
||||||
|
showDownloadError();
|
||||||
|
}else {
|
||||||
|
if (Regex.has("^http", success1)){
|
||||||
|
Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(success1));
|
||||||
|
startActivity(intent);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
fileOpen(new File(success1));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}, log::e, file.getTitle(), file.getUrl(), folderName, downloadNew);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void showDownloadError() {
|
||||||
|
AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(getContext());
|
||||||
|
alertDialogBuilder
|
||||||
|
.setTitle(R.string.ErrorFileDownload)
|
||||||
|
.setMessage(
|
||||||
|
R.string.ErrorFileDownloadText
|
||||||
|
)
|
||||||
|
.setCancelable(true)
|
||||||
|
.setNeutralButton(R.string.close, (dialog, id) -> dialog.cancel());
|
||||||
|
AlertDialog alertDialog = alertDialogBuilder.create();
|
||||||
|
alertDialog.show();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onAttach(Context context) {
|
||||||
|
super.onAttach(context);
|
||||||
|
if (context instanceof MainActivityListener) {
|
||||||
|
this.context = ((MainActivityListener) context);
|
||||||
|
this.context.addRequestPermissionsResultListener((requestCode, permissions, grantResults) -> {
|
||||||
|
if (requestedDownload == null) {
|
||||||
|
log.d("No request");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (getActivity() == null) {
|
||||||
|
showDownloadError();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ArrayList<Integer> intList = new ArrayList<>();
|
||||||
|
for (int i : grantResults)
|
||||||
|
{
|
||||||
|
intList.add(i);
|
||||||
|
}
|
||||||
|
log.d(requestCode, Arrays.asList(permissions), intList);
|
||||||
|
int pos = Arrays.asList(permissions).indexOf("android.permission.WRITE_EXTERNAL_STORAGE");
|
||||||
|
if (pos != -1) {
|
||||||
|
if (grantResults[pos] != -1) {
|
||||||
|
downloadOrOpen(requestedDownload.file, requestedDownload.folderName, requestedDownload.downloadNew);
|
||||||
|
} else {
|
||||||
|
log.d(requestedDownload, pos, grantResults[pos]);
|
||||||
|
showDownloadError();
|
||||||
|
}
|
||||||
|
requestedDownload = null;
|
||||||
|
}
|
||||||
|
}, "ModDetailResourceFragment");
|
||||||
|
} else
|
||||||
|
throw new RuntimeException(context.toString() + " must implement MainActivityListener");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDetach() {
|
||||||
|
super.onDetach();
|
||||||
|
this.context.removeRequestPermissionsResultListener("ModDetailResourceFragment");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Checks if external storage is available for read and write */
|
||||||
|
private boolean isExternalStorageWritable() {
|
||||||
|
String state = Environment.getExternalStorageState();
|
||||||
|
if (Environment.MEDIA_MOUNTED.equals(state)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
log.e("File system: Writing not possible");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void fileOpen(File url){
|
||||||
|
Uri uri = Uri.fromFile(url);
|
||||||
|
|
||||||
|
Intent intent = new Intent();//Intent.ACTION_VIEW
|
||||||
|
// Check what kind of file you are trying to open, by comparing the url with extensions.
|
||||||
|
// When the if condition is matched, plugin sets the correct intent (mime) type,
|
||||||
|
// so Android knew what application to use to open the file
|
||||||
|
if (url.toString().contains(".doc") || url.toString().contains(".docx")) {
|
||||||
|
// Word document
|
||||||
|
intent.setDataAndType(uri, "application/msword");
|
||||||
|
} else if(url.toString().contains(".pdf")) {
|
||||||
|
// PDF file
|
||||||
|
intent.setDataAndType(uri, "application/pdf");
|
||||||
|
} else if(url.toString().contains(".ppt") || url.toString().contains(".pptx")) {
|
||||||
|
// Powerpoint file
|
||||||
|
intent.setDataAndType(uri, "application/vnd.ms-powerpoint");
|
||||||
|
} else if(url.toString().contains(".xls") || url.toString().contains(".xlsx")) {
|
||||||
|
// Excel file
|
||||||
|
intent.setDataAndType(uri, "application/vnd.ms-excel");
|
||||||
|
} else if(url.toString().contains(".zip") || url.toString().contains(".rar")) {
|
||||||
|
// ZIP file
|
||||||
|
intent.setDataAndType(uri, "application/zip");
|
||||||
|
} else if(url.toString().contains(".rtf")) {
|
||||||
|
// RTF file
|
||||||
|
intent.setDataAndType(uri, "application/rtf");
|
||||||
|
} else if(url.toString().contains(".wav") || url.toString().contains(".mp3")) {
|
||||||
|
// WAV audio file
|
||||||
|
intent.setDataAndType(uri, "audio/x-wav");
|
||||||
|
} else if(url.toString().contains(".gif")) {
|
||||||
|
// GIF file
|
||||||
|
intent.setDataAndType(uri, "image/gif");
|
||||||
|
} else if(url.toString().contains(".jpg") || url.toString().contains(".jpeg") || url.toString().contains(".png")) {
|
||||||
|
// JPG file
|
||||||
|
intent.setDataAndType(uri, "image/jpeg");
|
||||||
|
} else if(url.toString().contains(".txt")) {
|
||||||
|
// Text file
|
||||||
|
intent.setDataAndType(uri, "text/plain");
|
||||||
|
} else if(url.toString().contains(".3gp") || url.toString().contains(".mpg") || url.toString().contains(".mpeg") || url.toString().contains(".mpe") || url.toString().contains(".mp4") || url.toString().contains(".avi")) {
|
||||||
|
// Video files
|
||||||
|
intent.setDataAndType(uri, "video/*");
|
||||||
|
} else {
|
||||||
|
//if you want you can also define the intent type for any other file
|
||||||
|
|
||||||
|
//additionally use else clause below, to manage other unknown extensions
|
||||||
|
//in this case, Android will show all applications installed on the device
|
||||||
|
//so you can choose which application to use
|
||||||
|
intent.setDataAndType(uri, "*/*");
|
||||||
|
}
|
||||||
|
|
||||||
|
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||||
|
startActivity(intent);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private void refresh(boolean forceRefresh) {
|
||||||
|
if (getActivity() != null) {
|
||||||
|
KVV kvv = ((MainActivity) getActivity()).getKVV();
|
||||||
|
kvv.getModule(mItemPos, (Modules.Module module) -> {
|
||||||
|
adapter.setModule(module);
|
||||||
|
kvv.getModuleResources(module, success1 -> {
|
||||||
|
adapter.setModule();
|
||||||
|
swipeLayout.setRefreshing(false);
|
||||||
|
}, error -> {
|
||||||
|
swipeLayout.setRefreshing(false);
|
||||||
|
log.e(error);
|
||||||
|
}, forceRefresh);
|
||||||
|
}, error -> {
|
||||||
|
swipeLayout.setRefreshing(false);
|
||||||
|
log.e(error);
|
||||||
|
}, forceRefresh);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
private class RequestedDownload {
|
||||||
|
Resource.File file;
|
||||||
|
String folderName;
|
||||||
|
boolean downloadNew;
|
||||||
|
|
||||||
|
RequestedDownload(Resource.File file, String folderName, boolean downloadNew) {
|
||||||
|
this.file = file;
|
||||||
|
this.folderName = folderName;
|
||||||
|
this.downloadNew = downloadNew;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -7,7 +7,8 @@ class ModulePart {
|
|||||||
static final int ASSIGNMENT = 3;
|
static final int ASSIGNMENT = 3;
|
||||||
static final int EVENT = 4;
|
static final int EVENT = 4;
|
||||||
static final int GRADEBOOK = 5;
|
static final int GRADEBOOK = 5;
|
||||||
private static final int[] pages = new int[]{OVERVIEW, ANNOUNCEMENT, ASSIGNMENT, EVENT, GRADEBOOK};
|
static final int RESOURCES = 6;
|
||||||
|
private static final int[] pages = new int[]{OVERVIEW, ANNOUNCEMENT, ASSIGNMENT, EVENT, GRADEBOOK, RESOURCES};
|
||||||
|
|
||||||
static int getPageCount() {
|
static int getPageCount() {
|
||||||
return pages.length;
|
return pages.length;
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ import de.sebse.fuplanner.services.Canteen.types.Canteen;
|
|||||||
import de.sebse.fuplanner.services.Canteen.types.Canteens;
|
import de.sebse.fuplanner.services.Canteen.types.Canteens;
|
||||||
import de.sebse.fuplanner.services.Canteen.types.Day;
|
import de.sebse.fuplanner.services.Canteen.types.Day;
|
||||||
import de.sebse.fuplanner.tools.AsyncQueue;
|
import de.sebse.fuplanner.tools.AsyncQueue;
|
||||||
import de.sebse.fuplanner.tools.MainAcitivityListener;
|
import de.sebse.fuplanner.tools.MainActivityListener;
|
||||||
import de.sebse.fuplanner.tools.network.HTTPService;
|
import de.sebse.fuplanner.tools.network.HTTPService;
|
||||||
import de.sebse.fuplanner.tools.network.NetworkCallback;
|
import de.sebse.fuplanner.tools.network.NetworkCallback;
|
||||||
import de.sebse.fuplanner.tools.network.NetworkError;
|
import de.sebse.fuplanner.tools.network.NetworkError;
|
||||||
@@ -23,12 +23,12 @@ public class CanteenBrowser extends HTTPService {
|
|||||||
private Canteens canteens;
|
private Canteens canteens;
|
||||||
private final AsyncQueue queue = new AsyncQueue();
|
private final AsyncQueue queue = new AsyncQueue();
|
||||||
private final Context context;
|
private final Context context;
|
||||||
private MainAcitivityListener mListener;
|
private MainActivityListener mListener;
|
||||||
|
|
||||||
public CanteenBrowser(Context context) {
|
public CanteenBrowser(Context context) {
|
||||||
super(context);
|
super(context);
|
||||||
this.context = context;
|
this.context = context;
|
||||||
if (context instanceof MainAcitivityListener)
|
if (context instanceof MainActivityListener)
|
||||||
mListener = (MainActivity) context;
|
mListener = (MainActivity) context;
|
||||||
else
|
else
|
||||||
throw new RuntimeException(context.toString() + "must implement MainActivityListener");
|
throw new RuntimeException(context.toString() + "must implement MainActivityListener");
|
||||||
@@ -65,7 +65,7 @@ public class CanteenBrowser extends HTTPService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void upgradeCanteens(final NetworkCallback<Canteens> callback, final NetworkErrorCallback errorCallback) {
|
private void upgradeCanteens(final NetworkCallback<Canteens> callback, final NetworkErrorCallback errorCallback) {
|
||||||
get("https://openmensa.org/api/v2/canteens", null, response -> {
|
get("https://openmensa.org/api/v2/canteens?near[lat]=52.449743&near[lng]=13.282245&near[dist]=7", null, response -> {
|
||||||
String body = response.getParsed();
|
String body = response.getParsed();
|
||||||
if (body == null) {
|
if (body == null) {
|
||||||
errorCallback.onError(new NetworkError(201101, 403, "No canteen list retrieved!"));
|
errorCallback.onError(new NetworkError(201101, 403, "No canteen list retrieved!"));
|
||||||
@@ -192,9 +192,9 @@ public class CanteenBrowser extends HTTPService {
|
|||||||
double priceEmply = 0;
|
double priceEmply = 0;
|
||||||
double priceOther = 0;
|
double priceOther = 0;
|
||||||
if (prices != null) {
|
if (prices != null) {
|
||||||
priceStdnt = prices.getDouble("students");
|
priceOther = prices.optDouble("others", -1);
|
||||||
priceEmply = prices.getDouble("employees");
|
priceEmply = prices.optDouble("employees", priceOther);
|
||||||
priceOther = prices.getDouble("others");
|
priceStdnt = prices.optDouble("students", priceEmply);
|
||||||
}
|
}
|
||||||
JSONArray noteArray = meal.getJSONArray("notes");
|
JSONArray noteArray = meal.getJSONArray("notes");
|
||||||
String[] notes = new String[noteArray.length()];
|
String[] notes = new String[noteArray.length()];
|
||||||
@@ -206,6 +206,7 @@ public class CanteenBrowser extends HTTPService {
|
|||||||
}
|
}
|
||||||
} catch (JSONException e) {
|
} catch (JSONException e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
|
log.d(body);
|
||||||
errorCallback.onError(new NetworkError(201302, 403, "Cannot parse meal list!"));
|
errorCallback.onError(new NetworkError(201302, 403, "Cannot parse meal list!"));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -224,17 +225,14 @@ public class CanteenBrowser extends HTTPService {
|
|||||||
private<T> NetworkCallback<T> saveOnCallback(NetworkCallback<T> callback, boolean forceRefresh){
|
private<T> NetworkCallback<T> saveOnCallback(NetworkCallback<T> callback, boolean forceRefresh){
|
||||||
return (success -> {
|
return (success -> {
|
||||||
if (forceRefresh)
|
if (forceRefresh)
|
||||||
mListener.refreshFailed(false);
|
mListener.onRefreshCompleted(false);
|
||||||
callback.onResponse(success);
|
callback.onResponse(success);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private NetworkErrorCallback errorOnCallback(NetworkErrorCallback errorCallback){
|
private NetworkErrorCallback errorOnCallback(NetworkErrorCallback errorCallback){
|
||||||
return (error -> {
|
return (error -> {
|
||||||
if (error.getHttpStatus() == 401 || error.getHttpStatus() == 403)
|
mListener.onRefreshCompleted(true);
|
||||||
mListener.loginTokenInvalid(false);
|
|
||||||
else
|
|
||||||
mListener.refreshFailed(true);
|
|
||||||
errorCallback.onError(error);
|
errorCallback.onError(error);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
package de.sebse.fuplanner.services.Canteen.types;
|
package de.sebse.fuplanner.services.Canteen.types;
|
||||||
|
|
||||||
import android.support.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
|
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
import java.util.Calendar;
|
import java.util.Calendar;
|
||||||
@@ -50,6 +50,17 @@ public class Canteen implements Serializable, Iterable<Day> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void cleanUpDays() {
|
||||||
|
SortedListDay newList = new SortedListDay();
|
||||||
|
Calendar cal = Calendar.getInstance();
|
||||||
|
for (Day day : list) {
|
||||||
|
if (Canteen.calendarToKey(day.getCalendar()).compareTo(Canteen.calendarToKey(cal)) >= 0) {
|
||||||
|
newList.add(day);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
list = newList;
|
||||||
|
}
|
||||||
|
|
||||||
public int size() {
|
public int size() {
|
||||||
return this.list.size();
|
return this.list.size();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
package de.sebse.fuplanner.services.Canteen.types;
|
package de.sebse.fuplanner.services.Canteen.types;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.support.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
|
|
||||||
import java.io.FileInputStream;
|
import java.io.FileInputStream;
|
||||||
import java.io.FileOutputStream;
|
import java.io.FileOutputStream;
|
||||||
@@ -12,7 +12,7 @@ import java.io.Serializable;
|
|||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
|
|
||||||
public class Canteens implements Serializable, Iterable<Canteen> {
|
public class Canteens implements Serializable, Iterable<Canteen> {
|
||||||
public static final int[] availableCanteens = {27, 28, 42};
|
public static final int[] availableCanteens = {27, 42, 813, 810, 811, 812, 821};
|
||||||
private static final String FILE_NAME = "CanteensSaving";
|
private static final String FILE_NAME = "CanteensSaving";
|
||||||
private SortedListCanteen list = new SortedListCanteen();
|
private SortedListCanteen list = new SortedListCanteen();
|
||||||
|
|
||||||
@@ -68,7 +68,7 @@ public class Canteens implements Serializable, Iterable<Canteen> {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void remove() {
|
public void remove() {
|
||||||
throw new UnsupportedOperationException("You are not alloed to remove an entry!");
|
throw new UnsupportedOperationException("You are not allowed to remove an entry!");
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
package de.sebse.fuplanner.services.Canteen.types;
|
package de.sebse.fuplanner.services.Canteen.types;
|
||||||
|
|
||||||
import android.support.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
|
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
import java.util.Calendar;
|
import java.util.Calendar;
|
||||||
|
|||||||
@@ -6,14 +6,14 @@ import java.util.Collections;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
public class Meal implements Serializable {
|
public class Meal implements Serializable {
|
||||||
public static final int LIGHT_NONE = 0;
|
private static final int LIGHT_NONE = 0;
|
||||||
public static final int LIGHT_GREEN = 1;
|
private static final int LIGHT_GREEN = 1;
|
||||||
public static final int LIGHT_YELLOW = 2;
|
private static final int LIGHT_YELLOW = 2;
|
||||||
public static final int LIGHT_RED = 3;
|
private static final int LIGHT_RED = 3;
|
||||||
public static final int VEGAN_NONE = 0;
|
private static final int VEGAN_NONE = 0;
|
||||||
public static final int VEGAN_VEGETERIAN = 1;
|
public static final int VEGAN_VEGETARIAN = 1;
|
||||||
public static final int VEGAN_VEGAN = 2;
|
public static final int VEGAN_VEGAN = 2;
|
||||||
public static final int CERT_NONE = 0b0000;
|
private static final int CERT_NONE = 0b0000;
|
||||||
public static final int CERT_BIO = 0b0001;
|
public static final int CERT_BIO = 0b0001;
|
||||||
public static final int CERT_MSC = 0b0010;
|
public static final int CERT_MSC = 0b0010;
|
||||||
|
|
||||||
@@ -42,7 +42,7 @@ public class Meal implements Serializable {
|
|||||||
for (String note : notes) {
|
for (String note : notes) {
|
||||||
switch (note.toLowerCase()) {
|
switch (note.toLowerCase()) {
|
||||||
case "vegetarisch":
|
case "vegetarisch":
|
||||||
vegan = VEGAN_VEGETERIAN;
|
vegan = VEGAN_VEGETARIAN;
|
||||||
break;
|
break;
|
||||||
case "vegan":
|
case "vegan":
|
||||||
vegan = VEGAN_VEGAN;
|
vegan = VEGAN_VEGAN;
|
||||||
|
|||||||
@@ -2,8 +2,6 @@ package de.sebse.fuplanner.services.GoogleAuth;
|
|||||||
|
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.content.IntentSender;
|
import android.content.IntentSender;
|
||||||
import android.support.annotation.Nullable;
|
|
||||||
import android.support.v4.app.FragmentActivity;
|
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
|
||||||
@@ -17,6 +15,9 @@ import com.google.android.gms.common.api.ApiException;
|
|||||||
import com.google.android.gms.common.api.CommonStatusCodes;
|
import com.google.android.gms.common.api.CommonStatusCodes;
|
||||||
import com.google.android.gms.common.api.ResolvableApiException;
|
import com.google.android.gms.common.api.ResolvableApiException;
|
||||||
|
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
import androidx.fragment.app.FragmentActivity;
|
||||||
|
|
||||||
import static android.app.Activity.RESULT_OK;
|
import static android.app.Activity.RESULT_OK;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -1,16 +1,17 @@
|
|||||||
package de.sebse.fuplanner.services.KVV;
|
package de.sebse.fuplanner.services.KVV;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.support.annotation.NonNull;
|
|
||||||
import android.util.Pair;
|
import android.util.Pair;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
import de.sebse.fuplanner.services.KVV.types.LoginToken;
|
import de.sebse.fuplanner.services.KVV.types.LoginToken;
|
||||||
import de.sebse.fuplanner.services.KVV.types.Modules;
|
import de.sebse.fuplanner.services.KVV.types.Modules;
|
||||||
import de.sebse.fuplanner.tools.MainAcitivityListener;
|
import de.sebse.fuplanner.tools.MainActivityListener;
|
||||||
|
import de.sebse.fuplanner.tools.logging.Logger;
|
||||||
import de.sebse.fuplanner.tools.network.NetworkCallback;
|
import de.sebse.fuplanner.tools.network.NetworkCallback;
|
||||||
import de.sebse.fuplanner.tools.network.NetworkErrorCallback;
|
import de.sebse.fuplanner.tools.network.NetworkErrorCallback;
|
||||||
|
|
||||||
@@ -24,14 +25,20 @@ public class KVV {
|
|||||||
private boolean isLoginPending = true;
|
private boolean isLoginPending = true;
|
||||||
private final ArrayList<LastTokenCallback> updatingList;
|
private final ArrayList<LastTokenCallback> updatingList;
|
||||||
private final HashMap<String, Object> addons = new HashMap<>();
|
private final HashMap<String, Object> addons = new HashMap<>();
|
||||||
private final MainAcitivityListener mListener;
|
private final MainActivityListener mListener;
|
||||||
|
private Logger log = new Logger(this);
|
||||||
|
|
||||||
public KVV(Context context) {
|
public KVV(Context context) {
|
||||||
mListener = (MainAcitivityListener) context;
|
mListener = (MainActivityListener) context;
|
||||||
this.context = context;
|
this.context = context;
|
||||||
this.updatingList = new ArrayList<>();
|
this.updatingList = new ArrayList<>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isLoggedIn() {
|
||||||
|
log.d(this.lastToken, new KVVLogin(this.context).easyLogin());
|
||||||
|
return this.lastToken != null;
|
||||||
|
}
|
||||||
|
|
||||||
public LoginToken easyLogin() {
|
public LoginToken easyLogin() {
|
||||||
KVVLogin login = new KVVLogin(this.context);
|
KVVLogin login = new KVVLogin(this.context);
|
||||||
lastToken = login.easyLogin();
|
lastToken = login.easyLogin();
|
||||||
@@ -137,6 +144,18 @@ public class KVV {
|
|||||||
getModulePart(modules -> modules.getResources(module, saveOnCallback(modules, callback, forceRefresh), errorOnCallback(error), forceRefresh));
|
getModulePart(modules -> modules.getResources(module, saveOnCallback(modules, callback, forceRefresh), errorOnCallback(error), forceRefresh));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void getResourceFile(final NetworkCallback<String> callback, final NetworkErrorCallback error, String filename, String url, String moduleName, boolean downloadNew){
|
||||||
|
getModulePart(modules -> modules.getResourceFile(callback, errorOnCallback(error),filename, url, moduleName, downloadNew));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testLogin(BooleanFunction callback) {
|
||||||
|
getLastToken(lastToken -> {
|
||||||
|
TestLogin.testLogin(context, lastToken, success -> callback.apply(true), error -> callback.apply(false));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
private void getModulePart(ModListFunction func) {
|
private void getModulePart(ModListFunction func) {
|
||||||
@@ -158,7 +177,7 @@ public class KVV {
|
|||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
if (forceRefresh)
|
if (forceRefresh)
|
||||||
mListener.refreshFailed(false);
|
mListener.onRefreshCompleted(false);
|
||||||
callback.onResponse(success);
|
callback.onResponse(success);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -166,9 +185,9 @@ public class KVV {
|
|||||||
private NetworkErrorCallback errorOnCallback(NetworkErrorCallback errorCallback){
|
private NetworkErrorCallback errorOnCallback(NetworkErrorCallback errorCallback){
|
||||||
return (error -> {
|
return (error -> {
|
||||||
if (error.getHttpStatus() == 401 || error.getHttpStatus() == 403)
|
if (error.getHttpStatus() == 401 || error.getHttpStatus() == 403)
|
||||||
mListener.loginTokenInvalid(false);
|
mListener.loginTokenInvalid(true);
|
||||||
else
|
else
|
||||||
mListener.refreshFailed(true);
|
mListener.onRefreshCompleted(true);
|
||||||
errorCallback.onError(error);
|
errorCallback.onError(error);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -178,6 +197,11 @@ public class KVV {
|
|||||||
void apply(KVVModuleList mod);
|
void apply(KVVModuleList mod);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@FunctionalInterface
|
||||||
|
public interface BooleanFunction {
|
||||||
|
void apply(boolean isSuccess);
|
||||||
|
}
|
||||||
|
|
||||||
private void getLastToken(LastTokenCallback lastTokenCallback) {
|
private void getLastToken(LastTokenCallback lastTokenCallback) {
|
||||||
if (this.isLoginPending) {
|
if (this.isLoginPending) {
|
||||||
this.updatingList.add(lastTokenCallback);
|
this.updatingList.add(lastTokenCallback);
|
||||||
|
|||||||
@@ -2,9 +2,6 @@ package de.sebse.fuplanner.services.KVV;
|
|||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
|
||||||
import org.json.JSONException;
|
|
||||||
import org.json.JSONObject;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.regex.Matcher;
|
import java.util.regex.Matcher;
|
||||||
@@ -16,17 +13,17 @@ import de.sebse.fuplanner.tools.network.NetworkCallback;
|
|||||||
import de.sebse.fuplanner.tools.network.NetworkError;
|
import de.sebse.fuplanner.tools.network.NetworkError;
|
||||||
import de.sebse.fuplanner.tools.network.NetworkErrorCallback;
|
import de.sebse.fuplanner.tools.network.NetworkErrorCallback;
|
||||||
|
|
||||||
|
import static de.sebse.fuplanner.services.KVV.TestLogin.testLogin;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Created by sebastian on 24.10.17.
|
* Created by sebastian on 24.10.17.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
class KVVLogin extends HTTPService {
|
class KVVLogin extends HTTPService {
|
||||||
private final Context mContext;
|
|
||||||
private LoginToken loginToken;
|
private LoginToken loginToken;
|
||||||
|
|
||||||
KVVLogin(Context context) {
|
KVVLogin(Context context) {
|
||||||
super(context, false);
|
super(context);
|
||||||
this.mContext = context;
|
|
||||||
try {
|
try {
|
||||||
this.loginToken = LoginToken.load(context);
|
this.loginToken = LoginToken.load(context);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
@@ -36,14 +33,14 @@ class KVVLogin extends HTTPService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public LoginToken easyLogin() {
|
LoginToken easyLogin() {
|
||||||
return this.loginToken;
|
return this.loginToken;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void login(String username, String password, NetworkCallback<LoginToken> callback, NetworkErrorCallback errorCallback) {
|
void login(String username, String password, NetworkCallback<LoginToken> callback, NetworkErrorCallback errorCallback) {
|
||||||
if (this.loginToken != null) {
|
if (this.loginToken != null) {
|
||||||
if (this.loginToken.getUsername().equals(username)) {
|
if (this.loginToken.getUsername().equals(username)) {
|
||||||
testLogin(this.loginToken, success -> callback.onResponse(this.loginToken), error -> {
|
testLogin(getContext(), this.loginToken, success -> callback.onResponse(this.loginToken), error -> {
|
||||||
this.loginToken = null;
|
this.loginToken = null;
|
||||||
login(username, password, callback, errorCallback);
|
login(username, password, callback, errorCallback);
|
||||||
});
|
});
|
||||||
@@ -54,19 +51,19 @@ class KVVLogin extends HTTPService {
|
|||||||
} else {
|
} else {
|
||||||
doLogin(username, password, token -> {
|
doLogin(username, password, token -> {
|
||||||
this.loginToken = token;
|
this.loginToken = token;
|
||||||
testLogin(this.loginToken, success -> callback.onResponse(this.loginToken), errorCallback);
|
testLogin(getContext(), this.loginToken, success -> callback.onResponse(this.loginToken), errorCallback);
|
||||||
}, errorCallback);
|
}, errorCallback);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void deleteOffline() {
|
void deleteOffline() {
|
||||||
if (this.loginToken != null)
|
if (this.loginToken != null)
|
||||||
this.loginToken.delete(mContext);
|
this.loginToken.delete(getContext());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void saveOffline() throws IOException {
|
void saveOffline() throws IOException {
|
||||||
if (this.loginToken != null)
|
if (this.loginToken != null)
|
||||||
this.loginToken.save(mContext);
|
this.loginToken.save(getContext());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -92,21 +89,6 @@ class KVVLogin extends HTTPService {
|
|||||||
}, error);
|
}, error);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void testLogin(LoginToken loginToken, NetworkCallback<LoginToken> callback, NetworkErrorCallback errorCallback) {
|
|
||||||
get(String.format("https://kvv.imp.fu-berlin.de/direct/profile/%s.json", loginToken.getUsername()), loginToken.getCookies(), response -> {
|
|
||||||
String body = response.getParsed();
|
|
||||||
try {
|
|
||||||
JSONObject json = new JSONObject(body);
|
|
||||||
String displayName = json.getString("displayName");
|
|
||||||
String email = json.getString("email");
|
|
||||||
loginToken.setAdditionals(displayName, email);
|
|
||||||
callback.onResponse(loginToken);
|
|
||||||
} catch (JSONException e) {
|
|
||||||
errorCallback.onError(new NetworkError(100201, 403, "Cannot parse profile!"));
|
|
||||||
}
|
|
||||||
}, error -> errorCallback.onError(new NetworkError(100200, error.networkResponse.statusCode, "Testing login failed!")));
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
GET https://kvv.imp.fu-berlin.de/portal/login
|
GET https://kvv.imp.fu-berlin.de/portal/login
|
||||||
-> JSESSIONID 5c10406f-588c-4c16-96e9-c80d115417de.tomcat1
|
-> JSESSIONID 5c10406f-588c-4c16-96e9-c80d115417de.tomcat1
|
||||||
@@ -233,10 +215,16 @@ class KVVLogin extends HTTPService {
|
|||||||
cookies.put("ROUTEID", ROUTEID);
|
cookies.put("ROUTEID", ROUTEID);
|
||||||
cookies.put("_idp_session", _idp_session);
|
cookies.put("_idp_session", _idp_session);
|
||||||
get("https://identity.fu-berlin.de/idp-fub/profile/SAML2/Redirect/SSO", cookies, response -> {
|
get("https://identity.fu-berlin.de/idp-fub/profile/SAML2/Redirect/SSO", cookies, response -> {
|
||||||
|
String body = response.getParsed();
|
||||||
|
if (body == null) {
|
||||||
|
errorCallback.onError(new NetworkError(100143, -1, "Error on getting SAML response!"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
HashMap<String, String> object = new HashMap<>();
|
HashMap<String, String> object = new HashMap<>();
|
||||||
|
|
||||||
Pattern pattern = Pattern.compile("ss:mem:([0-9a-f]+)");
|
Pattern pattern = Pattern.compile("ss:mem:([0-9a-f]+)");
|
||||||
Matcher matcher = pattern.matcher(response.getParsed());
|
Matcher matcher = pattern.matcher(body);
|
||||||
if (!matcher.find()) {
|
if (!matcher.find()) {
|
||||||
errorCallback.onError(new NetworkError(100142, -1, "Error on getting SAML response!"));
|
errorCallback.onError(new NetworkError(100142, -1, "Error on getting SAML response!"));
|
||||||
return;
|
return;
|
||||||
@@ -244,7 +232,7 @@ class KVVLogin extends HTTPService {
|
|||||||
object.put("RelayState", "ss:mem:"+matcher.group(1));
|
object.put("RelayState", "ss:mem:"+matcher.group(1));
|
||||||
|
|
||||||
pattern = Pattern.compile("name=\"SAMLResponse\" value=\"([0-9a-zA-Z+]+=*)");
|
pattern = Pattern.compile("name=\"SAMLResponse\" value=\"([0-9a-zA-Z+]+=*)");
|
||||||
matcher = pattern.matcher(response.getParsed());
|
matcher = pattern.matcher(body);
|
||||||
if (!matcher.find()) {
|
if (!matcher.find()) {
|
||||||
errorCallback.onError(new NetworkError(100141, -1, "Error on getting SAML response!"));
|
errorCallback.onError(new NetworkError(100141, -1, "Error on getting SAML response!"));
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -1,14 +1,16 @@
|
|||||||
package de.sebse.fuplanner.services.KVV;
|
package de.sebse.fuplanner.services.KVV;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
import android.os.Environment;
|
||||||
|
import android.text.Html;
|
||||||
import android.util.Pair;
|
import android.util.Pair;
|
||||||
|
|
||||||
import net.htmlparser.jericho.Source;
|
|
||||||
|
|
||||||
import org.json.JSONArray;
|
import org.json.JSONArray;
|
||||||
import org.json.JSONException;
|
import org.json.JSONException;
|
||||||
import org.json.JSONObject;
|
import org.json.JSONObject;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
@@ -18,20 +20,23 @@ import java.util.regex.MatchResult;
|
|||||||
|
|
||||||
import de.sebse.fuplanner.services.KVV.types.Announcement;
|
import de.sebse.fuplanner.services.KVV.types.Announcement;
|
||||||
import de.sebse.fuplanner.services.KVV.types.Assignment;
|
import de.sebse.fuplanner.services.KVV.types.Assignment;
|
||||||
|
import de.sebse.fuplanner.services.KVV.types.AssignmentList;
|
||||||
import de.sebse.fuplanner.services.KVV.types.Event;
|
import de.sebse.fuplanner.services.KVV.types.Event;
|
||||||
|
import de.sebse.fuplanner.services.KVV.types.EventList;
|
||||||
import de.sebse.fuplanner.services.KVV.types.Gradebook;
|
import de.sebse.fuplanner.services.KVV.types.Gradebook;
|
||||||
import de.sebse.fuplanner.services.KVV.types.Lecturer;
|
import de.sebse.fuplanner.services.KVV.types.Lecturer;
|
||||||
import de.sebse.fuplanner.services.KVV.types.LoginToken;
|
import de.sebse.fuplanner.services.KVV.types.LoginToken;
|
||||||
import de.sebse.fuplanner.services.KVV.types.Modules;
|
import de.sebse.fuplanner.services.KVV.types.Modules;
|
||||||
import de.sebse.fuplanner.services.KVV.types.AssignmentList;
|
|
||||||
import de.sebse.fuplanner.services.KVV.types.Resource;
|
import de.sebse.fuplanner.services.KVV.types.Resource;
|
||||||
import de.sebse.fuplanner.tools.AsyncQueue;
|
import de.sebse.fuplanner.tools.AsyncQueue;
|
||||||
import de.sebse.fuplanner.services.KVV.types.EventList;
|
|
||||||
import de.sebse.fuplanner.tools.Regex;
|
import de.sebse.fuplanner.tools.Regex;
|
||||||
import de.sebse.fuplanner.tools.network.HTTPService;
|
import de.sebse.fuplanner.tools.network.HTTPService;
|
||||||
import de.sebse.fuplanner.tools.network.NetworkCallback;
|
import de.sebse.fuplanner.tools.network.NetworkCallback;
|
||||||
import de.sebse.fuplanner.tools.network.NetworkError;
|
import de.sebse.fuplanner.tools.network.NetworkError;
|
||||||
import de.sebse.fuplanner.tools.network.NetworkErrorCallback;
|
import de.sebse.fuplanner.tools.network.NetworkErrorCallback;
|
||||||
|
import de.sebse.fuplanner.tools.network.Result;
|
||||||
|
|
||||||
|
import static de.sebse.fuplanner.services.KVV.TestLogin.testLogin;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Created by sebastian on 29.10.17.
|
* Created by sebastian on 29.10.17.
|
||||||
@@ -59,7 +64,7 @@ class KVVModuleList extends HTTPService {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
public void getModuleList(final NetworkCallback<Modules> callback, final NetworkErrorCallback errorCallback, boolean forceRefresh) {
|
void getModuleList(final NetworkCallback<Modules> callback, final NetworkErrorCallback errorCallback, boolean forceRefresh) {
|
||||||
queueModuleDetails.add("list", () -> {
|
queueModuleDetails.add("list", () -> {
|
||||||
if (this.moduleList != null && !forceRefresh) {
|
if (this.moduleList != null && !forceRefresh) {
|
||||||
callback.onResponse(this.moduleList);
|
callback.onResponse(this.moduleList);
|
||||||
@@ -108,7 +113,7 @@ class KVVModuleList extends HTTPService {
|
|||||||
}
|
}
|
||||||
String type = site.getJSONObject("props").getString("kvv_coursetype");
|
String type = site.getJSONObject("props").getString("kvv_coursetype");
|
||||||
String description = site.getString("description");
|
String description = site.getString("description");
|
||||||
description = new Source(description).getRenderer().toString();
|
description = String.valueOf(Html.fromHtml(description));
|
||||||
String id = site.getString("id");
|
String id = site.getString("id");
|
||||||
modules.addModule(semester, lvNumbers, title, lecturers, type, description, id);
|
modules.addModule(semester, lvNumbers, title, lecturers, type, description, id);
|
||||||
}
|
}
|
||||||
@@ -123,24 +128,26 @@ class KVVModuleList extends HTTPService {
|
|||||||
}
|
}
|
||||||
// Empty module *may be* because token is invalid -> check
|
// Empty module *may be* because token is invalid -> check
|
||||||
if (modules.size() == 0)
|
if (modules.size() == 0)
|
||||||
testLogin(token, token -> callback.onResponse(modules), errorCallback);
|
testLogin(getContext(), token, token -> callback.onResponse(modules), errorCallback);
|
||||||
else
|
else
|
||||||
callback.onResponse(modules);
|
callback.onResponse(modules);
|
||||||
}, error -> errorCallback.onError(new NetworkError(101104, error.networkResponse.statusCode, "Cannot get module list!")));
|
}, error -> errorCallback.onError(new NetworkError(101104, error.networkResponse.statusCode, "Cannot get module list!")));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void deleteModulesOffline(Context context) {
|
void deleteModulesOffline(Context context) {
|
||||||
if (this.moduleList != null)
|
if (this.moduleList != null)
|
||||||
this.moduleList.delete(context);
|
this.moduleList.delete(context);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void saveModulesOffline(Context context) throws IOException {
|
void saveModulesOffline(Context context) throws IOException {
|
||||||
if (this.moduleList != null)
|
if (this.moduleList != null)
|
||||||
this.moduleList.save(context);
|
this.moduleList.save(context);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void getModule(String id, final NetworkCallback<Modules.Module> callback, final NetworkErrorCallback errorCallback, boolean forceRefresh) {
|
void getModule(String id, final NetworkCallback<Modules.Module> callback, final NetworkErrorCallback errorCallback, boolean forceRefresh) {
|
||||||
this.getModuleList(success -> callback.onResponse(success.get(id)), errorCallback, forceRefresh);
|
this.getModuleList(success -> {
|
||||||
|
callback.onResponse(success.get(id));
|
||||||
|
}, errorCallback, forceRefresh);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -148,7 +155,7 @@ class KVVModuleList extends HTTPService {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
public void getModuleDetails(Modules.Module module, final NetworkCallback<Pair<Modules.Module, Boolean>> callback, final NetworkErrorCallback errorCallback, boolean forceRefresh) {
|
void getModuleDetails(Modules.Module module, final NetworkCallback<Pair<Modules.Module, Boolean>> callback, final NetworkErrorCallback errorCallback, boolean forceRefresh) {
|
||||||
AtomicInteger returns = new AtomicInteger(0);
|
AtomicInteger returns = new AtomicInteger(0);
|
||||||
AtomicReference<NetworkError> lastError = new AtomicReference<>(null);
|
AtomicReference<NetworkError> lastError = new AtomicReference<>(null);
|
||||||
final AtomicInteger items = new AtomicInteger(0);
|
final AtomicInteger items = new AtomicInteger(0);
|
||||||
@@ -188,7 +195,7 @@ class KVVModuleList extends HTTPService {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
public void getAnnouncements(Modules.Module module, final NetworkCallback<Modules.Module> callback, final NetworkErrorCallback errorCallback, boolean forceRefresh) {
|
void getAnnouncements(Modules.Module module, final NetworkCallback<Modules.Module> callback, final NetworkErrorCallback errorCallback, boolean forceRefresh) {
|
||||||
queueModuleDetails.add(module.getID(), () -> {
|
queueModuleDetails.add(module.getID(), () -> {
|
||||||
if (module.announcements != null && !forceRefresh) {
|
if (module.announcements != null && !forceRefresh) {
|
||||||
callback.onResponse(module);
|
callback.onResponse(module);
|
||||||
@@ -224,13 +231,13 @@ class KVVModuleList extends HTTPService {
|
|||||||
String id = site.getString("announcementId");
|
String id = site.getString("announcementId");
|
||||||
String title = site.getString("title");
|
String title = site.getString("title");
|
||||||
String text = site.getString("body");
|
String text = site.getString("body");
|
||||||
text = new Source(text).getRenderer().toString();
|
text = String.valueOf(Html.fromHtml(text));
|
||||||
String createdBy = site.getString("createdByDisplayName");
|
String createdBy = site.getString("createdByDisplayName");
|
||||||
long createdOn = site.getLong("createdOn");
|
long createdOn = site.getLong("createdOn");
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//PDFs links rausziehen
|
// Extract attachment links
|
||||||
JSONArray attachments = site.getJSONArray("attachments");
|
JSONArray attachments = site.getJSONArray("attachments");
|
||||||
ArrayList<String> urls = new ArrayList<>();
|
ArrayList<String> urls = new ArrayList<>();
|
||||||
for (int j =0; j<attachments.length(); j++){
|
for (int j =0; j<attachments.length(); j++){
|
||||||
@@ -247,7 +254,7 @@ class KVVModuleList extends HTTPService {
|
|||||||
}
|
}
|
||||||
// Empty announcements *may be* because token is invalid -> check
|
// Empty announcements *may be* because token is invalid -> check
|
||||||
if (announcements.size() == 0)
|
if (announcements.size() == 0)
|
||||||
testLogin(token, token -> callback.onResponse(announcements), errorCallback);
|
testLogin(getContext(), token, token -> callback.onResponse(announcements), errorCallback);
|
||||||
else
|
else
|
||||||
callback.onResponse(announcements);
|
callback.onResponse(announcements);
|
||||||
}, error -> errorCallback.onError(new NetworkError(101203, error.networkResponse.statusCode, "Cannot get announcements!")));
|
}, error -> errorCallback.onError(new NetworkError(101203, error.networkResponse.statusCode, "Cannot get announcements!")));
|
||||||
@@ -257,7 +264,7 @@ class KVVModuleList extends HTTPService {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
public void getAssignments(Modules.Module module, final NetworkCallback<Modules.Module> callback, final NetworkErrorCallback errorCallback, boolean forceRefresh) {
|
void getAssignments(Modules.Module module, final NetworkCallback<Modules.Module> callback, final NetworkErrorCallback errorCallback, boolean forceRefresh) {
|
||||||
queueModuleDetails.add(module.getID(), () -> {
|
queueModuleDetails.add(module.getID(), () -> {
|
||||||
if (module.assignments != null && !forceRefresh) {
|
if (module.assignments != null && !forceRefresh) {
|
||||||
callback.onResponse(module);
|
callback.onResponse(module);
|
||||||
@@ -293,7 +300,7 @@ class KVVModuleList extends HTTPService {
|
|||||||
String id = site.getString("id");
|
String id = site.getString("id");
|
||||||
String title = site.getString("title");
|
String title = site.getString("title");
|
||||||
String instructions = site.getString("instructions");
|
String instructions = site.getString("instructions");
|
||||||
instructions = new Source(instructions).getRenderer().toString();
|
instructions = String.valueOf(Html.fromHtml(instructions));
|
||||||
long dueTime = site.getJSONObject("dueTime").getLong("time");
|
long dueTime = site.getJSONObject("dueTime").getLong("time");
|
||||||
String gradebookItemName = site.optString("gradebookItemName", null);
|
String gradebookItemName = site.optString("gradebookItemName", null);
|
||||||
String gradeScale = site.getString("gradeScale");
|
String gradeScale = site.getString("gradeScale");
|
||||||
@@ -311,7 +318,7 @@ class KVVModuleList extends HTTPService {
|
|||||||
}
|
}
|
||||||
// Empty assignments *may be* because token is invalid -> check
|
// Empty assignments *may be* because token is invalid -> check
|
||||||
if (assignments.size() == 0)
|
if (assignments.size() == 0)
|
||||||
testLogin(token, token -> callback.onResponse(assignments), errorCallback);
|
testLogin(getContext(), token, token -> callback.onResponse(assignments), errorCallback);
|
||||||
else
|
else
|
||||||
callback.onResponse(assignments);
|
callback.onResponse(assignments);
|
||||||
}, error -> errorCallback.onError(new NetworkError(101303, error.networkResponse.statusCode, "Cannot get assignments!")));
|
}, error -> errorCallback.onError(new NetworkError(101303, error.networkResponse.statusCode, "Cannot get assignments!")));
|
||||||
@@ -324,7 +331,7 @@ class KVVModuleList extends HTTPService {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
public void getEvents(Modules.Module module, final NetworkCallback<Modules.Module> callback, final NetworkErrorCallback errorCallback, boolean forceRefresh) {
|
void getEvents(Modules.Module module, final NetworkCallback<Modules.Module> callback, final NetworkErrorCallback errorCallback, boolean forceRefresh) {
|
||||||
queueModuleDetails.add(module.getID(), () -> {
|
queueModuleDetails.add(module.getID(), () -> {
|
||||||
if (module.events != null && !forceRefresh) {
|
if (module.events != null && !forceRefresh) {
|
||||||
callback.onResponse(module);
|
callback.onResponse(module);
|
||||||
@@ -374,7 +381,7 @@ class KVVModuleList extends HTTPService {
|
|||||||
events.sort();
|
events.sort();
|
||||||
// Empty events *may be* because token is invalid -> check
|
// Empty events *may be* because token is invalid -> check
|
||||||
if (events.size() == 0)
|
if (events.size() == 0)
|
||||||
testLogin(token, token -> callback.onResponse(events), errorCallback);
|
testLogin(getContext(), token, token -> callback.onResponse(events), errorCallback);
|
||||||
else
|
else
|
||||||
callback.onResponse(events);
|
callback.onResponse(events);
|
||||||
}, error -> errorCallback.onError(new NetworkError(101403, error.networkResponse.statusCode, "Cannot get calendar entries!")));
|
}, error -> errorCallback.onError(new NetworkError(101403, error.networkResponse.statusCode, "Cannot get calendar entries!")));
|
||||||
@@ -384,7 +391,7 @@ class KVVModuleList extends HTTPService {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
public void getGradebook(Modules.Module module, final NetworkCallback<Modules.Module> callback, final NetworkErrorCallback errorCallback, boolean forceRefresh) {
|
void getGradebook(Modules.Module module, final NetworkCallback<Modules.Module> callback, final NetworkErrorCallback errorCallback, boolean forceRefresh) {
|
||||||
queueModuleDetails.add(module.getID(), () -> {
|
queueModuleDetails.add(module.getID(), () -> {
|
||||||
if (module.gradebook != null && !forceRefresh) {
|
if (module.gradebook != null && !forceRefresh) {
|
||||||
callback.onResponse(module);
|
callback.onResponse(module);
|
||||||
@@ -471,7 +478,16 @@ class KVVModuleList extends HTTPService {
|
|||||||
String title = site.getString("title");
|
String title = site.getString("title");
|
||||||
long modifiedDate = site.getLong("modifiedDate");
|
long modifiedDate = site.getLong("modifiedDate");
|
||||||
String url = site.getString("url");
|
String url = site.getString("url");
|
||||||
resources.add(new Resource(author, title, modifiedDate, url));
|
boolean visible = site.getBoolean("visible");
|
||||||
|
String type = site.getString("type");
|
||||||
|
String container = site.getString("container");
|
||||||
|
if (type.equals("collection")){
|
||||||
|
resources.add(new Resource.Folder(author, title, modifiedDate, url, visible, container));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
resources.add(new Resource.File(author, title, modifiedDate, url, visible, container, type));
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} catch (JSONException e) {
|
} catch (JSONException e) {
|
||||||
@@ -479,38 +495,133 @@ class KVVModuleList extends HTTPService {
|
|||||||
errorCallback.onError(new NetworkError(101602, 403, "Cannot parse resources!"));
|
errorCallback.onError(new NetworkError(101602, 403, "Cannot parse resources!"));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ArrayList<Resource> root = new ArrayList<>();
|
||||||
|
// Generate folder structure
|
||||||
|
for (Resource res: resources) {
|
||||||
|
if (!res.getContainer().equals("/content/group/")) {
|
||||||
|
if (res.getContainer().equals("/content/group/"+ID+"/")){
|
||||||
|
// if file in root folder
|
||||||
|
root.add(res);
|
||||||
|
} else {
|
||||||
|
// in sub folder
|
||||||
|
for (Resource res2: resources) {
|
||||||
|
if (res2.getUrl().endsWith(res.getContainer()) && res2 instanceof Resource.Folder) {
|
||||||
|
// Append File/Folder to list
|
||||||
|
((Resource.Folder) res2).add(res);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Empty resources *may be* because token is invalid -> check
|
// Empty resources *may be* because token is invalid -> check
|
||||||
if (resources.size() == 0)
|
if (root.size() == 0)
|
||||||
testLogin(token, token -> callback.onResponse(resources), errorCallback);
|
testLogin(getContext(), token, token -> callback.onResponse(root), errorCallback);
|
||||||
else
|
else
|
||||||
callback.onResponse(resources);
|
callback.onResponse(root);
|
||||||
|
|
||||||
|
|
||||||
callback.onResponse(resources);
|
|
||||||
}, error -> errorCallback.onError(new NetworkError(101603, error.networkResponse.statusCode, "Cannot get resources!")));
|
}, error -> errorCallback.onError(new NetworkError(101603, error.networkResponse.statusCode, "Cannot get resources!")));
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void getResourceFile(final NetworkCallback<String> callback, final NetworkErrorCallback errorCallback, String Filename, String url, String moduleName, boolean downloadNew) {
|
||||||
|
if (isExternalStorageReadable()){
|
||||||
|
File f = new File(Environment.getExternalStoragePublicDirectory(
|
||||||
|
Environment.DIRECTORY_DOWNLOADS)+"/"+moduleName+"/"+Filename);
|
||||||
|
// check if file already downloaded -> do not download again
|
||||||
|
if (f.exists() && !downloadNew) {
|
||||||
// TODO Better, more elegant solution than duplicate code KVVLogin
|
callback.onResponse(f.getPath());
|
||||||
private void testLogin(LoginToken loginToken, NetworkCallback<LoginToken> callback, NetworkErrorCallback errorCallback) {
|
return;
|
||||||
get(String.format("https://kvv.imp.fu-berlin.de/direct/profile/%s.json", loginToken.getUsername()), loginToken.getCookies(), response -> {
|
|
||||||
String body = response.getParsed();
|
|
||||||
try {
|
|
||||||
JSONObject json = new JSONObject(body);
|
|
||||||
String displayName = json.getString("displayName");
|
|
||||||
String email = json.getString("email");
|
|
||||||
loginToken.setAdditionals(displayName, email);
|
|
||||||
callback.onResponse(loginToken);
|
|
||||||
} catch (JSONException e) {
|
|
||||||
errorCallback.onError(new NetworkError(100201, 403, "Cannot parse profile!"));
|
|
||||||
}
|
}
|
||||||
}, error -> errorCallback.onError(new NetworkError(100200, error.networkResponse.statusCode, "Testing login failed!")));
|
}
|
||||||
|
getResourceFileUpgrade(Filename, url , moduleName, callback, errorCallback);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void getResourceFileUpgrade(String filename, String url , String moduleName, final NetworkCallback<String> callback, final NetworkErrorCallback errorCallback) {
|
||||||
|
if (token == null) {
|
||||||
|
errorCallback.onError(new NetworkError(101701, 500, "Currently running in offline mode!"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
get(url, token.getCookies(), response ->{
|
||||||
|
|
||||||
|
|
||||||
|
if (Regex.has("\\.[Uu][Rr][Ll]$", url)){
|
||||||
|
// Return redirected URL
|
||||||
|
String path = response.getHeaders().get("Location");
|
||||||
|
if (path == null){
|
||||||
|
path = "";
|
||||||
|
}
|
||||||
|
callback.onResponse(path);
|
||||||
|
} else if (response.getBytes()==null){
|
||||||
|
testLogin(getContext(), token, token -> {
|
||||||
|
if (isExternalStorageWritable()) {
|
||||||
|
// try to download file again
|
||||||
|
get(url, token.getCookies(), response2 -> {
|
||||||
|
String path = saveFileInDownloads(filename, response2, moduleName);
|
||||||
|
callback.onResponse(path);
|
||||||
|
}, error -> errorCallback.onError(new NetworkError(101705, error.networkResponse.statusCode, "Cannot get file!")));
|
||||||
|
} else {
|
||||||
|
errorCallback.onError(new NetworkError(101703, 403, "External storage not writable!"));
|
||||||
|
}
|
||||||
|
}, errorCallback);
|
||||||
|
} else if (isExternalStorageWritable()) {
|
||||||
|
String path = saveFileInDownloads(filename, response, moduleName);
|
||||||
|
callback.onResponse(path);
|
||||||
|
} else {
|
||||||
|
errorCallback.onError(new NetworkError(101704, 403, "External storage not writable!"));
|
||||||
|
}
|
||||||
|
}, error -> errorCallback.onError(new NetworkError(101702, error.networkResponse.statusCode, "Cannot get file!")));
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* Checks if external storage is available for read and write */
|
||||||
|
private boolean isExternalStorageWritable() {
|
||||||
|
String state = Environment.getExternalStorageState();
|
||||||
|
if (Environment.MEDIA_MOUNTED.equals(state)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
log.w("File system: Writing not possible!");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Checks if external storage is available to at least read */
|
||||||
|
private boolean isExternalStorageReadable() {
|
||||||
|
String state = Environment.getExternalStorageState();
|
||||||
|
if (Environment.MEDIA_MOUNTED.equals(state) ||
|
||||||
|
Environment.MEDIA_MOUNTED_READ_ONLY.equals(state)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
log.w("File system: Reading not possible!");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
private String saveFileInDownloads(String filename, Result fileResult, String moduleName) {
|
||||||
|
// Saves file in folder: DOWNLOADS/moduleName
|
||||||
|
File folder = new File(Environment.getExternalStoragePublicDirectory(
|
||||||
|
Environment.DIRECTORY_DOWNLOADS), moduleName);
|
||||||
|
if (!folder.mkdir()) {
|
||||||
|
log.w( "Directory not created");
|
||||||
|
}
|
||||||
|
String path = "";
|
||||||
|
try {
|
||||||
|
// TODO check if enough storage space is available
|
||||||
|
FileOutputStream out = new FileOutputStream(folder.getPath()+"/"+filename);
|
||||||
|
out.write(fileResult.getBytes());
|
||||||
|
out.close();
|
||||||
|
path = folder.getPath()+"/"+filename;
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.w("File not saved!");
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
return path;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,38 @@
|
|||||||
|
package de.sebse.fuplanner.services.KVV;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
|
||||||
|
import org.json.JSONException;
|
||||||
|
import org.json.JSONObject;
|
||||||
|
|
||||||
|
import de.sebse.fuplanner.services.KVV.types.LoginToken;
|
||||||
|
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;
|
||||||
|
|
||||||
|
final class TestLogin extends HTTPService {
|
||||||
|
|
||||||
|
private TestLogin(Context context) {
|
||||||
|
super(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void testLogin(Context context, LoginToken loginToken, NetworkCallback<LoginToken> callback, NetworkErrorCallback errorCallback) {
|
||||||
|
new TestLogin(context).get(String.format("https://kvv.imp.fu-berlin.de/direct/profile/%s.json", loginToken.getUsername()), loginToken.getCookies(), response -> {
|
||||||
|
String body = response.getParsed();
|
||||||
|
if (body == null) {
|
||||||
|
errorCallback.onError(new NetworkError(100202, 403, "Testing login failed!"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
JSONObject json = new JSONObject(body);
|
||||||
|
String displayName = json.getString("displayName");
|
||||||
|
String email = json.getString("email");
|
||||||
|
loginToken.setAdditionals(displayName, email);
|
||||||
|
callback.onResponse(loginToken);
|
||||||
|
} catch (JSONException e) {
|
||||||
|
errorCallback.onError(new NetworkError(100201, 403, "Cannot parse profile!"));
|
||||||
|
}
|
||||||
|
}, error -> errorCallback.onError(new NetworkError(100200, error.networkResponse.statusCode, "Testing login failed!")));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -25,7 +25,7 @@ public class Announcement implements Serializable {
|
|||||||
return urls;
|
return urls;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getId() {
|
private String getId() {
|
||||||
return id;
|
return id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -7,8 +7,6 @@ public class Assignment implements Serializable {
|
|||||||
private final String id;
|
private final String id;
|
||||||
private final String title;
|
private final String title;
|
||||||
private final long dueTime;
|
private final long dueTime;
|
||||||
private final String gradebookItemName;
|
|
||||||
private final String gradeScale;
|
|
||||||
private final ArrayList<String> urls;
|
private final ArrayList<String> urls;
|
||||||
private final String instructions;
|
private final String instructions;
|
||||||
|
|
||||||
@@ -16,14 +14,12 @@ public class Assignment implements Serializable {
|
|||||||
this.id = id;
|
this.id = id;
|
||||||
this.title = title;
|
this.title = title;
|
||||||
this.dueTime = dueTime;
|
this.dueTime = dueTime;
|
||||||
this.gradebookItemName = gradebookItemName;
|
|
||||||
this.gradeScale = gradeScale;
|
|
||||||
this.urls = urls;
|
this.urls = urls;
|
||||||
//this.grade = grade;
|
//this.grade = grade;
|
||||||
this.instructions = instructions;
|
this.instructions = instructions;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getId() {
|
private String getId() {
|
||||||
return id;
|
return id;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -52,6 +48,6 @@ public class Assignment implements Serializable {
|
|||||||
return "ID: "+getId()+
|
return "ID: "+getId()+
|
||||||
"\nTitle: "+getTitle()+
|
"\nTitle: "+getTitle()+
|
||||||
"\nDue date: "+getDueDate()+
|
"\nDue date: "+getDueDate()+
|
||||||
"\nInstuctions: "+getInstructions().substring(0, Math.min(getInstructions().length(), 100));
|
"\nInstructions: "+getInstructions().substring(0, Math.min(getInstructions().length(), 100));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import java.util.regex.Matcher;
|
|||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
public class Lecturer implements Serializable {
|
public class Lecturer implements Serializable {
|
||||||
private final String firstname;
|
private final String firstName;
|
||||||
private final String surname;
|
private final String surname;
|
||||||
private final String mail;
|
private final String mail;
|
||||||
|
|
||||||
@@ -15,26 +15,26 @@ public class Lecturer implements Serializable {
|
|||||||
if (!matcher.find()) {
|
if (!matcher.find()) {
|
||||||
throw new NoSuchFieldException();
|
throw new NoSuchFieldException();
|
||||||
}
|
}
|
||||||
this.firstname = matcher.group(1);
|
this.firstName = matcher.group(1);
|
||||||
this.surname = matcher.group(2);
|
this.surname = matcher.group(2);
|
||||||
this.mail = matcher.group(3);
|
this.mail = matcher.group(3);
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getFirstname() {
|
private String getFirstName() {
|
||||||
return firstname;
|
return firstName;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getSurname() {
|
private String getSurname() {
|
||||||
return surname;
|
return surname;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getMail() {
|
private String getMail() {
|
||||||
return mail;
|
return mail;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return "First name: "+getFirstname()+
|
return "First name: "+ getFirstName()+
|
||||||
"\nSurname: "+getSurname()+
|
"\nSurname: "+getSurname()+
|
||||||
"\nMail: "+getMail();
|
"\nMail: "+getMail();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
package de.sebse.fuplanner.services.KVV.types;
|
package de.sebse.fuplanner.services.KVV.types;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.support.annotation.Nullable;
|
|
||||||
|
|
||||||
import java.io.FileInputStream;
|
import java.io.FileInputStream;
|
||||||
import java.io.FileOutputStream;
|
import java.io.FileOutputStream;
|
||||||
@@ -11,6 +10,8 @@ import java.io.ObjectOutputStream;
|
|||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Created by sebastian on 29.10.17.
|
* Created by sebastian on 29.10.17.
|
||||||
*/
|
*/
|
||||||
@@ -23,7 +24,7 @@ public class LoginToken implements Serializable {
|
|||||||
private final String shibsessionKey;
|
private final String shibsessionKey;
|
||||||
private final String shibsessionName;
|
private final String shibsessionName;
|
||||||
private final String JSESSIONID;
|
private final String JSESSIONID;
|
||||||
private String fullname;
|
private String fullName;
|
||||||
private String email;
|
private String email;
|
||||||
private long saveDate = 0;
|
private long saveDate = 0;
|
||||||
|
|
||||||
@@ -58,8 +59,8 @@ public class LoginToken implements Serializable {
|
|||||||
context.deleteFile(FILE_NAME);
|
context.deleteFile(FILE_NAME);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setAdditionals(String fullname, String email) {
|
public void setAdditionals(String fullName, String email) {
|
||||||
this.fullname = fullname;
|
this.fullName = fullName;
|
||||||
this.email = email;
|
this.email = email;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -79,8 +80,8 @@ public class LoginToken implements Serializable {
|
|||||||
return JSESSIONID;
|
return JSESSIONID;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getFullname() {
|
public String getFullName() {
|
||||||
return fullname;
|
return fullName;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getEmail() {
|
public String getEmail() {
|
||||||
|
|||||||
@@ -1,8 +1,6 @@
|
|||||||
package de.sebse.fuplanner.services.KVV.types;
|
package de.sebse.fuplanner.services.KVV.types;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.support.annotation.NonNull;
|
|
||||||
import android.support.annotation.Nullable;
|
|
||||||
|
|
||||||
import java.io.FileInputStream;
|
import java.io.FileInputStream;
|
||||||
import java.io.FileOutputStream;
|
import java.io.FileOutputStream;
|
||||||
@@ -14,6 +12,9 @@ import java.util.ArrayList;
|
|||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Created by sebastian on 29.10.17.
|
* Created by sebastian on 29.10.17.
|
||||||
*/
|
*/
|
||||||
@@ -103,9 +104,9 @@ public class Modules implements Iterable<Modules.Module>, Serializable {
|
|||||||
|
|
||||||
public class Module implements Serializable {
|
public class Module implements Serializable {
|
||||||
public final String semester;
|
public final String semester;
|
||||||
public final HashSet<String> lvNumber;
|
final HashSet<String> lvNumber;
|
||||||
public final String title;
|
public final String title;
|
||||||
public final HashSet<Lecturer> lecturer;
|
final HashSet<Lecturer> lecturer;
|
||||||
public final String type;
|
public final String type;
|
||||||
public final String description;
|
public final String description;
|
||||||
private final String ID;
|
private final String ID;
|
||||||
|
|||||||
@@ -1,22 +1,31 @@
|
|||||||
package de.sebse.fuplanner.services.KVV.types;
|
package de.sebse.fuplanner.services.KVV.types;
|
||||||
|
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
|
||||||
|
import androidx.annotation.LayoutRes;
|
||||||
|
import de.sebse.fuplanner.R;
|
||||||
|
import de.sebse.fuplanner.tools.ui.treeview.LayoutItemType;
|
||||||
|
import de.sebse.fuplanner.tools.ui.treeview.TreeNode;
|
||||||
|
|
||||||
|
|
||||||
|
public abstract class Resource implements Serializable {
|
||||||
|
|
||||||
public class Resource implements Serializable {
|
final String author;
|
||||||
|
final long modifiedDate;
|
||||||
private final String author;
|
final String title;
|
||||||
private final long modifiedDate;
|
final String url;
|
||||||
private final String title;
|
private final boolean visible;
|
||||||
private final String url;
|
private final String container;
|
||||||
|
|
||||||
|
|
||||||
public Resource(String author, String title, long modifiedDate, String url) {
|
Resource(String author, String title, long modifiedDate, String url, boolean visible, String container) {
|
||||||
this.author = author;
|
this.author = author;
|
||||||
this.title = title;
|
this.title = title;
|
||||||
this.modifiedDate = modifiedDate;
|
this.modifiedDate = modifiedDate;
|
||||||
this.url = url;
|
this.url = url;
|
||||||
|
this.visible = visible;
|
||||||
|
this.container = container;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getAuthor() {
|
public String getAuthor() {
|
||||||
@@ -35,6 +44,24 @@ public class Resource implements Serializable {
|
|||||||
return url;
|
return url;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getContainer() {
|
||||||
|
return container;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isVisible() {
|
||||||
|
return visible;
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract TreeNode getTreeNode();
|
||||||
|
|
||||||
|
public static class File extends Resource implements LayoutItemType {
|
||||||
|
private final String type;
|
||||||
|
|
||||||
|
public File(String author, String title, long modifiedDate, String url, boolean visible, String container, String type) {
|
||||||
|
super(author, title, modifiedDate, url, visible, container);
|
||||||
|
this.type = type;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return "Resource{" +
|
return "Resource{" +
|
||||||
@@ -42,9 +69,66 @@ public class Resource implements Serializable {
|
|||||||
", modifiedDate=" + modifiedDate +
|
", modifiedDate=" + modifiedDate +
|
||||||
", title='" + title + '\'' +
|
", title='" + title + '\'' +
|
||||||
", url='" + url + '\'' +
|
", url='" + url + '\'' +
|
||||||
|
", type='" + type + '\'' +
|
||||||
'}';
|
'}';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public TreeNode getTreeNode() {
|
||||||
|
return new TreeNode<>(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @LayoutRes int getLayoutId() {
|
||||||
|
return R.layout.item_file;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Folder extends Resource implements LayoutItemType {
|
||||||
|
|
||||||
|
private final ArrayList<Resource> children;
|
||||||
|
public Folder(String author, String title, long modifiedDate, String url, boolean visible, String container) {
|
||||||
|
super(author, title, modifiedDate, url, visible, container);
|
||||||
|
children = new ArrayList<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void add(Resource res){
|
||||||
|
children.add(res);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Resource get(int id){
|
||||||
|
return children.get(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int size(){
|
||||||
|
return children.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "Resource{" +
|
||||||
|
"author='" + author + '\'' +
|
||||||
|
", modifiedDate=" + modifiedDate +
|
||||||
|
", title='" + title + '\'' +
|
||||||
|
", url='" + url + '\'' +
|
||||||
|
", children='" + children + '\'' +
|
||||||
|
'}';
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public TreeNode getTreeNode() {
|
||||||
|
TreeNode dir = new TreeNode<>(this);
|
||||||
|
for (Resource res: children) {
|
||||||
|
dir.addChild(res.getTreeNode());
|
||||||
|
}
|
||||||
|
return dir;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @LayoutRes int getLayoutId() {
|
||||||
|
return R.layout.item_dir;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -43,15 +43,15 @@ public class SortedListModule extends SortedList<Modules.Module, String, String>
|
|||||||
if (b == null)
|
if (b == null)
|
||||||
return LARGER;
|
return LARGER;
|
||||||
|
|
||||||
String s1type = Regex.regex("(S|WS)", a);
|
String s1type = Regex.regex("^(S|WS) ", a);
|
||||||
int s1year = Integer.parseInt(Regex.regex("(S|WS) ([0-9]{2})", a, 2));
|
int s1year = Integer.parseInt(Regex.regex("^(S|WS) ([0-9]{2})", a, 2));
|
||||||
String s2type = Regex.regex("(S|WS)", b);
|
String s2type = Regex.regex("^(S|WS) ", b);
|
||||||
int s2year = Integer.parseInt(Regex.regex("(S|WS) ([0-9]{2})", b, 2));
|
int s2year = Integer.parseInt(Regex.regex("^(S|WS) ([0-9]{2})", b, 2));
|
||||||
|
|
||||||
if (s1year == s2year) {
|
if (s1year == s2year) {
|
||||||
if (s1type.equals(s2type))
|
if (s1type.equals(s2type))
|
||||||
return EQUAL;
|
return EQUAL;
|
||||||
return s1type.equals("SS") ? SMALLER : LARGER;
|
return s1type.equals("S") ? SMALLER : LARGER;
|
||||||
}
|
}
|
||||||
return s1year < s2year ? SMALLER : LARGER;
|
return s1year < s2year ? SMALLER : LARGER;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import de.sebse.fuplanner.tools.network.NetworkErrorCallback;
|
|||||||
|
|
||||||
public class AsyncQueue {
|
public class AsyncQueue {
|
||||||
private final HashMap<String, LinkedList<AsyncQueueCallback>> mQueues = new HashMap<>();
|
private final HashMap<String, LinkedList<AsyncQueueCallback>> mQueues = new HashMap<>();
|
||||||
private final HashMap<String, Boolean> mRunnings = new HashMap<>();
|
private final HashMap<String, Boolean> mIsRunning = new HashMap<>();
|
||||||
|
|
||||||
public void add(String hash, AsyncQueueCallback callback) {
|
public void add(String hash, AsyncQueueCallback callback) {
|
||||||
if (isRunning(hash))
|
if (isRunning(hash))
|
||||||
@@ -46,11 +46,11 @@ public class AsyncQueue {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private boolean isRunning(String hash) {
|
private boolean isRunning(String hash) {
|
||||||
return mRunnings.containsKey(hash) && mRunnings.get(hash);
|
return mIsRunning.containsKey(hash) && mIsRunning.get(hash);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setRunning(String hash, boolean value) {
|
private void setRunning(String hash, boolean value) {
|
||||||
mRunnings.put(hash, value);
|
mIsRunning.put(hash, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
private LinkedList<AsyncQueueCallback> getQueue(String hash) {
|
private LinkedList<AsyncQueueCallback> getQueue(String hash) {
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ public class ColorRGB implements Color {
|
|||||||
private final int mGreen;
|
private final int mGreen;
|
||||||
private final int mBlue;
|
private final int mBlue;
|
||||||
|
|
||||||
public ColorRGB(int red, int green, int blue) {
|
private ColorRGB(int red, int green, int blue) {
|
||||||
this.mRed = red;
|
this.mRed = red;
|
||||||
this.mGreen = green;
|
this.mGreen = green;
|
||||||
this.mBlue = blue;
|
this.mBlue = blue;
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
package de.sebse.fuplanner.tools;
|
package de.sebse.fuplanner.tools;
|
||||||
|
|
||||||
import android.graphics.Paint;
|
import android.graphics.Paint;
|
||||||
import android.support.annotation.ColorInt;
|
import androidx.annotation.ColorInt;
|
||||||
|
|
||||||
public class ColorRes implements Color {
|
public class ColorRes implements Color {
|
||||||
@ColorInt private final int mResId;
|
@ColorInt private final int mResId;
|
||||||
|
|||||||
@@ -31,8 +31,8 @@ public abstract class DateSortedList<T> extends ArrayList<T> {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public T get(int index) {
|
public T get(int index) {
|
||||||
if (split < 0)
|
//if (split < 0)
|
||||||
sort();
|
// sort();
|
||||||
if (reversed())
|
if (reversed())
|
||||||
index = size() - index - 1;
|
index = size() - index - 1;
|
||||||
return super.get(index);
|
return super.get(index);
|
||||||
|
|||||||
@@ -1,23 +0,0 @@
|
|||||||
package de.sebse.fuplanner.tools;
|
|
||||||
|
|
||||||
import android.support.annotation.StringRes;
|
|
||||||
|
|
||||||
import de.sebse.fuplanner.services.Canteen.CanteenBrowser;
|
|
||||||
import de.sebse.fuplanner.services.GoogleAuth.GoogleAuth;
|
|
||||||
import de.sebse.fuplanner.services.KVV.KVV;
|
|
||||||
|
|
||||||
public interface MainAcitivityListener {
|
|
||||||
void onTitleTextChange(String newTitle);
|
|
||||||
|
|
||||||
void onTitleTextChange(@StringRes int titleId);
|
|
||||||
|
|
||||||
KVV getKVV();
|
|
||||||
|
|
||||||
GoogleAuth getGoogleAuth();
|
|
||||||
|
|
||||||
void loginTokenInvalid(boolean doPrecheck);
|
|
||||||
|
|
||||||
void refreshFailed(boolean isFailed);
|
|
||||||
|
|
||||||
CanteenBrowser getCanteenBrowser();
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
package de.sebse.fuplanner.tools;
|
||||||
|
|
||||||
|
import androidx.annotation.StringRes;
|
||||||
|
|
||||||
|
import de.sebse.fuplanner.services.Canteen.CanteenBrowser;
|
||||||
|
import de.sebse.fuplanner.services.GoogleAuth.GoogleAuth;
|
||||||
|
import de.sebse.fuplanner.services.KVV.KVV;
|
||||||
|
|
||||||
|
public interface MainActivityListener {
|
||||||
|
void onTitleTextChange(String newTitle);
|
||||||
|
|
||||||
|
void onTitleTextChange(@StringRes int titleId);
|
||||||
|
|
||||||
|
KVV getKVV();
|
||||||
|
|
||||||
|
GoogleAuth getGoogleAuth();
|
||||||
|
|
||||||
|
void loginTokenInvalid(boolean doLoginCheck);
|
||||||
|
|
||||||
|
void onRefreshCompleted(boolean isFailed);
|
||||||
|
|
||||||
|
CanteenBrowser getCanteenBrowser();
|
||||||
|
|
||||||
|
void addRequestPermissionsResultListener(RequestPermissionsResultListener listener, String id);
|
||||||
|
|
||||||
|
void removeRequestPermissionsResultListener(String id);
|
||||||
|
|
||||||
|
void showToast(@StringRes int msgStringRes);
|
||||||
|
|
||||||
|
void showToast(String message);
|
||||||
|
}
|
||||||
@@ -1,8 +1,8 @@
|
|||||||
package de.sebse.fuplanner.tools;
|
package de.sebse.fuplanner.tools;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.support.annotation.ArrayRes;
|
import androidx.annotation.ArrayRes;
|
||||||
import android.support.v7.preference.PreferenceManager;
|
import androidx.preference.PreferenceManager;
|
||||||
|
|
||||||
public class Preferences {
|
public class Preferences {
|
||||||
public static String getString(Context context, @ArrayRes int key) {
|
public static String getString(Context context, @ArrayRes int key) {
|
||||||
|
|||||||
@@ -9,6 +9,14 @@ import java.util.regex.Matcher;
|
|||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
public class Regex {
|
public class Regex {
|
||||||
|
public static boolean has(@Language("Regexp") String regex, String match) {
|
||||||
|
try {
|
||||||
|
regex(regex, match, 0);
|
||||||
|
return true;
|
||||||
|
} catch (NoSuchFieldException e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
public static String regex(@Language("Regexp") String regex, String match) throws NoSuchFieldException {
|
public static String regex(@Language("Regexp") String regex, String match) throws NoSuchFieldException {
|
||||||
return regex(regex, match, 1);
|
return regex(regex, match, 1);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,5 @@
|
|||||||
|
package de.sebse.fuplanner.tools;
|
||||||
|
|
||||||
|
public interface RequestPermissionsResultListener {
|
||||||
|
void callback(int requestCode, String[] permissions, int[] grantResults);
|
||||||
|
}
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
package de.sebse.fuplanner.tools;
|
package de.sebse.fuplanner.tools;
|
||||||
|
|
||||||
import android.support.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import android.support.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
@@ -25,6 +25,7 @@ public abstract class SortedList<T, I, F> implements Iterable<T>, Serializable {
|
|||||||
|
|
||||||
protected abstract boolean hasIdentifier(T o1, I id);
|
protected abstract boolean hasIdentifier(T o1, I id);
|
||||||
|
|
||||||
|
@SuppressWarnings("BooleanMethodIsAlwaysInverted")
|
||||||
protected abstract boolean hasFilter(T o1, F filter);
|
protected abstract boolean hasFilter(T o1, F filter);
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
|
|||||||
@@ -4,16 +4,14 @@ import android.annotation.SuppressLint;
|
|||||||
import android.annotation.TargetApi;
|
import android.annotation.TargetApi;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.support.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import android.text.format.DateFormat;
|
import android.text.format.DateFormat;
|
||||||
|
|
||||||
import com.google.android.gms.common.logging.Logger;
|
|
||||||
|
|
||||||
import java.text.SimpleDateFormat;
|
import java.text.SimpleDateFormat;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
|
|
||||||
public class DateUtils {
|
public class UtilsDate {
|
||||||
@Deprecated
|
@Deprecated
|
||||||
public static String getModifiedDateTime(long modified) {
|
public static String getModifiedDateTime(long modified) {
|
||||||
return getModifiedDateTime(null, modified);
|
return getModifiedDateTime(null, modified);
|
||||||
17
app/src/main/java/de/sebse/fuplanner/tools/UtilsUi.java
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
package de.sebse.fuplanner.tools;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.res.Resources;
|
||||||
|
import android.util.DisplayMetrics;
|
||||||
|
|
||||||
|
public class UtilsUi {
|
||||||
|
public static float convertPixelsToDp(float px, Context context) {
|
||||||
|
Resources resources = context.getResources();
|
||||||
|
DisplayMetrics metrics = resources.getDisplayMetrics();
|
||||||
|
return px / ((float) metrics.densityDpi / DisplayMetrics.DENSITY_DEFAULT);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static float convertDpToPixels(Context context, float dp) {
|
||||||
|
return dp * context.getResources().getDisplayMetrics().density;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -10,10 +10,14 @@ public class Logger {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public Logger(Object object) {
|
public Logger(Object object) {
|
||||||
|
this.tag = getClassName(object);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String getClassName(Object object) {
|
||||||
if (object instanceof String)
|
if (object instanceof String)
|
||||||
this.tag = (String) object;
|
return (String) object;
|
||||||
else
|
else
|
||||||
this.tag = object.getClass().getSimpleName();
|
return object.getClass().getSimpleName();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void d(Object... msg) {
|
public void d(Object... msg) {
|
||||||
|
|||||||
@@ -1,24 +1,18 @@
|
|||||||
package de.sebse.fuplanner.tools.network;
|
package de.sebse.fuplanner.tools.network;
|
||||||
|
|
||||||
import com.android.volley.AuthFailureError;
|
import com.android.volley.AuthFailureError;
|
||||||
|
import com.android.volley.Header;
|
||||||
import com.android.volley.Request;
|
import com.android.volley.Request;
|
||||||
|
import com.android.volley.toolbox.HttpResponse;
|
||||||
import com.android.volley.toolbox.HurlStack;
|
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.DataOutputStream;
|
||||||
|
import java.io.FilterInputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.net.HttpURLConnection;
|
import java.net.HttpURLConnection;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
@@ -28,12 +22,15 @@ import java.util.regex.Pattern;
|
|||||||
import javax.net.ssl.HttpsURLConnection;
|
import javax.net.ssl.HttpsURLConnection;
|
||||||
import javax.net.ssl.SSLSocketFactory;
|
import javax.net.ssl.SSLSocketFactory;
|
||||||
|
|
||||||
|
import androidx.annotation.VisibleForTesting;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Created by sebastian on 29.10.17.
|
* Created by sebastian on 29.10.17.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
public class BetterHurlStack extends HurlStack {
|
public class BetterHurlStack extends HurlStack {
|
||||||
private static final String HEADER_CONTENT_TYPE = "Content-Type";
|
private static final String HEADER_CONTENT_TYPE = "Content-Type";
|
||||||
|
private static final int HTTP_CONTINUE = 100;
|
||||||
|
|
||||||
private final UrlRewriter mUrlRewriter;
|
private final UrlRewriter mUrlRewriter;
|
||||||
private final SSLSocketFactory mSslSocketFactory;
|
private final SSLSocketFactory mSslSocketFactory;
|
||||||
@@ -50,14 +47,14 @@ public class BetterHurlStack extends HurlStack {
|
|||||||
/**
|
/**
|
||||||
* @param urlRewriter Rewriter to use for request URLs
|
* @param urlRewriter Rewriter to use for request URLs
|
||||||
*/
|
*/
|
||||||
BetterHurlStack(boolean followRedirects, UrlRewriter urlRewriter) {
|
private BetterHurlStack(boolean followRedirects, UrlRewriter urlRewriter) {
|
||||||
this(followRedirects, urlRewriter, null);
|
this(followRedirects, urlRewriter, null);
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* @param urlRewriter Rewriter to use for request URLs
|
* @param urlRewriter Rewriter to use for request URLs
|
||||||
* @param sslSocketFactory SSL factory to use for HTTPS connections
|
* @param sslSocketFactory SSL factory to use for HTTPS connections
|
||||||
*/
|
*/
|
||||||
BetterHurlStack(boolean followRedirects, UrlRewriter urlRewriter, SSLSocketFactory sslSocketFactory) {
|
private BetterHurlStack(boolean followRedirects, UrlRewriter urlRewriter, SSLSocketFactory sslSocketFactory) {
|
||||||
super(urlRewriter, sslSocketFactory);
|
super(urlRewriter, sslSocketFactory);
|
||||||
mUrlRewriter = urlRewriter;
|
mUrlRewriter = urlRewriter;
|
||||||
mSslSocketFactory = sslSocketFactory;
|
mSslSocketFactory = sslSocketFactory;
|
||||||
@@ -76,12 +73,12 @@ public class BetterHurlStack extends HurlStack {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public HttpResponse performRequest(Request<?> request, Map<String, String> additionalHeaders)
|
public HttpResponse executeRequest(Request<?> request, Map<String, String> additionalHeaders)
|
||||||
throws IOException, AuthFailureError {
|
throws IOException, AuthFailureError {
|
||||||
String url = request.getUrl();
|
String url = request.getUrl();
|
||||||
HashMap<String, String> map = new HashMap<>();
|
HashMap<String, String> map = new HashMap<>();
|
||||||
map.putAll(request.getHeaders());
|
|
||||||
map.putAll(additionalHeaders);
|
map.putAll(additionalHeaders);
|
||||||
|
map.putAll(request.getHeaders());
|
||||||
if (mUrlRewriter != null) {
|
if (mUrlRewriter != null) {
|
||||||
String rewritten = mUrlRewriter.rewriteUrl(url);
|
String rewritten = mUrlRewriter.rewriteUrl(url);
|
||||||
if (rewritten == null) {
|
if (rewritten == null) {
|
||||||
@@ -91,67 +88,85 @@ public class BetterHurlStack extends HurlStack {
|
|||||||
}
|
}
|
||||||
URL parsedUrl = new URL(url);
|
URL parsedUrl = new URL(url);
|
||||||
HttpURLConnection connection = openConnection(parsedUrl, request);
|
HttpURLConnection connection = openConnection(parsedUrl, request);
|
||||||
|
boolean keepConnectionOpen = false;
|
||||||
|
try {
|
||||||
for (String headerName : map.keySet()) {
|
for (String headerName : map.keySet()) {
|
||||||
connection.addRequestProperty(headerName, map.get(headerName));
|
connection.setRequestProperty(headerName, map.get(headerName));
|
||||||
}
|
}
|
||||||
setConnectionParametersForRequest(connection, request);
|
setConnectionParametersForRequest(connection, request);
|
||||||
// Initialize HttpResponse with data from the HttpURLConnection.
|
// Initialize HttpResponse with data from the HttpURLConnection.
|
||||||
ProtocolVersion protocolVersion = new ProtocolVersion("HTTP", 1, 1);
|
|
||||||
int responseCode = connection.getResponseCode();
|
int responseCode = connection.getResponseCode();
|
||||||
if (responseCode == -1) {
|
if (responseCode == -1) {
|
||||||
// -1 is returned by getResponseCode() if the response code could not be retrieved.
|
// -1 is returned by getResponseCode() if the response code could not be retrieved.
|
||||||
// Signal to the caller that something was wrong with the connection.
|
// Signal to the caller that something was wrong with the connection.
|
||||||
throw new IOException("Could not retrieve response code from HttpUrlConnection.");
|
throw new IOException("Could not retrieve response code from HttpUrlConnection.");
|
||||||
}
|
}
|
||||||
StatusLine responseStatus = new BasicStatusLine(protocolVersion,
|
|
||||||
connection.getResponseCode(), connection.getResponseMessage());
|
if (!hasResponseBody(request.getMethod(), responseCode)) {
|
||||||
BasicHttpResponse response = new BasicHttpResponse(responseStatus);
|
return new HttpResponse(responseCode, convertHeaders(connection.getHeaderFields()));
|
||||||
response.setEntity(entityFromConnection(connection));
|
}
|
||||||
for (Map.Entry<String, List<String>> header : connection.getHeaderFields().entrySet()) {
|
|
||||||
if (header.getKey() != null) {
|
// Need to keep the connection open until the stream is consumed by the caller. Wrap the
|
||||||
Header h;
|
// stream such that close() will disconnect the connection.
|
||||||
if (header.getKey().equals("Set-Cookie")) {
|
keepConnectionOpen = true;
|
||||||
|
return new HttpResponse(
|
||||||
|
responseCode,
|
||||||
|
convertHeaders(connection.getHeaderFields()),
|
||||||
|
connection.getContentLength(),
|
||||||
|
new UrlConnectionInputStream(connection));
|
||||||
|
|
||||||
|
} finally {
|
||||||
|
if (!keepConnectionOpen) {
|
||||||
|
connection.disconnect();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
private static List<Header> convertHeaders(Map<String, List<String>> responseHeaders) {
|
||||||
|
List<Header> headerList = new ArrayList<>(responseHeaders.size());
|
||||||
|
for (Map.Entry<String, List<String>> entry : responseHeaders.entrySet()) {
|
||||||
|
// HttpUrlConnection includes the status line as a header with a null key; omit it here
|
||||||
|
// since it's not really a header and the rest of Volley assumes non-null keys.
|
||||||
|
if (entry.getKey() != null) {
|
||||||
|
if (entry.getKey().equals("Set-Cookie")) {
|
||||||
Pattern pattern = Pattern.compile("^([^=]+=[^;]+;)");
|
Pattern pattern = Pattern.compile("^([^=]+=[^;]+;)");
|
||||||
StringBuilder cookieValue = new StringBuilder();
|
StringBuilder cookieValue = new StringBuilder();
|
||||||
for (String value: header.getValue()) {
|
for (String value : entry.getValue()) {
|
||||||
Matcher matcher = pattern.matcher(value);
|
Matcher matcher = pattern.matcher(value);
|
||||||
if (matcher.find()) {
|
if (matcher.find()) {
|
||||||
cookieValue.append(matcher.group(1));
|
cookieValue.append(matcher.group(1));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
h = new BasicHeader(header.getKey(), cookieValue.toString());
|
headerList.add(new Header(entry.getKey(), cookieValue.toString()));
|
||||||
} else {
|
} else {
|
||||||
h = new BasicHeader(header.getKey(), header.getValue().get(0));
|
headerList.add(new Header(entry.getKey(), entry.getValue().get(0)));
|
||||||
}
|
|
||||||
response.addHeader(h);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return response;
|
}
|
||||||
|
return headerList;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initializes an {@link HttpEntity} from the given {@link HttpURLConnection}.
|
* Checks if a response message contains a body.
|
||||||
* @param connection A http connection
|
*
|
||||||
* @return an HttpEntity populated with data from <code>connection</code>.
|
* @see <a href="https://tools.ietf.org/html/rfc7230#section-3.3">RFC 7230 section 3.3</a>
|
||||||
|
* @param requestMethod request method
|
||||||
|
* @param responseCode response status code
|
||||||
|
* @return whether the response has a body
|
||||||
*/
|
*/
|
||||||
private static HttpEntity entityFromConnection(HttpURLConnection connection) {
|
private static boolean hasResponseBody(int requestMethod, int responseCode) {
|
||||||
BasicHttpEntity entity = new BasicHttpEntity();
|
return requestMethod != Request.Method.HEAD
|
||||||
InputStream inputStream;
|
&& !(HTTP_CONTINUE <= responseCode && responseCode < HttpURLConnection.HTTP_OK)
|
||||||
try {
|
&& responseCode != HttpURLConnection.HTTP_NO_CONTENT
|
||||||
inputStream = connection.getInputStream();
|
&& responseCode != HttpURLConnection.HTTP_NOT_MODIFIED;
|
||||||
} catch (IOException ioe) {
|
|
||||||
inputStream = connection.getErrorStream();
|
|
||||||
}
|
|
||||||
entity.setContent(inputStream);
|
|
||||||
entity.setContentLength(connection.getContentLength());
|
|
||||||
entity.setContentEncoding(connection.getContentEncoding());
|
|
||||||
entity.setContentType(connection.getContentType());
|
|
||||||
return entity;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NOTE: Any request headers added here (via setRequestProperty or addRequestProperty) should be
|
||||||
|
// checked against the existing properties in the connection and not overridden if already set.
|
||||||
@SuppressWarnings("deprecation")
|
@SuppressWarnings("deprecation")
|
||||||
/* package */ static void setConnectionParametersForRequest(HttpURLConnection connection,
|
/* package */ private static void setConnectionParametersForRequest(
|
||||||
Request<?> request) throws IOException, AuthFailureError {
|
HttpURLConnection connection, Request<?> request) throws IOException, AuthFailureError {
|
||||||
switch (request.getMethod()) {
|
switch (request.getMethod()) {
|
||||||
case Request.Method.DEPRECATED_GET_OR_POST:
|
case Request.Method.DEPRECATED_GET_OR_POST:
|
||||||
// This is the deprecated way that needs to be handled for backwards compatibility.
|
// This is the deprecated way that needs to be handled for backwards compatibility.
|
||||||
@@ -159,16 +174,8 @@ public class BetterHurlStack extends HurlStack {
|
|||||||
// GET. Otherwise, it is assumed that the request is a POST.
|
// GET. Otherwise, it is assumed that the request is a POST.
|
||||||
byte[] postBody = request.getPostBody();
|
byte[] postBody = request.getPostBody();
|
||||||
if (postBody != null) {
|
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.setRequestMethod("POST");
|
||||||
connection.addRequestProperty(HEADER_CONTENT_TYPE,
|
addBody(connection, request, postBody);
|
||||||
request.getPostBodyContentType());
|
|
||||||
DataOutputStream out = new DataOutputStream(connection.getOutputStream());
|
|
||||||
out.write(postBody);
|
|
||||||
out.close();
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case Request.Method.GET:
|
case Request.Method.GET:
|
||||||
@@ -209,13 +216,25 @@ public class BetterHurlStack extends HurlStack {
|
|||||||
throws IOException, AuthFailureError {
|
throws IOException, AuthFailureError {
|
||||||
byte[] body = request.getBody();
|
byte[] body = request.getBody();
|
||||||
if (body != null) {
|
if (body != null) {
|
||||||
|
addBody(connection, request, body);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void addBody(HttpURLConnection connection, Request<?> request, byte[] body)
|
||||||
|
throws IOException {
|
||||||
|
// 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.setDoOutput(true);
|
||||||
connection.addRequestProperty(HEADER_CONTENT_TYPE, request.getBodyContentType());
|
// Set the content-type unless it was already set (by Request#getHeaders).
|
||||||
|
if (!connection.getRequestProperties().containsKey(HEADER_CONTENT_TYPE)) {
|
||||||
|
connection.setRequestProperty(
|
||||||
|
HEADER_CONTENT_TYPE, request.getBodyContentType());
|
||||||
|
}
|
||||||
DataOutputStream out = new DataOutputStream(connection.getOutputStream());
|
DataOutputStream out = new DataOutputStream(connection.getOutputStream());
|
||||||
out.write(body);
|
out.write(body);
|
||||||
out.close();
|
out.close();
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
private HttpURLConnection openConnection(URL url, Request<?> request) throws IOException {
|
private HttpURLConnection openConnection(URL url, Request<?> request) throws IOException {
|
||||||
HttpURLConnection connection = createConnection(url);
|
HttpURLConnection connection = createConnection(url);
|
||||||
@@ -230,4 +249,40 @@ public class BetterHurlStack extends HurlStack {
|
|||||||
}
|
}
|
||||||
return connection;
|
return connection;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wrapper for a {@link HttpURLConnection}'s InputStream which disconnects the connection on
|
||||||
|
* stream close.
|
||||||
|
*/
|
||||||
|
static class UrlConnectionInputStream extends FilterInputStream {
|
||||||
|
private final HttpURLConnection mConnection;
|
||||||
|
|
||||||
|
UrlConnectionInputStream(HttpURLConnection connection) {
|
||||||
|
super(inputStreamFromConnection(connection));
|
||||||
|
mConnection = connection;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void close() throws IOException {
|
||||||
|
super.close();
|
||||||
|
mConnection.disconnect();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initializes an {@link InputStream} from the given {@link HttpURLConnection}.
|
||||||
|
*
|
||||||
|
* @param connection A http url connection
|
||||||
|
* @return an HttpEntity populated with data from <code>connection</code>.
|
||||||
|
*/
|
||||||
|
private static InputStream inputStreamFromConnection(HttpURLConnection connection) {
|
||||||
|
InputStream inputStream;
|
||||||
|
try {
|
||||||
|
inputStream = connection.getInputStream();
|
||||||
|
} catch (IOException ioe) {
|
||||||
|
inputStream = connection.getErrorStream();
|
||||||
|
}
|
||||||
|
return inputStream;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
package de.sebse.fuplanner.tools.network;
|
package de.sebse.fuplanner.tools.network;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.support.annotation.Nullable;
|
|
||||||
|
|
||||||
import com.android.volley.AuthFailureError;
|
import com.android.volley.AuthFailureError;
|
||||||
import com.android.volley.NetworkResponse;
|
import com.android.volley.NetworkResponse;
|
||||||
@@ -17,6 +16,7 @@ import java.net.URLEncoder;
|
|||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
import de.sebse.fuplanner.tools.logging.Logger;
|
import de.sebse.fuplanner.tools.logging.Logger;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -25,14 +25,12 @@ import de.sebse.fuplanner.tools.logging.Logger;
|
|||||||
|
|
||||||
public class HTTPService {
|
public class HTTPService {
|
||||||
private final RequestQueue requestQueue;
|
private final RequestQueue requestQueue;
|
||||||
|
private final Context mContext;
|
||||||
protected Logger log = new Logger(this);
|
protected Logger log = new Logger(this);
|
||||||
|
|
||||||
protected HTTPService(Context context) {
|
protected HTTPService(Context context) {
|
||||||
this(context, false);
|
this.mContext = context;
|
||||||
}
|
requestQueue = Volley.newRequestQueue(context, new BetterHurlStack(false));
|
||||||
|
|
||||||
protected HTTPService(Context context, boolean 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) {
|
protected void get(String url, @Nullable final HashMap<String, String> cookies, Response.Listener<Result> response, Response.ErrorListener error) {
|
||||||
@@ -40,14 +38,14 @@ public class HTTPService {
|
|||||||
@Override
|
@Override
|
||||||
public void deliverError(VolleyError error) {
|
public void deliverError(VolleyError error) {
|
||||||
if (error == null) {
|
if (error == null) {
|
||||||
super.deliverError(new VolleyError(new NetworkResponse(500, null, null, true, 0)));
|
super.deliverError(new VolleyError(new NetworkResponse(500, null, true, 0, null)));
|
||||||
} else if (error.networkResponse == null) {
|
} else if (error.networkResponse == null) {
|
||||||
int statusCode;
|
int statusCode;
|
||||||
if (error instanceof TimeoutError)
|
if (error instanceof TimeoutError)
|
||||||
statusCode = 408;
|
statusCode = 408;
|
||||||
else
|
else
|
||||||
statusCode = 500;
|
statusCode = 500;
|
||||||
super.deliverError(new VolleyError(new NetworkResponse(statusCode, null, null, true, error.getNetworkTimeMs())));
|
super.deliverError(new VolleyError(new NetworkResponse(statusCode, null, true, error.getNetworkTimeMs(), null)));
|
||||||
} else {
|
} else {
|
||||||
final int status = error.networkResponse.statusCode;
|
final int status = error.networkResponse.statusCode;
|
||||||
if (status == 302) {
|
if (status == 302) {
|
||||||
@@ -96,7 +94,6 @@ public class HTTPService {
|
|||||||
sb.append('&');
|
sb.append('&');
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
//Log.e("Superissimo", e.getKey()+"|||"+e.getValue());
|
|
||||||
sb.append(URLEncoder.encode(e.getKey(), "UTF-8")).append('=').append(URLEncoder.encode(e.getValue(), "UTF-8"));
|
sb.append(URLEncoder.encode(e.getKey(), "UTF-8")).append('=').append(URLEncoder.encode(e.getValue(), "UTF-8"));
|
||||||
} catch (UnsupportedEncodingException ignored) {
|
} catch (UnsupportedEncodingException ignored) {
|
||||||
}
|
}
|
||||||
@@ -112,14 +109,14 @@ public class HTTPService {
|
|||||||
@Override
|
@Override
|
||||||
public void deliverError(VolleyError error) {
|
public void deliverError(VolleyError error) {
|
||||||
if (error == null) {
|
if (error == null) {
|
||||||
super.deliverError(new VolleyError(new NetworkResponse(500, null, null, true, 0)));
|
super.deliverError(new VolleyError(new NetworkResponse(500, null, true, 0, null)));
|
||||||
} else if (error.networkResponse == null) {
|
} else if (error.networkResponse == null) {
|
||||||
int statusCode;
|
int statusCode;
|
||||||
if (error instanceof TimeoutError)
|
if (error instanceof TimeoutError)
|
||||||
statusCode = 408;
|
statusCode = 408;
|
||||||
else
|
else
|
||||||
statusCode = 500;
|
statusCode = 500;
|
||||||
super.deliverError(new VolleyError(new NetworkResponse(statusCode, null, null, true, error.getNetworkTimeMs())));
|
super.deliverError(new VolleyError(new NetworkResponse(statusCode, null, true, error.getNetworkTimeMs(), null)));
|
||||||
} else {
|
} else {
|
||||||
final int status = error.networkResponse.statusCode;
|
final int status = error.networkResponse.statusCode;
|
||||||
if (status == 302) {
|
if (status == 302) {
|
||||||
@@ -149,4 +146,8 @@ public class HTTPService {
|
|||||||
};
|
};
|
||||||
requestQueue.add(request);
|
requestQueue.add(request);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected Context getContext() {
|
||||||
|
return mContext;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,8 +6,6 @@ import com.android.volley.Request;
|
|||||||
import com.android.volley.Response;
|
import com.android.volley.Response;
|
||||||
import com.android.volley.toolbox.HttpHeaderParser;
|
import com.android.volley.toolbox.HttpHeaderParser;
|
||||||
|
|
||||||
import java.io.UnsupportedEncodingException;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Created by sebastian on 24.10.17.
|
* Created by sebastian on 24.10.17.
|
||||||
*/
|
*/
|
||||||
@@ -28,13 +26,7 @@ class HttpRequest extends Request<Result> {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Response<Result> parseNetworkResponse(NetworkResponse response) {
|
protected Response<Result> parseNetworkResponse(NetworkResponse response) {
|
||||||
String parsed;
|
Result result = new Result(response.data, response.headers);
|
||||||
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));
|
return Response.success(result, HttpHeaderParser.parseCacheHeaders(response));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
package de.sebse.fuplanner.tools.network;
|
package de.sebse.fuplanner.tools.network;
|
||||||
|
|
||||||
import android.support.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
|
|
||||||
public interface NetworkCallback<T> {
|
public interface NetworkCallback<T> {
|
||||||
void onResponse(@NonNull T success);
|
void onResponse(@NonNull T success);
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ public class NetworkError {
|
|||||||
return httpStatus;
|
return httpStatus;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getMessage() {
|
private String getMessage() {
|
||||||
return message;
|
return message;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,21 +1,38 @@
|
|||||||
package de.sebse.fuplanner.tools.network;
|
package de.sebse.fuplanner.tools.network;
|
||||||
|
|
||||||
|
import com.android.volley.toolbox.HttpHeaderParser;
|
||||||
|
|
||||||
|
import java.io.UnsupportedEncodingException;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Created by sebastian on 24.10.17.
|
* Created by sebastian on 24.10.17.
|
||||||
*/
|
*/
|
||||||
public class Result {
|
public class Result {
|
||||||
private final String parsed;
|
@Nullable private final byte[] body;
|
||||||
private final Map<String, String> headers;
|
private final Map<String, String> headers;
|
||||||
|
|
||||||
public Result(String parsed, Map<String, String> headers) {
|
Result(@Nullable byte[] body, Map<String, String> headers) {
|
||||||
this.parsed = parsed;
|
this.body = body;
|
||||||
this.headers = headers;
|
this.headers = headers;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
public String getParsed() {
|
public String getParsed() {
|
||||||
return parsed;
|
if (this.body == null)
|
||||||
|
return null;
|
||||||
|
try {
|
||||||
|
return new String(this.body, HttpHeaderParser.parseCharset(headers));
|
||||||
|
} catch (UnsupportedEncodingException e) {
|
||||||
|
return new String(this.body);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public byte[] getBytes() {
|
||||||
|
return body;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Map<String, String> getHeaders() {
|
public Map<String, String> getHeaders() {
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
package de.sebse.fuplanner.tools.ui;
|
package de.sebse.fuplanner.tools.ui;
|
||||||
|
|
||||||
import android.support.v7.widget.RecyclerView;
|
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
|
|
||||||
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
|
|
||||||
public class CustomViewHolder extends RecyclerView.ViewHolder {
|
public class CustomViewHolder extends RecyclerView.ViewHolder {
|
||||||
public final View mView;
|
public final View mView;
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,27 @@
|
|||||||
|
package de.sebse.fuplanner.tools.ui;
|
||||||
|
|
||||||
|
import android.view.View;
|
||||||
|
|
||||||
|
import de.sebse.fuplanner.tools.ui.cardview.ExpandableCardView;
|
||||||
|
|
||||||
|
public class ExpandableCardViewHolder extends CustomViewHolder {
|
||||||
|
ExpandableCardViewHolder(View view) {
|
||||||
|
super(view);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void reset() {
|
||||||
|
getView().reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
private ExpandableCardView getView() {
|
||||||
|
return (ExpandableCardView) mView;
|
||||||
|
}
|
||||||
|
|
||||||
|
View getOuterView() {
|
||||||
|
return getView().getOuterView();
|
||||||
|
}
|
||||||
|
|
||||||
|
View getInnerView() {
|
||||||
|
return getView().getInnerView();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -9,7 +9,7 @@ public class ItemViewHolder extends CustomViewHolder {
|
|||||||
public final TextView mTitle;
|
public final TextView mTitle;
|
||||||
public final TextView mSubLeft;
|
public final TextView mSubLeft;
|
||||||
public final TextView mSubRight;
|
public final TextView mSubRight;
|
||||||
public final TextView mTopRight;
|
private final TextView mTopRight;
|
||||||
|
|
||||||
public ItemViewHolder(View view) {
|
public ItemViewHolder(View view) {
|
||||||
super(view);
|
super(view);
|
||||||
|
|||||||
@@ -0,0 +1,39 @@
|
|||||||
|
package de.sebse.fuplanner.tools.ui;
|
||||||
|
|
||||||
|
import android.view.View;
|
||||||
|
import android.widget.ImageView;
|
||||||
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
import de.sebse.fuplanner.R;
|
||||||
|
|
||||||
|
public class MealViewHolder extends ExpandableCardViewHolder {
|
||||||
|
public final TextView mTitle;
|
||||||
|
public final TextView mSubTitle;
|
||||||
|
public final TextView mCategory;
|
||||||
|
public final TextView mNotes;
|
||||||
|
|
||||||
|
public final ImageView mIconVegan;
|
||||||
|
public final ImageView mIconVegetarian;
|
||||||
|
public final ImageView mIconBio;
|
||||||
|
public final ImageView mIconMsc;
|
||||||
|
|
||||||
|
|
||||||
|
public MealViewHolder(View view) {
|
||||||
|
super(view);
|
||||||
|
View outerView = getOuterView();
|
||||||
|
View innerView = getInnerView();
|
||||||
|
mTitle = outerView.findViewById(R.id.title);
|
||||||
|
mSubTitle = outerView.findViewById(R.id.sub_title);
|
||||||
|
mNotes = innerView.findViewById(R.id.notes);
|
||||||
|
mCategory = innerView.findViewById(R.id.category);
|
||||||
|
mIconVegan = innerView.findViewById(R.id.icon_vegan);
|
||||||
|
mIconVegetarian = innerView.findViewById(R.id.icon_vegetarian);
|
||||||
|
mIconBio = innerView.findViewById(R.id.icon_organic);
|
||||||
|
mIconMsc = innerView.findViewById(R.id.icon_msc);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return super.toString() + " '" + mTitle.getText() + "' '" + mSubTitle.getText() + "'";
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,368 @@
|
|||||||
|
package de.sebse.fuplanner.tools.ui.cardview;
|
||||||
|
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.res.TypedArray;
|
||||||
|
import android.os.Build;
|
||||||
|
import android.util.AttributeSet;
|
||||||
|
import android.util.TypedValue;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.animation.Animation;
|
||||||
|
import android.view.animation.RotateAnimation;
|
||||||
|
import android.view.animation.Transformation;
|
||||||
|
import android.widget.ImageButton;
|
||||||
|
|
||||||
|
import androidx.annotation.LayoutRes;
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
import androidx.cardview.widget.CardView;
|
||||||
|
import de.sebse.fuplanner.R;
|
||||||
|
import de.sebse.fuplanner.tools.UtilsUi;
|
||||||
|
import de.sebse.fuplanner.tools.logging.Logger;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Copyright (c) 2018 Alessandro Sperotti
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* Created by alessandros on 23/02/2018.
|
||||||
|
* @author Alessandro Sperotti
|
||||||
|
*/
|
||||||
|
|
||||||
|
public class ExpandableCardView extends CardView {
|
||||||
|
private int innerViewRes;
|
||||||
|
private int outerViewRes;
|
||||||
|
|
||||||
|
private View innerView;
|
||||||
|
private View outerView;
|
||||||
|
private ImageButton imageButton;
|
||||||
|
|
||||||
|
private static final int DEFAULT_ANIM_DURATION = 350;
|
||||||
|
private long animDuration = DEFAULT_ANIM_DURATION;
|
||||||
|
|
||||||
|
private final static int COLLAPSING = 0;
|
||||||
|
private final static int EXPANDING = 1;
|
||||||
|
|
||||||
|
private boolean isExpanded = false;
|
||||||
|
private boolean isExpanding = false;
|
||||||
|
private boolean isCollapsing = false;
|
||||||
|
private boolean startExpanded = false;
|
||||||
|
|
||||||
|
private int collapsedHeight = 0;
|
||||||
|
private int expandedHeight = 0;
|
||||||
|
|
||||||
|
private OnExpandedListener listener;
|
||||||
|
|
||||||
|
private OnClickListener defaultClickListener = v -> {
|
||||||
|
if(isExpanded()) collapse();
|
||||||
|
else expand();
|
||||||
|
};
|
||||||
|
private Logger log = new Logger(this);
|
||||||
|
|
||||||
|
public ExpandableCardView(Context context) {
|
||||||
|
super(context);
|
||||||
|
initView(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ExpandableCardView(Context context, AttributeSet attrs) {
|
||||||
|
super(context, attrs);
|
||||||
|
|
||||||
|
initAttributes(context, attrs);
|
||||||
|
initView(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ExpandableCardView(Context context, AttributeSet attrs, int defStyleAttr) {
|
||||||
|
super(context, attrs, defStyleAttr);
|
||||||
|
|
||||||
|
initAttributes(context, attrs);
|
||||||
|
initView(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initView(Context context){
|
||||||
|
//Inflating View
|
||||||
|
imageButton = new ImageButton(context);
|
||||||
|
imageButton.setImageDrawable(getResources().getDrawable(R.drawable.arrow_down));
|
||||||
|
imageButton.setPadding(
|
||||||
|
(int) UtilsUi.convertDpToPixels(getContext(), 10),
|
||||||
|
(int) UtilsUi.convertDpToPixels(getContext(), 10),
|
||||||
|
(int) UtilsUi.convertDpToPixels(getContext(), 10),
|
||||||
|
(int) UtilsUi.convertDpToPixels(getContext(), 10)
|
||||||
|
);
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
|
||||||
|
TypedValue outValue = new TypedValue();
|
||||||
|
context.getTheme().resolveAttribute(android.R.attr.selectableItemBackground, outValue, true);
|
||||||
|
imageButton.setBackgroundResource(outValue.resourceId);
|
||||||
|
}
|
||||||
|
isExpanded = startExpanded;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initAttributes(Context context, AttributeSet attrs){
|
||||||
|
TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.ExpandableCardView);
|
||||||
|
|
||||||
|
innerViewRes = typedArray.getResourceId(R.styleable.ExpandableCardView_inner_view, View.NO_ID);
|
||||||
|
outerViewRes = typedArray.getResourceId(R.styleable.ExpandableCardView_outer_view, View.NO_ID);
|
||||||
|
animDuration = typedArray.getInteger(R.styleable.ExpandableCardView_animationDuration, DEFAULT_ANIM_DURATION);
|
||||||
|
startExpanded = typedArray.getBoolean(R.styleable.ExpandableCardView_startExpanded, false);
|
||||||
|
typedArray.recycle();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onFinishInflate() {
|
||||||
|
super.onFinishInflate();
|
||||||
|
|
||||||
|
innerView = inflateChild(innerViewRes);
|
||||||
|
outerView = inflateChild(outerViewRes);
|
||||||
|
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||||
|
setElevation(UtilsUi.convertDpToPixels(getContext(), 4));
|
||||||
|
}
|
||||||
|
|
||||||
|
setOnClickListener(defaultClickListener);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onLayout(boolean changed, int l, int t, int r, int b) {
|
||||||
|
removeAllViews();
|
||||||
|
int x = getPaddingLeft();
|
||||||
|
int y = getPaddingTop();
|
||||||
|
addView(outerView);
|
||||||
|
outerView.layout(x, y, x+outerView.getMeasuredWidth(), y+outerView.getMeasuredHeight());
|
||||||
|
addView(imageButton);
|
||||||
|
imageButton.layout(
|
||||||
|
getMeasuredWidth() - getPaddingRight() - imageButton.getMeasuredWidth(),
|
||||||
|
y,
|
||||||
|
getMeasuredWidth() - getPaddingRight(),
|
||||||
|
y+imageButton.getMeasuredHeight()
|
||||||
|
);
|
||||||
|
addView(innerView);
|
||||||
|
innerView.layout(x, y+outerView.getMeasuredHeight(), x+innerView.getMeasuredWidth(), y+outerView.getMeasuredHeight()+innerView.getMeasuredHeight());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
|
||||||
|
if (MeasureSpec.getSize(widthMeasureSpec) == 0 && MeasureSpec.getMode(widthMeasureSpec) != MeasureSpec.UNSPECIFIED || MeasureSpec.getSize(heightMeasureSpec) == 0 && MeasureSpec.getMode(heightMeasureSpec) != MeasureSpec.UNSPECIFIED) {
|
||||||
|
log.w("This should not happen! Invalid dimension size");
|
||||||
|
setMeasuredDimension(reconcileSize(10, widthMeasureSpec), reconcileSize(10, heightMeasureSpec));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int desiredWidth;
|
||||||
|
int desiredHeight;
|
||||||
|
|
||||||
|
int widthMeasure = atMostSpec(MeasureSpec.getSize(widthMeasureSpec), widthMeasureSpec);
|
||||||
|
int heightMeasure = atMostSpec(MeasureSpec.getSize(heightMeasureSpec), heightMeasureSpec);
|
||||||
|
imageButton.measure(widthMeasure, heightMeasure);
|
||||||
|
|
||||||
|
widthMeasure = atMostExactlySpec(Math.max(0, MeasureSpec.getSize(widthMeasureSpec)-imageButton.getMeasuredWidth()), widthMeasureSpec);
|
||||||
|
heightMeasure = atMostSpec(MeasureSpec.getSize(heightMeasureSpec), heightMeasureSpec);
|
||||||
|
outerView.measure(widthMeasure, heightMeasure);
|
||||||
|
desiredWidth = imageButton.getMeasuredWidth() + outerView.getMeasuredWidth();
|
||||||
|
desiredHeight = Math.max(imageButton.getMeasuredHeight(), outerView.getMeasuredHeight());
|
||||||
|
|
||||||
|
widthMeasure = atMostSpec(MeasureSpec.getSize(widthMeasureSpec), widthMeasureSpec);
|
||||||
|
heightMeasure = atMostSpec(Math.max(0, MeasureSpec.getSize(heightMeasureSpec)-desiredHeight), heightMeasureSpec);
|
||||||
|
innerView.measure(widthMeasure, heightMeasure);
|
||||||
|
desiredWidth = Math.max(desiredWidth, innerView.getMeasuredWidth());
|
||||||
|
desiredHeight += innerView.getMeasuredHeight();
|
||||||
|
|
||||||
|
desiredWidth += getPaddingLeft() + getPaddingRight();
|
||||||
|
desiredHeight += getPaddingTop() + getPaddingBottom();
|
||||||
|
|
||||||
|
if (MeasureSpec.getMode(heightMeasureSpec) != MeasureSpec.EXACTLY) {
|
||||||
|
expandedHeight = desiredHeight;
|
||||||
|
collapsedHeight = desiredHeight - innerView.getMeasuredHeight();
|
||||||
|
}
|
||||||
|
|
||||||
|
setMeasuredDimension(reconcileSize(desiredWidth, widthMeasureSpec), reconcileSize(isExpanded ? expandedHeight : collapsedHeight, heightMeasureSpec));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void expand() {
|
||||||
|
final int initialHeight = this.getHeight();
|
||||||
|
int targetHeight = expandedHeight;
|
||||||
|
|
||||||
|
if(targetHeight - initialHeight != 0) {
|
||||||
|
animateViews(initialHeight, targetHeight - initialHeight, EXPANDING);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void collapse() {
|
||||||
|
final int initialHeight = this.getHeight();
|
||||||
|
int targetHeight = collapsedHeight;
|
||||||
|
|
||||||
|
if(initialHeight - targetHeight != 0) {
|
||||||
|
animateViews(initialHeight, initialHeight - targetHeight, COLLAPSING);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isExpanded() {
|
||||||
|
return isExpanded;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void animateViews(final int initialHeight, final int distance, final int animationType){
|
||||||
|
|
||||||
|
Animation expandAnimation = new Animation() {
|
||||||
|
@Override
|
||||||
|
protected void applyTransformation(float interpolatedTime, Transformation t) {
|
||||||
|
if (interpolatedTime == 1) {
|
||||||
|
//Setting isExpanding/isCollapsing to false
|
||||||
|
isExpanding = false;
|
||||||
|
isCollapsing = false;
|
||||||
|
|
||||||
|
if (listener != null) {
|
||||||
|
if (animationType == EXPANDING)
|
||||||
|
listener.onExpandChanged(ExpandableCardView.this, true);
|
||||||
|
else
|
||||||
|
listener.onExpandChanged(ExpandableCardView.this, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ExpandableCardView.this.getLayoutParams().height = animationType == EXPANDING
|
||||||
|
? (int) (initialHeight + (distance * interpolatedTime))
|
||||||
|
: (int) (initialHeight - (distance * interpolatedTime));
|
||||||
|
ExpandableCardView.this.requestLayout();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean willChangeBounds() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
RotateAnimation arrowAnimation = animationType == EXPANDING ?
|
||||||
|
new RotateAnimation(0,180,Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF,
|
||||||
|
0.5f) :
|
||||||
|
new RotateAnimation(180,0,Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF,
|
||||||
|
0.5f);
|
||||||
|
|
||||||
|
arrowAnimation.setFillAfter(true);
|
||||||
|
|
||||||
|
|
||||||
|
arrowAnimation.setDuration(animDuration);
|
||||||
|
expandAnimation.setDuration(animDuration);
|
||||||
|
|
||||||
|
isExpanding = animationType == EXPANDING;
|
||||||
|
isCollapsing = animationType == COLLAPSING;
|
||||||
|
|
||||||
|
startAnimation(expandAnimation);
|
||||||
|
imageButton.startAnimation(arrowAnimation);
|
||||||
|
isExpanded = animationType == EXPANDING;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isExpanding(){
|
||||||
|
return isExpanding;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isCollapsing(){
|
||||||
|
return isCollapsing;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isMoving(){
|
||||||
|
return isExpanding() || isCollapsing();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setOnExpandedListener(OnExpandedListener listener) {
|
||||||
|
this.listener = listener;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void removeOnExpandedListener(){
|
||||||
|
this.listener = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private View inflateChild(@LayoutRes int resId) {
|
||||||
|
return inflate(getContext(), resId, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public View getOuterView() {
|
||||||
|
return outerView;
|
||||||
|
}
|
||||||
|
|
||||||
|
public View getInnerView() {
|
||||||
|
return innerView;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setOnClickListener(@Nullable OnClickListener l) {
|
||||||
|
if(imageButton != null) imageButton.setOnClickListener(l);
|
||||||
|
super.setOnClickListener(l);
|
||||||
|
}
|
||||||
|
|
||||||
|
private long getAnimDuration() {
|
||||||
|
return animDuration;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setAnimDuration(long animDuration) {
|
||||||
|
this.animDuration = animDuration;
|
||||||
|
}
|
||||||
|
|
||||||
|
private int reconcileSize(int contentSize, int measureSpec) {
|
||||||
|
final int mode = MeasureSpec.getMode(measureSpec);
|
||||||
|
final int specSize = MeasureSpec.getSize(measureSpec);
|
||||||
|
switch(mode) {
|
||||||
|
case MeasureSpec.EXACTLY:
|
||||||
|
return specSize;
|
||||||
|
case MeasureSpec.AT_MOST:
|
||||||
|
if (contentSize < specSize) {
|
||||||
|
return contentSize;
|
||||||
|
} else {
|
||||||
|
log.w("Your content may be cropped!");
|
||||||
|
return specSize;
|
||||||
|
}
|
||||||
|
case MeasureSpec.UNSPECIFIED:
|
||||||
|
default:
|
||||||
|
return contentSize;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private int atMostSpec(int atMostSpace, int measureSpec) {
|
||||||
|
if (MeasureSpec.getMode(measureSpec) == MeasureSpec.UNSPECIFIED)
|
||||||
|
return measureSpec;
|
||||||
|
else
|
||||||
|
return MeasureSpec.makeMeasureSpec(atMostSpace, MeasureSpec.AT_MOST);
|
||||||
|
}
|
||||||
|
|
||||||
|
private int atMostExactlySpec(int atMostExactlySpace, int measureSpec) {
|
||||||
|
if (MeasureSpec.getMode(measureSpec) == MeasureSpec.UNSPECIFIED)
|
||||||
|
return measureSpec;
|
||||||
|
else
|
||||||
|
return MeasureSpec.makeMeasureSpec(atMostExactlySpace, MeasureSpec.getMode(measureSpec));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void reset() {
|
||||||
|
long anim = getAnimDuration();
|
||||||
|
this.setAnimDuration(0);
|
||||||
|
if (startExpanded) {
|
||||||
|
this.expand();
|
||||||
|
} else {
|
||||||
|
this.collapse();
|
||||||
|
}
|
||||||
|
this.setAnimDuration(anim);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interfaces
|
||||||
|
*/
|
||||||
|
|
||||||
|
interface OnExpandedListener {
|
||||||
|
|
||||||
|
void onExpandChanged(View v, boolean isExpanded);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -0,0 +1,56 @@
|
|||||||
|
package de.sebse.fuplanner.tools.ui.treeview;
|
||||||
|
|
||||||
|
import android.view.View;
|
||||||
|
import android.widget.ImageView;
|
||||||
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
import de.sebse.fuplanner.R;
|
||||||
|
import de.sebse.fuplanner.services.KVV.types.Resource;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by tlh on 2016/10/1 :)
|
||||||
|
*/
|
||||||
|
|
||||||
|
public class DirectoryNodeBinder extends TreeViewBinder<DirectoryNodeBinder.ViewHolder> {
|
||||||
|
@Override
|
||||||
|
public ViewHolder provideViewHolder(View itemView) {
|
||||||
|
return new ViewHolder(itemView);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void bindView(ViewHolder holder, int position, TreeNode node) {
|
||||||
|
holder.ivArrow.setRotation(0);
|
||||||
|
holder.ivArrow.setImageResource(R.drawable.ic_keyboard_arrow_right_black_18dp);
|
||||||
|
int rotateDegree = node.isExpand() ? 90 : 0;
|
||||||
|
holder.ivArrow.setRotation(rotateDegree);
|
||||||
|
Resource.Folder dirNode = (Resource.Folder) node.getContent();
|
||||||
|
holder.tvName.setText(dirNode.getTitle());
|
||||||
|
if (node.isLeaf())
|
||||||
|
holder.ivArrow.setVisibility(View.INVISIBLE);
|
||||||
|
else holder.ivArrow.setVisibility(View.VISIBLE);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getLayoutId() {
|
||||||
|
return R.layout.item_dir;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class ViewHolder extends TreeViewBinder.ViewHolder {
|
||||||
|
private ImageView ivArrow;
|
||||||
|
private TextView tvName;
|
||||||
|
|
||||||
|
ViewHolder(View rootView) {
|
||||||
|
super(rootView);
|
||||||
|
this.ivArrow = rootView.findViewById(R.id.iv_arrow);
|
||||||
|
this.tvName = rootView.findViewById(R.id.tv_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ImageView getIvArrow() {
|
||||||
|
return ivArrow;
|
||||||
|
}
|
||||||
|
|
||||||
|
public TextView getTvName() {
|
||||||
|
return tvName;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
package de.sebse.fuplanner.tools.ui.treeview;
|
||||||
|
|
||||||
|
import android.view.View;
|
||||||
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
import de.sebse.fuplanner.R;
|
||||||
|
import de.sebse.fuplanner.services.KVV.types.Resource;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by tlh on 2016/10/1 :)
|
||||||
|
*/
|
||||||
|
|
||||||
|
public class FileNodeBinder extends TreeViewBinder<FileNodeBinder.ViewHolder> {
|
||||||
|
@Override
|
||||||
|
public ViewHolder provideViewHolder(View itemView) {
|
||||||
|
return new ViewHolder(itemView);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void bindView(ViewHolder holder, int position, TreeNode node) {
|
||||||
|
Resource.File fileNode = (Resource.File) node.getContent();
|
||||||
|
holder.tvName.setText(fileNode.getTitle());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getLayoutId() {
|
||||||
|
return R.layout.item_file;
|
||||||
|
}
|
||||||
|
|
||||||
|
public class ViewHolder extends TreeViewBinder.ViewHolder {
|
||||||
|
TextView tvName;
|
||||||
|
|
||||||
|
ViewHolder(View rootView) {
|
||||||
|
super(rootView);
|
||||||
|
this.tvName = rootView.findViewById(R.id.tv_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
package de.sebse.fuplanner.tools.ui.treeview;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by tlh on 2016/10/1 :)
|
||||||
|
*/
|
||||||
|
|
||||||
|
public interface LayoutItemType {
|
||||||
|
int getLayoutId();
|
||||||
|
}
|
||||||
@@ -0,0 +1,147 @@
|
|||||||
|
package de.sebse.fuplanner.tools.ui.treeview;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by tlh on 2016/10/1 :)
|
||||||
|
*/
|
||||||
|
|
||||||
|
public class TreeNode<T extends LayoutItemType> implements Cloneable {
|
||||||
|
private T content;
|
||||||
|
private TreeNode parent;
|
||||||
|
private List<TreeNode> childList;
|
||||||
|
private boolean isExpand;
|
||||||
|
private boolean isLocked;
|
||||||
|
//the tree high
|
||||||
|
private int height = UNDEFINE;
|
||||||
|
|
||||||
|
private static final int UNDEFINE = -1;
|
||||||
|
|
||||||
|
public TreeNode(@NonNull T content) {
|
||||||
|
this.content = content;
|
||||||
|
this.childList = new ArrayList<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getHeight() {
|
||||||
|
if (isRoot())
|
||||||
|
height = 0;
|
||||||
|
else if (height == UNDEFINE)
|
||||||
|
height = parent.getHeight() + 1;
|
||||||
|
return height;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isRoot() {
|
||||||
|
return parent == null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isLeaf() {
|
||||||
|
return childList == null || childList.isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setContent(T content) {
|
||||||
|
this.content = content;
|
||||||
|
}
|
||||||
|
|
||||||
|
public T getContent() {
|
||||||
|
return content;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<TreeNode> getChildList() {
|
||||||
|
return childList;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setChildList(List<TreeNode> childList) {
|
||||||
|
this.childList.clear();
|
||||||
|
for (TreeNode treeNode : childList) {
|
||||||
|
addChild(treeNode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addChild(TreeNode node) {
|
||||||
|
if (childList == null)
|
||||||
|
childList = new ArrayList<>();
|
||||||
|
childList.add(node);
|
||||||
|
node.parent = this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void toggle() {
|
||||||
|
isExpand = !isExpand;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void collapse() {
|
||||||
|
if (isExpand) {
|
||||||
|
isExpand = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void collapseAll() {
|
||||||
|
if (childList == null || childList.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (TreeNode child : this.childList) {
|
||||||
|
child.collapseAll();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void expand() {
|
||||||
|
if (!isExpand) {
|
||||||
|
isExpand = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void expandAll() {
|
||||||
|
expand();
|
||||||
|
if (childList == null || childList.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (TreeNode child : this.childList) {
|
||||||
|
child.expandAll();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isExpand() {
|
||||||
|
return isExpand;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setParent(TreeNode parent) {
|
||||||
|
this.parent = parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
public TreeNode getParent() {
|
||||||
|
return parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
public TreeNode<T> lock() {
|
||||||
|
isLocked = true;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public TreeNode<T> unlock() {
|
||||||
|
isLocked = false;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isLocked() {
|
||||||
|
return isLocked;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "TreeNode{" +
|
||||||
|
"content=" + this.content +
|
||||||
|
", parent=" + (parent == null ? "null" : parent.getContent().toString()) +
|
||||||
|
", childList=" + (childList == null ? "null" : childList.toString()) +
|
||||||
|
", isExpand=" + isExpand +
|
||||||
|
'}';
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected TreeNode<T> clone() {
|
||||||
|
TreeNode<T> clone = new TreeNode<>(this.content);
|
||||||
|
clone.isExpand = this.isExpand;
|
||||||
|
return clone;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,317 @@
|
|||||||
|
package de.sebse.fuplanner.tools.ui.treeview;
|
||||||
|
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
import androidx.recyclerview.widget.DiffUtil;
|
||||||
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by tlh on 2016/10/1 :)
|
||||||
|
*/
|
||||||
|
public class TreeViewAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
|
||||||
|
private static final String KEY_IS_EXPAND = "IS_EXPAND";
|
||||||
|
private final List<? extends TreeViewBinder> viewBinders;
|
||||||
|
private List<TreeNode> displayNodes;
|
||||||
|
private int padding = 30;
|
||||||
|
private OnTreeNodeListener onTreeNodeListener;
|
||||||
|
private boolean toCollapseChild;
|
||||||
|
|
||||||
|
protected TreeViewAdapter(List<? extends TreeViewBinder> viewBinders) {
|
||||||
|
this(null, viewBinders);
|
||||||
|
}
|
||||||
|
|
||||||
|
private TreeViewAdapter(List<TreeNode> nodes, List<? extends TreeViewBinder> viewBinders) {
|
||||||
|
displayNodes = new ArrayList<>();
|
||||||
|
if (nodes != null)
|
||||||
|
findDisplayNodes(nodes);
|
||||||
|
this.viewBinders = viewBinders;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 从nodes的结点中寻找展开了的非叶结点,添加到displayNodes中。
|
||||||
|
*
|
||||||
|
* @param nodes 基准点
|
||||||
|
*/
|
||||||
|
private void findDisplayNodes(List<TreeNode> nodes) {
|
||||||
|
for (TreeNode node : nodes) {
|
||||||
|
displayNodes.add(node);
|
||||||
|
if (!node.isLeaf() && node.isExpand())
|
||||||
|
findDisplayNodes(node.getChildList());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getItemViewType(int position) {
|
||||||
|
return displayNodes.get(position).getContent().getLayoutId();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
|
||||||
|
View v = LayoutInflater.from(parent.getContext())
|
||||||
|
.inflate(viewType, parent, false);
|
||||||
|
if (viewBinders.size() == 1)
|
||||||
|
return viewBinders.get(0).provideViewHolder(v);
|
||||||
|
for (TreeViewBinder viewBinder : viewBinders) {
|
||||||
|
if (viewBinder.getLayoutId() == viewType)
|
||||||
|
return viewBinder.provideViewHolder(v);
|
||||||
|
}
|
||||||
|
return viewBinders.get(0).provideViewHolder(v);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position, List<Object> payloads) {
|
||||||
|
if (payloads != null && !payloads.isEmpty()) {
|
||||||
|
Bundle b = (Bundle) payloads.get(0);
|
||||||
|
for (String key : b.keySet()) {
|
||||||
|
switch (key) {
|
||||||
|
case KEY_IS_EXPAND:
|
||||||
|
if (onTreeNodeListener != null)
|
||||||
|
onTreeNodeListener.onToggle(b.getBoolean(key), holder);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
super.onBindViewHolder(holder, position, payloads);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onBindViewHolder(final RecyclerView.ViewHolder holder, int position) {
|
||||||
|
holder.itemView.setPadding(displayNodes.get(position).getHeight() * padding, 3, 3, 3);
|
||||||
|
holder.itemView.setOnClickListener(new View.OnClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onClick(View v) {
|
||||||
|
TreeNode selectedNode = displayNodes.get(holder.getLayoutPosition());
|
||||||
|
// Prevent multi-click during the short interval.
|
||||||
|
try {
|
||||||
|
long lastClickTime = (long) holder.itemView.getTag();
|
||||||
|
if (System.currentTimeMillis() - lastClickTime < 500)
|
||||||
|
return;
|
||||||
|
} catch (Exception e) {
|
||||||
|
holder.itemView.setTag(System.currentTimeMillis());
|
||||||
|
}
|
||||||
|
holder.itemView.setTag(System.currentTimeMillis());
|
||||||
|
|
||||||
|
if (onTreeNodeListener != null && onTreeNodeListener.onClick(selectedNode, holder))
|
||||||
|
return;
|
||||||
|
if (selectedNode.isLeaf())
|
||||||
|
return;
|
||||||
|
// This TreeNode was locked to click.
|
||||||
|
if (selectedNode.isLocked()) return;
|
||||||
|
boolean isExpand = selectedNode.isExpand();
|
||||||
|
int positionStart = displayNodes.indexOf(selectedNode) + 1;
|
||||||
|
if (!isExpand) {
|
||||||
|
notifyItemRangeInserted(positionStart, addChildNodes(selectedNode, positionStart));
|
||||||
|
} else {
|
||||||
|
notifyItemRangeRemoved(positionStart, removeChildNodes(selectedNode, true));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
for (TreeViewBinder viewBinder : viewBinders) {
|
||||||
|
if (viewBinder.getLayoutId() == displayNodes.get(position).getContent().getLayoutId())
|
||||||
|
viewBinder.bindView(holder, position, displayNodes.get(position));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private int addChildNodes(TreeNode pNode, int startIndex) {
|
||||||
|
List<TreeNode> childList = pNode.getChildList();
|
||||||
|
int addChildCount = 0;
|
||||||
|
for (TreeNode treeNode : childList) {
|
||||||
|
displayNodes.add(startIndex + addChildCount++, treeNode);
|
||||||
|
if (treeNode.isExpand()) {
|
||||||
|
addChildCount += addChildNodes(treeNode, startIndex + addChildCount);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!pNode.isExpand())
|
||||||
|
pNode.toggle();
|
||||||
|
return addChildCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
private int removeChildNodes(TreeNode pNode) {
|
||||||
|
return removeChildNodes(pNode, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private int removeChildNodes(TreeNode pNode, boolean shouldToggle) {
|
||||||
|
if (pNode.isLeaf())
|
||||||
|
return 0;
|
||||||
|
List<TreeNode> childList = pNode.getChildList();
|
||||||
|
int removeChildCount = childList.size();
|
||||||
|
displayNodes.removeAll(childList);
|
||||||
|
for (TreeNode child : childList) {
|
||||||
|
if (child.isExpand()) {
|
||||||
|
if (toCollapseChild)
|
||||||
|
child.toggle();
|
||||||
|
removeChildCount += removeChildNodes(child, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (shouldToggle)
|
||||||
|
pNode.toggle();
|
||||||
|
return removeChildCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getItemCount() {
|
||||||
|
return displayNodes == null ? 0 : displayNodes.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPadding(int padding) {
|
||||||
|
this.padding = padding;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ifCollapseChildWhileCollapseParent(boolean toCollapseChild) {
|
||||||
|
this.toCollapseChild = toCollapseChild;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setOnTreeNodeListener(OnTreeNodeListener onTreeNodeListener) {
|
||||||
|
this.onTreeNodeListener = onTreeNodeListener;
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface OnTreeNodeListener {
|
||||||
|
/**
|
||||||
|
* called when TreeNodes were clicked.
|
||||||
|
* @return weather consume the click event.
|
||||||
|
*/
|
||||||
|
boolean onClick(TreeNode node, RecyclerView.ViewHolder holder);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* called when TreeNodes were toggle.
|
||||||
|
* @param isExpand the status of TreeNodes after being toggled.
|
||||||
|
*/
|
||||||
|
void onToggle(boolean isExpand, RecyclerView.ViewHolder holder);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void refresh(List<TreeNode> treeNodes) {
|
||||||
|
displayNodes.clear();
|
||||||
|
findDisplayNodes(treeNodes);
|
||||||
|
notifyDataSetChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Iterator<TreeNode> getDisplayNodesIterator() {
|
||||||
|
return displayNodes.iterator();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void notifyDiff(final List<TreeNode> temp) {
|
||||||
|
DiffUtil.DiffResult diffResult = DiffUtil.calculateDiff(new DiffUtil.Callback() {
|
||||||
|
@Override
|
||||||
|
public int getOldListSize() {
|
||||||
|
return temp.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getNewListSize() {
|
||||||
|
return displayNodes.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
// judge if the same items
|
||||||
|
@Override
|
||||||
|
public boolean areItemsTheSame(int oldItemPosition, int newItemPosition) {
|
||||||
|
return TreeViewAdapter.this.areItemsTheSame(temp.get(oldItemPosition), displayNodes.get(newItemPosition));
|
||||||
|
}
|
||||||
|
|
||||||
|
// if they are the same items, whether the contents has bean changed.
|
||||||
|
@Override
|
||||||
|
public boolean areContentsTheSame(int oldItemPosition, int newItemPosition) {
|
||||||
|
return TreeViewAdapter.this.areContentsTheSame(temp.get(oldItemPosition), displayNodes.get(newItemPosition));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
public Object getChangePayload(int oldItemPosition, int newItemPosition) {
|
||||||
|
return TreeViewAdapter.this.getChangePayload(temp.get(oldItemPosition), displayNodes.get(newItemPosition));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
diffResult.dispatchUpdatesTo(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Object getChangePayload(TreeNode oldNode, TreeNode newNode) {
|
||||||
|
Bundle diffBundle = new Bundle();
|
||||||
|
if (newNode.isExpand() != oldNode.isExpand()) {
|
||||||
|
diffBundle.putBoolean(KEY_IS_EXPAND, newNode.isExpand());
|
||||||
|
}
|
||||||
|
if (diffBundle.size() == 0)
|
||||||
|
return null;
|
||||||
|
return diffBundle;
|
||||||
|
}
|
||||||
|
|
||||||
|
// For DiffUtil, if they are the same items, whether the contents has bean changed.
|
||||||
|
private boolean areContentsTheSame(TreeNode oldNode, TreeNode newNode) {
|
||||||
|
return oldNode.getContent() != null && oldNode.getContent().equals(newNode.getContent())
|
||||||
|
&& oldNode.isExpand() == newNode.isExpand();
|
||||||
|
}
|
||||||
|
|
||||||
|
// judge if the same item for DiffUtil
|
||||||
|
private boolean areItemsTheSame(TreeNode oldNode, TreeNode newNode) {
|
||||||
|
return oldNode.getContent() != null && oldNode.getContent().equals(newNode.getContent());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* collapse all root nodes.
|
||||||
|
*/
|
||||||
|
public void collapseAll() {
|
||||||
|
// Back up the nodes are displaying.
|
||||||
|
List<TreeNode> temp = backupDisplayNodes();
|
||||||
|
//find all root nodes.
|
||||||
|
List<TreeNode> roots = new ArrayList<>();
|
||||||
|
for (TreeNode displayNode : displayNodes) {
|
||||||
|
if (displayNode.isRoot())
|
||||||
|
roots.add(displayNode);
|
||||||
|
}
|
||||||
|
//Close all root nodes.
|
||||||
|
for (TreeNode root : roots) {
|
||||||
|
if (root.isExpand())
|
||||||
|
removeChildNodes(root);
|
||||||
|
}
|
||||||
|
notifyDiff(temp);
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
private List<TreeNode> backupDisplayNodes() {
|
||||||
|
List<TreeNode> temp = new ArrayList<>();
|
||||||
|
for (TreeNode displayNode : displayNodes) {
|
||||||
|
temp.add(displayNode.clone());
|
||||||
|
}
|
||||||
|
return temp;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void collapseNode(TreeNode pNode) {
|
||||||
|
List<TreeNode> temp = backupDisplayNodes();
|
||||||
|
removeChildNodes(pNode);
|
||||||
|
notifyDiff(temp);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void collapseBrotherNode(TreeNode pNode) {
|
||||||
|
List<TreeNode> temp = backupDisplayNodes();
|
||||||
|
if (pNode.isRoot()) {
|
||||||
|
List<TreeNode> roots = new ArrayList<>();
|
||||||
|
for (TreeNode displayNode : displayNodes) {
|
||||||
|
if (displayNode.isRoot())
|
||||||
|
roots.add(displayNode);
|
||||||
|
}
|
||||||
|
//Close all root nodes.
|
||||||
|
for (TreeNode root : roots) {
|
||||||
|
if (root.isExpand() && !root.equals(pNode))
|
||||||
|
removeChildNodes(root);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
TreeNode parent = pNode.getParent();
|
||||||
|
if (parent == null)
|
||||||
|
return;
|
||||||
|
List<TreeNode> childList = parent.getChildList();
|
||||||
|
for (TreeNode node : childList) {
|
||||||
|
if (node.equals(pNode) || !node.isExpand())
|
||||||
|
continue;
|
||||||
|
removeChildNodes(node);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
notifyDiff(temp);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
package de.sebse.fuplanner.tools.ui.treeview;
|
||||||
|
|
||||||
|
import android.view.View;
|
||||||
|
|
||||||
|
import androidx.annotation.IdRes;
|
||||||
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
|
|
||||||
|
|
||||||
|
public abstract class TreeViewBinder<VH extends RecyclerView.ViewHolder> implements LayoutItemType {
|
||||||
|
public abstract VH provideViewHolder(View itemView);
|
||||||
|
|
||||||
|
public abstract void bindView(VH holder, int position, TreeNode node);
|
||||||
|
|
||||||
|
public static class ViewHolder extends RecyclerView.ViewHolder {
|
||||||
|
public ViewHolder(View rootView) {
|
||||||
|
super(rootView);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected <T extends View> T findViewById(@IdRes int id) {
|
||||||
|
//noinspection unchecked
|
||||||
|
return (T) itemView.findViewById(id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
package de.sebse.fuplanner.tools.ui.weekview;
|
package de.sebse.fuplanner.tools.ui.weekview;
|
||||||
|
|
||||||
import android.support.annotation.ColorInt;
|
import androidx.annotation.ColorInt;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -9,16 +9,10 @@ import android.graphics.Paint;
|
|||||||
import android.graphics.PointF;
|
import android.graphics.PointF;
|
||||||
import android.graphics.Rect;
|
import android.graphics.Rect;
|
||||||
import android.graphics.RectF;
|
import android.graphics.RectF;
|
||||||
import android.graphics.Region;
|
|
||||||
import android.graphics.Typeface;
|
import android.graphics.Typeface;
|
||||||
import android.graphics.drawable.BitmapDrawable;
|
import android.graphics.drawable.BitmapDrawable;
|
||||||
import android.graphics.drawable.Drawable;
|
import android.graphics.drawable.Drawable;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.support.annotation.Nullable;
|
|
||||||
import android.support.annotation.RequiresApi;
|
|
||||||
import android.support.v4.view.GestureDetectorCompat;
|
|
||||||
import android.support.v4.view.ViewCompat;
|
|
||||||
import android.support.v4.view.animation.FastOutLinearInInterpolator;
|
|
||||||
import android.text.Layout;
|
import android.text.Layout;
|
||||||
import android.text.SpannableStringBuilder;
|
import android.text.SpannableStringBuilder;
|
||||||
import android.text.StaticLayout;
|
import android.text.StaticLayout;
|
||||||
@@ -45,9 +39,15 @@ import java.util.Collections;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
|
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
import androidx.annotation.RequiresApi;
|
||||||
|
import androidx.core.view.GestureDetectorCompat;
|
||||||
|
import androidx.core.view.ViewCompat;
|
||||||
|
import androidx.interpolator.view.animation.FastOutLinearInInterpolator;
|
||||||
import de.sebse.fuplanner.R;
|
import de.sebse.fuplanner.R;
|
||||||
import de.sebse.fuplanner.tools.ColorRes;
|
import de.sebse.fuplanner.tools.ColorRes;
|
||||||
import de.sebse.fuplanner.tools.DateUtils;
|
import de.sebse.fuplanner.tools.UtilsDate;
|
||||||
|
import de.sebse.fuplanner.tools.UtilsUi;
|
||||||
import de.sebse.fuplanner.tools.logging.Logger;
|
import de.sebse.fuplanner.tools.logging.Logger;
|
||||||
|
|
||||||
import static de.sebse.fuplanner.tools.ui.weekview.WeekViewUtil.daysBetween;
|
import static de.sebse.fuplanner.tools.ui.weekview.WeekViewUtil.daysBetween;
|
||||||
@@ -62,7 +62,6 @@ import static de.sebse.fuplanner.tools.ui.weekview.WeekViewUtil.today;
|
|||||||
*/
|
*/
|
||||||
@SuppressWarnings("unused")
|
@SuppressWarnings("unused")
|
||||||
public class WeekView extends View {
|
public class WeekView extends View {
|
||||||
|
|
||||||
private enum Direction {
|
private enum Direction {
|
||||||
NONE, LEFT, RIGHT, VERTICAL
|
NONE, LEFT, RIGHT, VERTICAL
|
||||||
}
|
}
|
||||||
@@ -361,8 +360,8 @@ public class WeekView extends View {
|
|||||||
selectedTime.set(Calendar.HOUR_OF_DAY, mMinTime);
|
selectedTime.set(Calendar.HOUR_OF_DAY, mMinTime);
|
||||||
selectedTime.set(Calendar.MINUTE, 0);
|
selectedTime.set(Calendar.MINUTE, 0);
|
||||||
}
|
}
|
||||||
int unroundedMinutes = selectedTime.get(Calendar.MINUTE);
|
int nonRoundedMinutes = selectedTime.get(Calendar.MINUTE);
|
||||||
int mod = unroundedMinutes % mNewEventTimeResolutionInMinutes;
|
int mod = nonRoundedMinutes % mNewEventTimeResolutionInMinutes;
|
||||||
selectedTime.add(Calendar.MINUTE, mod < Math.ceil(mNewEventTimeResolutionInMinutes / 2) ? -mod : (mNewEventTimeResolutionInMinutes - mod));
|
selectedTime.add(Calendar.MINUTE, mod < Math.ceil(mNewEventTimeResolutionInMinutes / 2) ? -mod : (mNewEventTimeResolutionInMinutes - mod));
|
||||||
|
|
||||||
Calendar endTime = (Calendar) selectedTime.clone();
|
Calendar endTime = (Calendar) selectedTime.clone();
|
||||||
@@ -741,7 +740,8 @@ public class WeekView extends View {
|
|||||||
canvas.drawRect(0, mHeaderHeight + mHeaderRowPadding * 2, mHeaderColumnWidth, getHeight(), mHeaderColumnBackgroundPaint);
|
canvas.drawRect(0, mHeaderHeight + mHeaderRowPadding * 2, mHeaderColumnWidth, getHeight(), mHeaderColumnBackgroundPaint);
|
||||||
|
|
||||||
// Clip to paint in left column only.
|
// Clip to paint in left column only.
|
||||||
canvas.clipRect(0, mHeaderHeight + mHeaderRowPadding * 2, mHeaderColumnWidth, getHeight(), Region.Op.REPLACE);
|
canvas.save();
|
||||||
|
canvas.clipRect(0, mHeaderHeight + mHeaderRowPadding * 2, mHeaderColumnWidth, getHeight());
|
||||||
|
|
||||||
for (int i = 0; i < getNumberOfPeriods(); i++) {
|
for (int i = 0; i < getNumberOfPeriods(); i++) {
|
||||||
// If we are showing half hours (eg. 5:30am), space the times out by half the hour height
|
// If we are showing half hours (eg. 5:30am), space the times out by half the hour height
|
||||||
@@ -768,6 +768,7 @@ public class WeekView extends View {
|
|||||||
if (top < getHeight())
|
if (top < getHeight())
|
||||||
canvas.drawText(time, mTimeTextWidth + mHeaderColumnPadding, top + mTimeTextHeight, mTimeTextPaint);
|
canvas.drawText(time, mTimeTextWidth + mHeaderColumnPadding, top + mTimeTextHeight, mTimeTextPaint);
|
||||||
}
|
}
|
||||||
|
canvas.restore();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void drawHeaderRowAndEvents(Canvas canvas) {
|
private void drawHeaderRowAndEvents(Canvas canvas) {
|
||||||
@@ -852,7 +853,8 @@ public class WeekView extends View {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Clip to paint events only.
|
// Clip to paint events only.
|
||||||
canvas.clipRect(mHeaderColumnWidth, mHeaderHeight + mHeaderRowPadding * 2 + mHeaderMarginBottom + mTimeTextHeight / 2, getWidth(), getHeight(), Region.Op.REPLACE);
|
canvas.save();
|
||||||
|
canvas.clipRect(mHeaderColumnWidth, mHeaderHeight + mHeaderRowPadding * 2 + mHeaderMarginBottom + mTimeTextHeight / 2, getWidth(), getHeight());
|
||||||
|
|
||||||
// Iterate through each day.
|
// Iterate through each day.
|
||||||
Calendar oldFirstVisibleDay = mFirstVisibleDay;
|
Calendar oldFirstVisibleDay = mFirstVisibleDay;
|
||||||
@@ -954,13 +956,17 @@ public class WeekView extends View {
|
|||||||
// In the next iteration, start from the next day.
|
// In the next iteration, start from the next day.
|
||||||
startPixel += mWidthPerDay + mColumnGap;
|
startPixel += mWidthPerDay + mColumnGap;
|
||||||
}
|
}
|
||||||
|
canvas.restore();
|
||||||
|
|
||||||
// Hide everything in the first cell (top left corner).
|
// Hide everything in the first cell (top left corner).
|
||||||
canvas.clipRect(0, 0, mTimeTextWidth + mHeaderColumnPadding * 2, mHeaderHeight + mHeaderRowPadding * 2, Region.Op.REPLACE);
|
canvas.save();
|
||||||
|
canvas.clipRect(0, 0, mTimeTextWidth + mHeaderColumnPadding * 2, mHeaderHeight + mHeaderRowPadding * 2);
|
||||||
canvas.drawRect(0, 0, mTimeTextWidth + mHeaderColumnPadding * 2, mHeaderHeight + mHeaderRowPadding * 2, mHeaderBackgroundPaint);
|
canvas.drawRect(0, 0, mTimeTextWidth + mHeaderColumnPadding * 2, mHeaderHeight + mHeaderRowPadding * 2, mHeaderBackgroundPaint);
|
||||||
|
canvas.restore();
|
||||||
|
|
||||||
// Clip to paint header row only.
|
// Clip to paint header row only.
|
||||||
canvas.clipRect(mHeaderColumnWidth, 0, getWidth(), mHeaderHeight + mHeaderRowPadding * 2, Region.Op.REPLACE);
|
canvas.save();
|
||||||
|
canvas.clipRect(mHeaderColumnWidth, 0, getWidth(), mHeaderHeight + mHeaderRowPadding * 2);
|
||||||
|
|
||||||
// Draw the header background.
|
// Draw the header background.
|
||||||
canvas.drawRect(0, 0, getWidth(), mHeaderHeight + mHeaderRowPadding * 2, mHeaderBackgroundPaint);
|
canvas.drawRect(0, 0, getWidth(), mHeaderHeight + mHeaderRowPadding * 2, mHeaderBackgroundPaint);
|
||||||
@@ -981,10 +987,11 @@ public class WeekView extends View {
|
|||||||
String dayLabel = getDateTimeInterpreter().interpretDate(day);
|
String dayLabel = getDateTimeInterpreter().interpretDate(day);
|
||||||
if (dayLabel == null)
|
if (dayLabel == null)
|
||||||
throw new IllegalStateException("A DateTimeInterpreter must not return null date");
|
throw new IllegalStateException("A DateTimeInterpreter must not return null date");
|
||||||
canvas.drawText(dayLabel, startPixel + mWidthPerDay / 2, mHeaderTextHeight + mHeaderRowPadding, isToday ? mTodayHeaderTextPaint : mHeaderTextPaint);
|
canvas.drawText(dayLabel, startPixel + mWidthPerDay / 2, mHeaderTextHeight + mHeaderRowPadding+ UtilsUi.convertPixelsToDp(40, mContext), isToday ? mTodayHeaderTextPaint : mHeaderTextPaint);
|
||||||
drawAllDayEvents(day, startPixel, canvas);
|
drawAllDayEvents(day, startPixel, canvas);
|
||||||
startPixel += mWidthPerDay + mColumnGap;
|
startPixel += mWidthPerDay + mColumnGap;
|
||||||
}
|
}
|
||||||
|
canvas.restore();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1253,8 +1260,8 @@ public class WeekView extends View {
|
|||||||
*/
|
*/
|
||||||
private class EventRect {
|
private class EventRect {
|
||||||
public WeekViewEvent event;
|
public WeekViewEvent event;
|
||||||
public WeekViewEvent originalEvent;
|
WeekViewEvent originalEvent;
|
||||||
public RectF rectF;
|
RectF rectF;
|
||||||
public float left;
|
public float left;
|
||||||
public float width;
|
public float width;
|
||||||
public float top;
|
public float top;
|
||||||
@@ -1645,7 +1652,7 @@ public class WeekView extends View {
|
|||||||
@Override
|
@Override
|
||||||
public String interpretDate(Calendar date) {
|
public String interpretDate(Calendar date) {
|
||||||
try {
|
try {
|
||||||
return DateUtils.getModifiedDate(getContext(), date.getTimeInMillis(), "EEE dd.M").toUpperCase();
|
return UtilsDate.getModifiedDate(getContext(), date.getTimeInMillis(), "EEE dd.M").toUpperCase();
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
return "";
|
return "";
|
||||||
@@ -2414,7 +2421,7 @@ public class WeekView extends View {
|
|||||||
/**
|
/**
|
||||||
* Set the scroll duration
|
* Set the scroll duration
|
||||||
*
|
*
|
||||||
* @param scrollDuration the new scrollDuraction
|
* @param scrollDuration the new scrollDuration
|
||||||
*/
|
*/
|
||||||
public void setScrollDuration(int scrollDuration) {
|
public void setScrollDuration(int scrollDuration) {
|
||||||
mScrollDuration = scrollDuration;
|
mScrollDuration = scrollDuration;
|
||||||
@@ -2518,7 +2525,7 @@ public class WeekView extends View {
|
|||||||
// Check after call of mGestureDetector, so mCurrentFlingDirection and mCurrentScrollDirection are set.
|
// Check after call of mGestureDetector, so mCurrentFlingDirection and mCurrentScrollDirection are set.
|
||||||
if (event.getAction() == MotionEvent.ACTION_UP && !mIsZooming && mCurrentFlingDirection == Direction.NONE) {
|
if (event.getAction() == MotionEvent.ACTION_UP && !mIsZooming && mCurrentFlingDirection == Direction.NONE) {
|
||||||
if (mCurrentScrollDirection == Direction.RIGHT || mCurrentScrollDirection == Direction.LEFT) {
|
if (mCurrentScrollDirection == Direction.RIGHT || mCurrentScrollDirection == Direction.LEFT) {
|
||||||
goToNearestOrigin();
|
performClick();
|
||||||
}
|
}
|
||||||
mCurrentScrollDirection = Direction.NONE;
|
mCurrentScrollDirection = Direction.NONE;
|
||||||
}
|
}
|
||||||
@@ -2526,6 +2533,13 @@ public class WeekView extends View {
|
|||||||
return val;
|
return val;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean performClick() {
|
||||||
|
super.performClick();
|
||||||
|
goToNearestOrigin();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
private void goToNearestOrigin() {
|
private void goToNearestOrigin() {
|
||||||
double leftDays = mCurrentOrigin.x / (mWidthPerDay + mColumnGap);
|
double leftDays = mCurrentOrigin.x / (mWidthPerDay + mColumnGap);
|
||||||
|
|
||||||
|
|||||||
@@ -76,7 +76,7 @@ public class WeekViewEvent {
|
|||||||
* @param allDay Is the event an all day event.
|
* @param allDay Is the event an all day event.
|
||||||
* @param shader the Shader of the event rectangle
|
* @param shader the Shader of the event rectangle
|
||||||
*/
|
*/
|
||||||
public WeekViewEvent(String id, String name, String location, Calendar startTime, Calendar endTime, boolean allDay, Shader shader) {
|
private WeekViewEvent(String id, String name, String location, Calendar startTime, Calendar endTime, boolean allDay, Shader shader) {
|
||||||
this.mId = id;
|
this.mId = id;
|
||||||
this.mName = name;
|
this.mName = name;
|
||||||
this.mLocation = location;
|
this.mLocation = location;
|
||||||
@@ -96,7 +96,7 @@ public class WeekViewEvent {
|
|||||||
* @param endTime The time when the event ends.
|
* @param endTime The time when the event ends.
|
||||||
* @param allDay Is the event an all day event
|
* @param allDay Is the event an all day event
|
||||||
*/
|
*/
|
||||||
public WeekViewEvent(String id, String name, String location, Calendar startTime, Calendar endTime, boolean allDay) {
|
private WeekViewEvent(String id, String name, String location, Calendar startTime, Calendar endTime, boolean allDay) {
|
||||||
this(id, name, location, startTime, endTime, allDay, null);
|
this(id, name, location, startTime, endTime, allDay, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import java.util.Calendar;
|
|||||||
/**
|
/**
|
||||||
* Created by jesse on 6/02/2016.
|
* Created by jesse on 6/02/2016.
|
||||||
*/
|
*/
|
||||||
public class WeekViewUtil {
|
class WeekViewUtil {
|
||||||
|
|
||||||
|
|
||||||
/////////////////////////////////////////////////////////////////
|
/////////////////////////////////////////////////////////////////
|
||||||
|
|||||||
BIN
app/src/main/res/drawable-hdpi/ic_folder_light_blue_700_24dp.png
Normal file
|
After Width: | Height: | Size: 168 B |
|
After Width: | Height: | Size: 192 B |
|
After Width: | Height: | Size: 156 B |
BIN
app/src/main/res/drawable-mdpi/ic_folder_light_blue_700_24dp.png
Normal file
|
After Width: | Height: | Size: 142 B |
|
After Width: | Height: | Size: 155 B |
|
After Width: | Height: | Size: 128 B |
|
After Width: | Height: | Size: 238 B |