diff --git a/app/build.gradle b/app/build.gradle
index 0a6bebcf..acfabf96 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -71,6 +71,7 @@ android {
main {
res.srcDirs =
[
+ 'src/main/res/layouts/courses',
'src/main/res/layouts/homework',
'src/main/res/layouts/files',
'src/main/res/layouts/main',
@@ -145,6 +146,10 @@ dependencies {
compile "com.jakewharton:butterknife:$BUTTERKNIFE_VERSION"
apt "com.jakewharton:butterknife-compiler:$BUTTERKNIFE_VERSION"
+ // Picasso
+ compile 'com.squareup.picasso:picasso:2.5.2'
+ compile 'com.jakewharton.picasso:picasso2-okhttp3-downloader:1.1.0'
+
compile "com.google.dagger:dagger:$DAGGER_VERSION"
//Required by Dagger2
apt daggerCompiler
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 6a3a2411..72218462 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -26,6 +26,31 @@
android:label="@string/app_name">
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
> getSubmissions() {
public Submission getSubmissionForId(String homeworkId) {
return mDatabaseHelper.getSubmissionForId(homeworkId);
}
+
+ /**** Courses ****/
+
+ public Observable syncCourses() {
+ return mRestService.getCourses(getAccessToken())
+ .concatMap(new Func1, Observable>() {
+ @Override
+ public Observable call(FeathersResponse courses) {
+ mDatabaseHelper.clearTable(Course.class);
+ return mDatabaseHelper.setCourses(courses.data);
+ }
+ })
+ .doOnError(Throwable::printStackTrace);
+ }
+
+ public Observable> getCourses() {
+ return mDatabaseHelper.getCourses().distinct();
+ }
+
+ public Course getCourseForId(String courseId) {
+ return mDatabaseHelper.getCourseForId(courseId);
+ }
+
+ /**** Topics ****/
+
+ public Observable syncTopics(String courseId) {
+ return mRestService.getTopics(getAccessToken(), courseId)
+ .concatMap(new Func1, Observable>() {
+ @Override
+ public Observable call(FeathersResponse topics) {
+ mDatabaseHelper.clearTable(Topic.class);
+ return mDatabaseHelper.setTopics(topics.data);
+ }
+ })
+ .doOnError(Throwable::printStackTrace);
+ }
+
+ public Observable> getTopics() {
+ return mDatabaseHelper.getTopics().distinct();
+ }
+
+ public List getContents(String topicId) {
+ return mDatabaseHelper.getContents(topicId).contents;
+ }
}
diff --git a/app/src/main/java/org/schulcloud/mobile/data/local/DatabaseHelper.java b/app/src/main/java/org/schulcloud/mobile/data/local/DatabaseHelper.java
index 23c9ec46..a1a3e676 100644
--- a/app/src/main/java/org/schulcloud/mobile/data/local/DatabaseHelper.java
+++ b/app/src/main/java/org/schulcloud/mobile/data/local/DatabaseHelper.java
@@ -1,6 +1,7 @@
package org.schulcloud.mobile.data.local;
import org.schulcloud.mobile.data.model.AccessToken;
+import org.schulcloud.mobile.data.model.Course;
import org.schulcloud.mobile.data.model.CurrentUser;
import org.schulcloud.mobile.data.model.Device;
import org.schulcloud.mobile.data.model.Directory;
@@ -8,6 +9,7 @@
import org.schulcloud.mobile.data.model.File;
import org.schulcloud.mobile.data.model.Homework;
import org.schulcloud.mobile.data.model.Submission;
+import org.schulcloud.mobile.data.model.Topic;
import org.schulcloud.mobile.data.model.User;
import java.util.Collection;
@@ -291,4 +293,70 @@ public Submission getSubmissionForId(String homeworkId) {
final Realm realm = mRealmProvider.get();
return realm.where(Submission.class).equalTo("homeworkId", homeworkId).findFirst();
}
+
+ /**** Courses ****/
+
+ public Observable setCourses(final Collection newCourse) {
+ return Observable.create(subscriber -> {
+ if (subscriber.isUnsubscribed()) return;
+ Realm realm = null;
+
+ try {
+ realm = mRealmProvider.get();
+ realm.executeTransaction(realm1 -> realm1.copyToRealmOrUpdate(newCourse));
+ } catch (Exception e) {
+ Timber.e(e, "There was an error while adding in Realm.");
+ subscriber.onError(e);
+ } finally {
+ if (realm != null) {
+ realm.close();
+ }
+ }
+ });
+ }
+
+ public Observable> getCourses() {
+ final Realm realm = mRealmProvider.get();
+ return realm.where(Course.class).findAllAsync().asObservable()
+ .filter(course -> course.isLoaded())
+ .map(course -> realm.copyFromRealm(course));
+ }
+
+ public Course getCourseForId(String courseId) {
+ final Realm realm = mRealmProvider.get();
+ return realm.where(Course.class).equalTo("_id", courseId).findFirst();
+ }
+
+ /**** Topics ****/
+
+ public Observable setTopics(final Collection newTopic) {
+ return Observable.create(subscriber -> {
+ if (subscriber.isUnsubscribed()) return;
+ Realm realm = null;
+
+ try {
+ realm = mRealmProvider.get();
+ realm.executeTransaction(realm1 -> realm1.copyToRealmOrUpdate(newTopic));
+ } catch (Exception e) {
+ Timber.e(e, "There was an error while adding in Realm.");
+ subscriber.onError(e);
+ } finally {
+ if (realm != null) {
+ realm.close();
+ }
+ }
+ });
+ }
+
+ public Observable> getTopics() {
+ final Realm realm = mRealmProvider.get();
+ return realm.where(Topic.class).findAllAsync().asObservable()
+ .filter(topic -> topic.isLoaded())
+ .map(topic -> realm.copyFromRealm(topic));
+ }
+
+ public Topic getContents(String topicId) {
+ final Realm realm = mRealmProvider.get();
+ return realm.where(Topic.class).equalTo("_id", topicId).findFirst();
+ }
}
diff --git a/app/src/main/java/org/schulcloud/mobile/data/model/Content.java b/app/src/main/java/org/schulcloud/mobile/data/model/Content.java
new file mode 100644
index 00000000..b2fef14e
--- /dev/null
+++ b/app/src/main/java/org/schulcloud/mobile/data/model/Content.java
@@ -0,0 +1,8 @@
+package org.schulcloud.mobile.data.model;
+
+import io.realm.RealmObject;
+
+public class Content extends RealmObject{
+ public String text;
+ public String materialId;
+}
diff --git a/app/src/main/java/org/schulcloud/mobile/data/model/Contents.java b/app/src/main/java/org/schulcloud/mobile/data/model/Contents.java
new file mode 100644
index 00000000..cf2f6ad8
--- /dev/null
+++ b/app/src/main/java/org/schulcloud/mobile/data/model/Contents.java
@@ -0,0 +1,10 @@
+package org.schulcloud.mobile.data.model;
+
+import io.realm.RealmObject;
+
+public class Contents extends RealmObject {
+ public String component;
+ public String title;
+ public Boolean hidden;
+ public Content content;
+}
\ No newline at end of file
diff --git a/app/src/main/java/org/schulcloud/mobile/data/model/Course.java b/app/src/main/java/org/schulcloud/mobile/data/model/Course.java
index 9cc20215..b382d265 100644
--- a/app/src/main/java/org/schulcloud/mobile/data/model/Course.java
+++ b/app/src/main/java/org/schulcloud/mobile/data/model/Course.java
@@ -1,5 +1,6 @@
package org.schulcloud.mobile.data.model;
+import io.realm.RealmList;
import io.realm.RealmModel;
import io.realm.annotations.PrimaryKey;
import io.realm.annotations.RealmClass;
@@ -12,4 +13,11 @@ public class Course implements RealmModel {
public String name;
public String description;
public String color;
+ public RealmList times;
+ public String startDate;
+ public String untilDate;
+ public Boolean gradeSystem;
+ public RealmList substitutionIds;
+ public RealmList teacherIds;
+ public RealmList userIds;
}
diff --git a/app/src/main/java/org/schulcloud/mobile/data/model/Times.java b/app/src/main/java/org/schulcloud/mobile/data/model/Times.java
new file mode 100644
index 00000000..fbd923ba
--- /dev/null
+++ b/app/src/main/java/org/schulcloud/mobile/data/model/Times.java
@@ -0,0 +1,16 @@
+package org.schulcloud.mobile.data.model;
+
+
+import io.realm.RealmModel;
+import io.realm.annotations.PrimaryKey;
+import io.realm.annotations.RealmClass;
+
+@RealmClass
+public class Times implements RealmModel {
+ @PrimaryKey
+ public Integer weekday;
+ public Integer startTime;
+ public Integer duration;
+ public String eventId;
+ public String room;
+}
diff --git a/app/src/main/java/org/schulcloud/mobile/data/model/Topic.java b/app/src/main/java/org/schulcloud/mobile/data/model/Topic.java
new file mode 100644
index 00000000..13f11e7d
--- /dev/null
+++ b/app/src/main/java/org/schulcloud/mobile/data/model/Topic.java
@@ -0,0 +1,15 @@
+package org.schulcloud.mobile.data.model;
+
+import io.realm.RealmList;
+import io.realm.RealmObject;
+
+public class Topic extends RealmObject {
+ public String _id;
+ public String name;
+ public String description;
+ public String date;
+ public String time;
+ public String courseId;
+ public Boolean hidden;
+ public RealmList contents;
+}
diff --git a/app/src/main/java/org/schulcloud/mobile/data/model/responseBodies/FeathersResponse.java b/app/src/main/java/org/schulcloud/mobile/data/model/responseBodies/FeathersResponse.java
new file mode 100644
index 00000000..1dc3d6d6
--- /dev/null
+++ b/app/src/main/java/org/schulcloud/mobile/data/model/responseBodies/FeathersResponse.java
@@ -0,0 +1,7 @@
+package org.schulcloud.mobile.data.model.responseBodies;
+
+import java.util.List;
+
+public class FeathersResponse {
+ public List data;
+}
diff --git a/app/src/main/java/org/schulcloud/mobile/data/remote/RestService.java b/app/src/main/java/org/schulcloud/mobile/data/remote/RestService.java
index 178df1b1..802f3a73 100644
--- a/app/src/main/java/org/schulcloud/mobile/data/remote/RestService.java
+++ b/app/src/main/java/org/schulcloud/mobile/data/remote/RestService.java
@@ -1,17 +1,20 @@
package org.schulcloud.mobile.data.remote;
import org.schulcloud.mobile.data.model.AccessToken;
+import org.schulcloud.mobile.data.model.Course;
import org.schulcloud.mobile.data.model.CurrentUser;
import org.schulcloud.mobile.data.model.Device;
import org.schulcloud.mobile.data.model.Event;
import org.schulcloud.mobile.data.model.Homework;
import org.schulcloud.mobile.data.model.Submission;
+import org.schulcloud.mobile.data.model.Topic;
import org.schulcloud.mobile.data.model.User;
import org.schulcloud.mobile.data.model.requestBodies.CallbackRequest;
import org.schulcloud.mobile.data.model.requestBodies.Credentials;
import org.schulcloud.mobile.data.model.requestBodies.DeviceRequest;
import org.schulcloud.mobile.data.model.requestBodies.SignedUrlRequest;
import org.schulcloud.mobile.data.model.responseBodies.DeviceResponse;
+import org.schulcloud.mobile.data.model.responseBodies.FeathersResponse;
import org.schulcloud.mobile.data.model.responseBodies.FilesResponse;
import org.schulcloud.mobile.data.model.responseBodies.SignedUrlResponse;
@@ -88,4 +91,10 @@ Observable uploadFile(
@GET("submissions?$limit=-1&$populate=comments")
Observable> getSubmissions(@Header("Authorization") String accessToken);
+
+ @GET("courses?$populate[0]=teacherIds&$populate[1]=userIds&$populate[2]=substitutionIds")
+ Observable> getCourses(@Header("Authorization") String accessToken);
+
+ @GET("lessons")
+ Observable> getTopics(@Header("Authorization") String accessToken, @Query("courseId") String courseId);
}
diff --git a/app/src/main/java/org/schulcloud/mobile/data/sync/CourseSyncService.java b/app/src/main/java/org/schulcloud/mobile/data/sync/CourseSyncService.java
new file mode 100644
index 00000000..14c1105e
--- /dev/null
+++ b/app/src/main/java/org/schulcloud/mobile/data/sync/CourseSyncService.java
@@ -0,0 +1,102 @@
+package org.schulcloud.mobile.data.sync;
+
+import android.app.Service;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.net.ConnectivityManager;
+import android.os.IBinder;
+
+import org.schulcloud.mobile.SchulCloudApplication;
+import org.schulcloud.mobile.data.DataManager;
+import org.schulcloud.mobile.data.model.Course;
+import org.schulcloud.mobile.util.AndroidComponentUtil;
+import org.schulcloud.mobile.util.NetworkUtil;
+
+import javax.inject.Inject;
+
+import rx.Observer;
+import rx.Subscription;
+import rx.schedulers.Schedulers;
+import timber.log.Timber;
+
+public class CourseSyncService extends Service {
+
+ @Inject
+ DataManager mDataManager;
+ private Subscription mSubscription;
+
+ public static Intent getStartIntent(Context context) {
+ return new Intent(context, CourseSyncService.class);
+ }
+
+ public static boolean isRunning(Context context) {
+ return AndroidComponentUtil.isServiceRunning(context, CourseSyncService.class);
+ }
+
+ @Override
+ public void onCreate() {
+ super.onCreate();
+ SchulCloudApplication.get(this).getComponent().inject(this);
+ }
+
+ @Override
+ public int onStartCommand(Intent intent, int flags, final int startId) {
+ Timber.i("Starting course sync...");
+
+ if (!NetworkUtil.isNetworkConnected(this)) {
+ Timber.i("Sync canceled, connection not available");
+ AndroidComponentUtil.toggleComponent(this, SyncOnConnectionAvailable.class, true);
+ stopSelf(startId);
+ return START_NOT_STICKY;
+ }
+
+ if (mSubscription != null && !mSubscription.isUnsubscribed()) mSubscription.unsubscribe();
+ mSubscription = mDataManager.syncCourses()
+ .subscribeOn(Schedulers.io())
+ .subscribe(new Observer() {
+ @Override
+ public void onCompleted() {
+ Timber.i("Synced successfully!");
+ stopSelf(startId);
+ }
+
+ @Override
+ public void onError(Throwable e) {
+ Timber.w(e, "Error syncing.");
+ stopSelf(startId);
+ }
+
+ @Override
+ public void onNext(Course course) {
+ }
+ });
+
+ return START_STICKY;
+ }
+
+ @Override
+ public void onDestroy() {
+ if (mSubscription != null) mSubscription.unsubscribe();
+ super.onDestroy();
+ }
+
+ @Override
+ public IBinder onBind(Intent intent) {
+ return null;
+ }
+
+ public static class SyncOnConnectionAvailable extends BroadcastReceiver {
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (intent.getAction().equals(ConnectivityManager.CONNECTIVITY_ACTION)
+ && NetworkUtil.isNetworkConnected(context)) {
+ Timber.i("Connection is now available, triggering sync...");
+ AndroidComponentUtil.toggleComponent(context, this.getClass(), false);
+ context.startService(getStartIntent(context));
+ }
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/org/schulcloud/mobile/data/sync/TopicSyncService.java b/app/src/main/java/org/schulcloud/mobile/data/sync/TopicSyncService.java
new file mode 100644
index 00000000..2f586a20
--- /dev/null
+++ b/app/src/main/java/org/schulcloud/mobile/data/sync/TopicSyncService.java
@@ -0,0 +1,111 @@
+package org.schulcloud.mobile.data.sync;
+
+import android.app.Service;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.net.ConnectivityManager;
+import android.os.Bundle;
+import android.os.IBinder;
+
+import org.schulcloud.mobile.SchulCloudApplication;
+import org.schulcloud.mobile.data.DataManager;
+import org.schulcloud.mobile.data.model.Topic;
+import org.schulcloud.mobile.util.AndroidComponentUtil;
+import org.schulcloud.mobile.util.NetworkUtil;
+
+import javax.inject.Inject;
+
+import rx.Observer;
+import rx.Subscription;
+import rx.schedulers.Schedulers;
+import timber.log.Timber;
+
+public class TopicSyncService extends Service {
+
+ @Inject
+ DataManager mDataManager;
+ private Subscription mSubscription;
+
+ private String courseId = null;
+
+ public static Intent getStartIntent(Context context) {
+ return new Intent(context, TopicSyncService.class);
+ }
+
+ public static boolean isRunning(Context context) {
+ return AndroidComponentUtil.isServiceRunning(context, TopicSyncService.class);
+ }
+
+ @Override
+ public void onCreate() {
+ super.onCreate();
+ SchulCloudApplication.get(this).getComponent().inject(this);
+ }
+
+ @Override
+ public int onStartCommand(Intent intent, int flags, final int startId) {
+ Timber.i("Starting topic sync...");
+
+ if (!NetworkUtil.isNetworkConnected(this)) {
+ Timber.i("Sync canceled, connection not available");
+ AndroidComponentUtil.toggleComponent(this, SyncOnConnectionAvailable.class, true);
+ stopSelf(startId);
+ return START_NOT_STICKY;
+ }
+
+ Bundle extras = intent.getExtras();
+ if (extras != null) {
+ courseId = extras.getString("courseId");
+ //The key argument here must match that used in the other activity
+ }
+
+ if (mSubscription != null && !mSubscription.isUnsubscribed()) mSubscription.unsubscribe();
+ mSubscription = mDataManager.syncTopics(courseId)
+ .subscribeOn(Schedulers.io())
+ .subscribe(new Observer() {
+ @Override
+ public void onCompleted() {
+ Timber.i("Synced successfully!");
+ stopSelf(startId);
+ }
+
+ @Override
+ public void onError(Throwable e) {
+ Timber.w(e, "Error syncing.");
+ stopSelf(startId);
+ }
+
+ @Override
+ public void onNext(Topic topic) {
+ }
+ });
+
+ return START_STICKY;
+ }
+
+ @Override
+ public void onDestroy() {
+ if (mSubscription != null) mSubscription.unsubscribe();
+ super.onDestroy();
+ }
+
+ @Override
+ public IBinder onBind(Intent intent) {
+ return null;
+ }
+
+ public static class SyncOnConnectionAvailable extends BroadcastReceiver {
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (intent.getAction().equals(ConnectivityManager.CONNECTIVITY_ACTION)
+ && NetworkUtil.isNetworkConnected(context)) {
+ Timber.i("Connection is now available, triggering sync...");
+ AndroidComponentUtil.toggleComponent(context, this.getClass(), false);
+ context.startService(getStartIntent(context));
+ }
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/org/schulcloud/mobile/injection/component/ActivityComponent.java b/app/src/main/java/org/schulcloud/mobile/injection/component/ActivityComponent.java
index 76379624..66d35e1d 100644
--- a/app/src/main/java/org/schulcloud/mobile/injection/component/ActivityComponent.java
+++ b/app/src/main/java/org/schulcloud/mobile/injection/component/ActivityComponent.java
@@ -4,6 +4,9 @@
import org.schulcloud.mobile.injection.scope.PerActivity;
import org.schulcloud.mobile.ui.base.BaseActivity;
import org.schulcloud.mobile.ui.base.BaseFragment;
+import org.schulcloud.mobile.ui.courses.CourseActivity;
+import org.schulcloud.mobile.ui.courses.detailed.DetailedCourseFragment;
+import org.schulcloud.mobile.ui.courses.detailed.TopicFragment;
import org.schulcloud.mobile.ui.homework.HomeworkActivity;
import org.schulcloud.mobile.ui.files.FileActivity;
import org.schulcloud.mobile.ui.homework.detailed.DetailedHomeworkFragment;
@@ -35,4 +38,10 @@ public interface ActivityComponent {
void inject(HomeworkActivity homeworkActivity);
void inject(DetailedHomeworkFragment detailedHomeworkFragment);
+
+ void inject(CourseActivity courseActivity);
+
+ void inject (DetailedCourseFragment detailedCourseFragment);
+
+ void inject(TopicFragment topicFragment);
}
diff --git a/app/src/main/java/org/schulcloud/mobile/injection/component/ApplicationComponent.java b/app/src/main/java/org/schulcloud/mobile/injection/component/ApplicationComponent.java
index c8352925..fc1b2a06 100644
--- a/app/src/main/java/org/schulcloud/mobile/injection/component/ApplicationComponent.java
+++ b/app/src/main/java/org/schulcloud/mobile/injection/component/ApplicationComponent.java
@@ -7,12 +7,14 @@
import org.schulcloud.mobile.data.local.DatabaseHelper;
import org.schulcloud.mobile.data.local.PreferencesHelper;
import org.schulcloud.mobile.data.remote.RestService;
+import org.schulcloud.mobile.data.sync.CourseSyncService;
import org.schulcloud.mobile.data.sync.DeviceSyncService;
import org.schulcloud.mobile.data.sync.DirectorySyncService;
import org.schulcloud.mobile.data.sync.EventSyncService;
import org.schulcloud.mobile.data.sync.FileSyncService;
import org.schulcloud.mobile.data.sync.HomeworkSyncService;
import org.schulcloud.mobile.data.sync.SubmissionSyncService;
+import org.schulcloud.mobile.data.sync.TopicSyncService;
import org.schulcloud.mobile.data.sync.UserSyncService;
import org.schulcloud.mobile.injection.ApplicationContext;
import org.schulcloud.mobile.injection.module.ApplicationModule;
@@ -45,6 +47,10 @@ public interface ApplicationComponent {
void inject(SubmissionSyncService submissionSyncService);
+ void inject(CourseSyncService courseSyncService);
+
+ void inject(TopicSyncService topicSyncService);
+
@ApplicationContext
Context context();
diff --git a/app/src/main/java/org/schulcloud/mobile/ui/base/BaseActivity.java b/app/src/main/java/org/schulcloud/mobile/ui/base/BaseActivity.java
index e75bbd9a..ef1b5a6e 100644
--- a/app/src/main/java/org/schulcloud/mobile/ui/base/BaseActivity.java
+++ b/app/src/main/java/org/schulcloud/mobile/ui/base/BaseActivity.java
@@ -23,6 +23,7 @@
import org.schulcloud.mobile.injection.component.ConfigPersistentComponent;
import org.schulcloud.mobile.injection.component.DaggerConfigPersistentComponent;
import org.schulcloud.mobile.injection.module.ActivityModule;
+import org.schulcloud.mobile.ui.courses.CourseActivity;
import org.schulcloud.mobile.ui.files.FileActivity;
import org.schulcloud.mobile.ui.homework.HomeworkActivity;
import org.schulcloud.mobile.ui.settings.SettingsActivity;
@@ -47,6 +48,7 @@ public class BaseActivity extends AppCompatActivity {
// Curently just nonsense Data and Logos, change here for the actual list
private String[] layers = {
"Meine Dateien",
+ "Meine Fächer",
"Hausaufgaben",
"Kontakt",
"Einstellungen",
@@ -55,6 +57,7 @@ public class BaseActivity extends AppCompatActivity {
};
private String[] resources = {
FontAwesome.FA_FILE,
+ FontAwesome.FA_GRADUATION_CAP,
FontAwesome.FA_TASKS,
FontAwesome.FA_CONTAO,
FontAwesome.FA_COGS,
@@ -140,9 +143,12 @@ private void openActivityForPos(int pos) {
c = FileActivity.class;
break;
case 2: // homework
+ c = CourseActivity.class;
+ break;
+ case 3: // homework
c = HomeworkActivity.class;
break;
- case 3: // contact
+ case 4: // contact
Intent mailIntent = new Intent(Intent.ACTION_VIEW);
Uri data = Uri.parse("mailto:" +
getResources().getString(R.string.mail_to_mail) +
@@ -151,15 +157,15 @@ private void openActivityForPos(int pos) {
mailIntent.setData(data);
startActivity(mailIntent);
return;
- case 4: // settings
+ case 5: // settings
c = SettingsActivity.class;
break;
- case 5: // impressum
+ case 6: // impressum
c = BaseActivity.class;
Intent browserIntent = new Intent(Intent.ACTION_VIEW, Uri.parse("https://hpi.de/impressum.html"));
startActivity(browserIntent);
return;
- case 6: // logout
+ case 7: // logout
// delete accessToken and currentUser
mPreferencesHelper.clear("jwt");
mPreferencesHelper.clear("currentUser");
diff --git a/app/src/main/java/org/schulcloud/mobile/ui/courses/CourseActivity.java b/app/src/main/java/org/schulcloud/mobile/ui/courses/CourseActivity.java
new file mode 100644
index 00000000..fffa278e
--- /dev/null
+++ b/app/src/main/java/org/schulcloud/mobile/ui/courses/CourseActivity.java
@@ -0,0 +1,124 @@
+package org.schulcloud.mobile.ui.courses;
+
+import android.app.FragmentManager;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+import android.support.v7.widget.LinearLayoutManager;
+import android.support.v7.widget.RecyclerView;
+import android.view.LayoutInflater;
+import android.view.View;
+
+import org.schulcloud.mobile.R;
+import org.schulcloud.mobile.data.model.Course;
+import org.schulcloud.mobile.data.sync.CourseSyncService;
+import org.schulcloud.mobile.ui.base.BaseActivity;
+import org.schulcloud.mobile.ui.courses.detailed.DetailedCourseFragment;
+import org.schulcloud.mobile.ui.signin.SignInActivity;
+import org.schulcloud.mobile.util.DialogFactory;
+
+import java.util.Collections;
+import java.util.List;
+
+import javax.inject.Inject;
+
+import butterknife.BindView;
+import butterknife.ButterKnife;
+
+public class CourseActivity extends BaseActivity implements CourseMvpView {
+
+ private static final String EXTRA_TRIGGER_SYNC_FLAG =
+ "org.schulcloud.mobile.ui.main.MainActivity.EXTRA_TRIGGER_SYNC_FLAG";
+
+ @Inject
+ CoursePresenter mCoursePresenter;
+ @Inject
+ CourseAdapter mCourseAdapter;
+
+ @BindView(R.id.recycler_view)
+ RecyclerView mRecyclerView;
+
+ /**
+ * Return an Intent to start this Activity.
+ * triggerDataSyncOnCreate allows disabling the background sync service onCreate. Should
+ * only be set to false during testing.
+ */
+ public static Intent getStartIntent(Context context, boolean triggerDataSyncOnCreate) {
+ Intent intent = new Intent(context, CourseActivity.class);
+ intent.putExtra(EXTRA_TRIGGER_SYNC_FLAG, triggerDataSyncOnCreate);
+ return intent;
+ }
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ activityComponent().inject(this);
+ //setContentView(R.layout.activity_main);
+
+ LayoutInflater inflater =
+ (LayoutInflater) this.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+
+ //inflate your activity layout here!
+ View contentView = inflater.inflate(R.layout.activity_course, null, false);
+ mDrawer.addView(contentView, 0);
+ getSupportActionBar().setTitle(R.string.title_courses);
+ ButterKnife.bind(this);
+
+
+ mRecyclerView.setAdapter(mCourseAdapter);
+ mRecyclerView.setLayoutManager(new LinearLayoutManager(this));
+ mCoursePresenter.attachView(this);
+ mCoursePresenter.checkSignedIn(this);
+
+ mCoursePresenter.loadCourses();
+
+ if (getIntent().getBooleanExtra(EXTRA_TRIGGER_SYNC_FLAG, true)) {
+ startService(CourseSyncService.getStartIntent(this));
+ }
+ }
+
+ @Override
+ protected void onDestroy() {
+ mCoursePresenter.detachView();
+ super.onDestroy();
+ }
+
+ /***** MVP View methods implementation *****/
+
+ @Override
+ public void showCourses(List courses) {
+ mCourseAdapter.setCourses(courses);
+ mCourseAdapter.notifyDataSetChanged();
+ }
+
+ @Override
+ public void showError() {
+ DialogFactory.createGenericErrorDialog(this, "Leider gab es ein Problem beim fetchen der Kurse")
+ .show();
+ }
+
+ @Override
+ public void showCoursesEmpty() {
+ mCourseAdapter.setCourses(Collections.emptyList());
+ mCourseAdapter.notifyDataSetChanged();
+ }
+
+ @Override
+ public void showCourseDialog(String courseId) {
+ DetailedCourseFragment frag = new DetailedCourseFragment();
+ Bundle args = new Bundle();
+ args.putString("courseId", courseId);
+ frag.setArguments(args);
+ FragmentManager fragmentManager = getFragmentManager();
+ fragmentManager.beginTransaction()
+ .replace(R.id.overlay_fragment_container, frag)
+ .addToBackStack(null)
+ .commit();
+ }
+
+ @Override
+ public void goToSignIn() {
+ Intent intent = new Intent(this, SignInActivity.class);
+ this.startActivity(intent);
+ }
+}
diff --git a/app/src/main/java/org/schulcloud/mobile/ui/courses/CourseAdapter.java b/app/src/main/java/org/schulcloud/mobile/ui/courses/CourseAdapter.java
new file mode 100644
index 00000000..f38dc41c
--- /dev/null
+++ b/app/src/main/java/org/schulcloud/mobile/ui/courses/CourseAdapter.java
@@ -0,0 +1,80 @@
+package org.schulcloud.mobile.ui.courses;
+
+import android.graphics.Color;
+import android.support.v7.widget.CardView;
+import android.support.v7.widget.RecyclerView;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.TextView;
+
+import com.beardedhen.androidbootstrap.AwesomeTextView;
+
+import org.schulcloud.mobile.R;
+import org.schulcloud.mobile.data.model.Course;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.inject.Inject;
+
+import butterknife.BindView;
+import butterknife.ButterKnife;
+
+public class CourseAdapter extends RecyclerView.Adapter {
+
+ private List mCourse;
+
+ @Inject
+ CoursePresenter mCoursePresenter;
+
+ @Inject
+ public CourseAdapter() {
+ mCourse = new ArrayList<>();
+ }
+
+ public void setCourses(List course) {
+ mCourse = course;
+ }
+
+ @Override
+ public CourseViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
+ View itemView = LayoutInflater.from(parent.getContext())
+ .inflate(R.layout.item_course, parent, false);
+ return new CourseViewHolder(itemView);
+ }
+
+ @Override
+ public void onBindViewHolder(CourseViewHolder holder, int position) {
+ Course course = mCourse.get(position);
+
+ holder.nameTextView.setText(course.name);
+ holder.descriptionTextView.setText(course.description);
+
+ holder.colorView.setBackgroundColor(Color.parseColor(course.color));
+
+ holder.cardView.setOnClickListener(v -> mCoursePresenter.showCourseDetail(course._id));
+ }
+
+ @Override
+ public int getItemCount() {
+ return mCourse.size();
+ }
+
+ class CourseViewHolder extends RecyclerView.ViewHolder {
+
+ @BindView(R.id.text_name)
+ TextView nameTextView;
+ @BindView(R.id.text_description)
+ TextView descriptionTextView;
+ @BindView(R.id.view_hex_color)
+ AwesomeTextView colorView;
+ @BindView(R.id.card_view)
+ CardView cardView;
+
+ public CourseViewHolder(View itemView) {
+ super(itemView);
+ ButterKnife.bind(this, itemView);
+ }
+ }
+}
diff --git a/app/src/main/java/org/schulcloud/mobile/ui/courses/CourseMvpView.java b/app/src/main/java/org/schulcloud/mobile/ui/courses/CourseMvpView.java
new file mode 100644
index 00000000..c8aec174
--- /dev/null
+++ b/app/src/main/java/org/schulcloud/mobile/ui/courses/CourseMvpView.java
@@ -0,0 +1,18 @@
+package org.schulcloud.mobile.ui.courses;
+
+import org.schulcloud.mobile.data.model.Course;
+import org.schulcloud.mobile.ui.base.MvpView;
+
+import java.util.List;
+
+public interface CourseMvpView extends MvpView {
+
+ void showCourses(List courses);
+
+ void showCoursesEmpty();
+
+ void showCourseDialog(String courseId);
+
+ void showError();
+
+}
diff --git a/app/src/main/java/org/schulcloud/mobile/ui/courses/CoursePresenter.java b/app/src/main/java/org/schulcloud/mobile/ui/courses/CoursePresenter.java
new file mode 100644
index 00000000..db92d415
--- /dev/null
+++ b/app/src/main/java/org/schulcloud/mobile/ui/courses/CoursePresenter.java
@@ -0,0 +1,73 @@
+package org.schulcloud.mobile.ui.courses;
+
+import android.content.Context;
+
+import org.schulcloud.mobile.data.DataManager;
+import org.schulcloud.mobile.data.model.Course;
+import org.schulcloud.mobile.injection.ConfigPersistent;
+import org.schulcloud.mobile.ui.base.BasePresenter;
+import org.schulcloud.mobile.util.RxUtil;
+
+import java.util.List;
+
+import javax.inject.Inject;
+
+import rx.Subscriber;
+import rx.android.schedulers.AndroidSchedulers;
+import timber.log.Timber;
+
+@ConfigPersistent
+public class CoursePresenter extends BasePresenter {
+
+ @Inject
+ public CoursePresenter(DataManager dataManager) {
+ mDataManager = dataManager;
+ }
+
+ @Override
+ public void attachView(CourseMvpView mvpView) {
+ super.attachView(mvpView);
+ }
+
+ @Override
+ public void detachView() {
+ super.detachView();
+ if (mSubscription != null) mSubscription.unsubscribe();
+ }
+
+ public void loadCourses() {
+ checkViewAttached();
+ RxUtil.unsubscribe(mSubscription);
+ mSubscription = mDataManager.getCourses()
+ .observeOn(AndroidSchedulers.mainThread())
+ .subscribe(new Subscriber>() {
+ @Override
+ public void onCompleted() {
+ }
+
+ @Override
+ public void onError(Throwable e) {
+ Timber.e(e, "There was an error loading the users.");
+ getMvpView().showError();
+ }
+
+ @Override
+ public void onNext(List courses) {
+ if (courses.isEmpty()) {
+ getMvpView().showCoursesEmpty();
+ } else {
+ getMvpView().showCourses(courses);
+ }
+ }
+ });
+ }
+
+ public void checkSignedIn(Context context) {
+ super.isAlreadySignedIn(mDataManager, context);
+ }
+
+ public void showCourseDetail(String courseId) {
+ getMvpView().showCourseDialog(courseId);
+ }
+
+}
diff --git a/app/src/main/java/org/schulcloud/mobile/ui/courses/detailed/ContentAdapter.java b/app/src/main/java/org/schulcloud/mobile/ui/courses/detailed/ContentAdapter.java
new file mode 100644
index 00000000..5e7606e5
--- /dev/null
+++ b/app/src/main/java/org/schulcloud/mobile/ui/courses/detailed/ContentAdapter.java
@@ -0,0 +1,84 @@
+package org.schulcloud.mobile.ui.courses.detailed;
+
+import android.content.Context;
+import android.support.v7.widget.RecyclerView;
+import android.text.Html;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.TextView;
+
+import org.schulcloud.mobile.R;
+import org.schulcloud.mobile.data.DataManager;
+import org.schulcloud.mobile.data.model.Contents;
+import org.schulcloud.mobile.util.PicassoImageGetter;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.inject.Inject;
+
+import butterknife.BindView;
+import butterknife.ButterKnife;
+
+public class ContentAdapter extends RecyclerView.Adapter {
+
+ private List mContent;
+ private String mUserId;
+ private Context mContext;
+
+ @Inject
+ DetailedCoursePresenter mDetailedCoursePresenter;
+
+ @Inject
+ DataManager mDataManger;
+
+ @Inject
+ public ContentAdapter() {
+ mContent = new ArrayList<>();
+ }
+
+ public void setContent(List content) {
+ mContent = content;
+ }
+
+ public void setContext(Context context) { mContext = context; }
+
+ @Override
+ public ContentViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
+ View itemView = LayoutInflater.from(parent.getContext())
+ .inflate(R.layout.item_content, parent, false);
+ return new ContentViewHolder(itemView);
+ }
+
+ @Override
+ public void onBindViewHolder(ContentViewHolder holder, int position) {
+ Contents contents = mContent.get(position);
+ holder.nameTextView.setText(contents.title);
+
+ PicassoImageGetter imageGetter = new PicassoImageGetter(holder.descriptionTextView, mContext, mDataManger.getAccessToken());
+
+ if (contents.component.equals("text"))
+ holder.descriptionTextView.setText(Html.fromHtml(contents.content.text, imageGetter, null));
+ else
+ holder.descriptionTextView.setText(contents.component + " wird zurzeit nicht unterstüzt.");
+ }
+
+ @Override
+ public int getItemCount() {
+ return mContent.size();
+ }
+
+ class ContentViewHolder extends RecyclerView.ViewHolder {
+
+ @BindView(R.id.text_name)
+ TextView nameTextView;
+ @BindView(R.id.text_description)
+ TextView descriptionTextView;
+
+ public ContentViewHolder(View itemView) {
+ super(itemView);
+ ButterKnife.bind(this, itemView);
+ }
+ }
+}
diff --git a/app/src/main/java/org/schulcloud/mobile/ui/courses/detailed/DetailedCourseFragment.java b/app/src/main/java/org/schulcloud/mobile/ui/courses/detailed/DetailedCourseFragment.java
new file mode 100644
index 00000000..b5a953c8
--- /dev/null
+++ b/app/src/main/java/org/schulcloud/mobile/ui/courses/detailed/DetailedCourseFragment.java
@@ -0,0 +1,103 @@
+package org.schulcloud.mobile.ui.courses.detailed;
+
+import android.app.FragmentManager;
+import android.content.Intent;
+import android.os.Bundle;
+import android.support.v7.widget.LinearLayoutManager;
+import android.support.v7.widget.RecyclerView;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.TextView;
+
+import org.schulcloud.mobile.R;
+import org.schulcloud.mobile.data.model.Course;
+import org.schulcloud.mobile.data.model.Topic;
+import org.schulcloud.mobile.data.sync.TopicSyncService;
+import org.schulcloud.mobile.ui.base.BaseFragment;
+
+import java.util.List;
+
+import javax.inject.Inject;
+
+import butterknife.BindView;
+import butterknife.ButterKnife;
+
+public class DetailedCourseFragment extends BaseFragment implements DetailedCourseMvpView {
+
+ private static final String EXTRA_TRIGGER_SYNC_FLAG =
+ "org.schulcloud.mobile.ui.main.MainActivity.EXTRA_TRIGGER_SYNC_FLAG";
+
+ private String courseId = null;
+
+ @Inject
+ DetailedCoursePresenter mDetailedCoursePresenter;
+
+ @Inject
+ TopicsAdapter mTopicsAdapter;
+
+ @BindView(R.id.courseName)
+ TextView courseName;
+ @BindView(R.id.topicRecycler)
+ RecyclerView mRecyclerView;
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container,
+ Bundle savedInstanceState) {
+ activityComponent().inject(this);
+ View view = inflater.inflate(R.layout.fragment_detailed_course, container, false);
+ ButterKnife.bind(this, view);
+ Bundle args = getArguments();
+ courseId = args.getString("courseId");
+
+ mRecyclerView.setAdapter(mTopicsAdapter);
+ mRecyclerView.setLayoutManager(new LinearLayoutManager(getActivity()));
+
+ mDetailedCoursePresenter.attachView(this);
+ mDetailedCoursePresenter.loadCourse(courseId);
+ mDetailedCoursePresenter.loadTopics();
+
+ Intent intent = new Intent(getActivity(),TopicSyncService.class);
+ intent.putExtra("courseId", courseId);
+ getActivity().startService(intent);
+
+ return view;
+ }
+
+ /***** MVP View methods implementation *****/
+
+ @Override
+ public void showCourse(Course course) {
+ courseName.setText(course.name);
+ }
+
+ @Override
+ public void showTopics(List topics) {
+ mTopicsAdapter.setTopics(topics);
+ mTopicsAdapter.notifyDataSetChanged();
+ }
+
+ @Override
+ public void showTopicFragment(String topicId, String topicName) {
+ TopicFragment frag = new TopicFragment();
+ Bundle args = new Bundle();
+ args.putString("topicId", topicId);
+ args.putString("topicName", topicName);
+ frag.setArguments(args);
+ FragmentManager fragmentManager = getFragmentManager();
+ fragmentManager.beginTransaction()
+ .replace(R.id.overlay_fragment_container, frag)
+ .addToBackStack(null)
+ .commit();
+ }
+
+ @Override
+ public void showError() {
+
+ }
+
+ @Override
+ public void goToSignIn() {
+ // Necessary in fragment?
+ }
+}
diff --git a/app/src/main/java/org/schulcloud/mobile/ui/courses/detailed/DetailedCourseMvpView.java b/app/src/main/java/org/schulcloud/mobile/ui/courses/detailed/DetailedCourseMvpView.java
new file mode 100644
index 00000000..68992966
--- /dev/null
+++ b/app/src/main/java/org/schulcloud/mobile/ui/courses/detailed/DetailedCourseMvpView.java
@@ -0,0 +1,18 @@
+package org.schulcloud.mobile.ui.courses.detailed;
+
+import org.schulcloud.mobile.data.model.Course;
+import org.schulcloud.mobile.data.model.Topic;
+import org.schulcloud.mobile.ui.base.MvpView;
+
+import java.util.List;
+
+public interface DetailedCourseMvpView extends MvpView {
+
+ void showCourse(Course course);
+
+ void showTopics(List topics);
+
+ void showTopicFragment(String topicId, String topicName);
+
+ void showError();
+}
diff --git a/app/src/main/java/org/schulcloud/mobile/ui/courses/detailed/DetailedCoursePresenter.java b/app/src/main/java/org/schulcloud/mobile/ui/courses/detailed/DetailedCoursePresenter.java
new file mode 100644
index 00000000..16c078ce
--- /dev/null
+++ b/app/src/main/java/org/schulcloud/mobile/ui/courses/detailed/DetailedCoursePresenter.java
@@ -0,0 +1,63 @@
+package org.schulcloud.mobile.ui.courses.detailed;
+
+import org.schulcloud.mobile.data.DataManager;
+import org.schulcloud.mobile.injection.ConfigPersistent;
+import org.schulcloud.mobile.ui.base.BasePresenter;
+import org.schulcloud.mobile.util.RxUtil;
+
+import javax.inject.Inject;
+
+import rx.android.schedulers.AndroidSchedulers;
+import timber.log.Timber;
+
+@ConfigPersistent
+public class DetailedCoursePresenter extends BasePresenter {
+
+ @Inject
+ public DetailedCoursePresenter(DataManager dataManager) {
+ mDataManager = dataManager;
+ }
+
+ @Override
+ public void attachView(DetailedCourseMvpView mvpView) {
+ super.attachView(mvpView);
+ }
+
+ @Override
+ public void detachView() {
+ super.detachView();
+ if (mSubscription != null) mSubscription.unsubscribe();
+ }
+
+ /**
+ * loads a specific course for a given id.
+ * @param courseId given id for referencing.
+ */
+ public void loadCourse(String courseId) {
+ checkViewAttached();
+ getMvpView().showCourse(mDataManager.getCourseForId(courseId));
+ }
+
+ public void loadTopics() {
+ checkViewAttached();
+ RxUtil.unsubscribe(mSubscription);
+ mSubscription = mDataManager.getTopics()
+ .observeOn(AndroidSchedulers.mainThread())
+ .subscribe(
+ // onNext
+ topics -> {
+ getMvpView().showTopics(topics);
+ },
+ // onError
+ error -> {
+ Timber.e(error, "There was an error loading the topics.");
+ getMvpView().showError();
+ },
+ () -> {
+ });
+ };
+
+ public void showTopicDetail(String topicId, String topicName) {
+ getMvpView().showTopicFragment(topicId, topicName);
+ }
+}
diff --git a/app/src/main/java/org/schulcloud/mobile/ui/courses/detailed/TopicFragment.java b/app/src/main/java/org/schulcloud/mobile/ui/courses/detailed/TopicFragment.java
new file mode 100644
index 00000000..cb9cba40
--- /dev/null
+++ b/app/src/main/java/org/schulcloud/mobile/ui/courses/detailed/TopicFragment.java
@@ -0,0 +1,78 @@
+package org.schulcloud.mobile.ui.courses.detailed;
+
+import android.os.Bundle;
+import android.support.v7.widget.LinearLayoutManager;
+import android.support.v7.widget.RecyclerView;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.TextView;
+
+import org.schulcloud.mobile.R;
+import org.schulcloud.mobile.data.model.Contents;
+import org.schulcloud.mobile.ui.base.BaseFragment;
+
+import java.util.List;
+
+import javax.inject.Inject;
+
+import butterknife.BindView;
+import butterknife.ButterKnife;
+
+public class TopicFragment extends BaseFragment implements TopicMvpView {
+
+ private static final String EXTRA_TRIGGER_SYNC_FLAG =
+ "org.schulcloud.mobile.ui.main.MainActivity.EXTRA_TRIGGER_SYNC_FLAG";
+
+ private String topicId = null;
+
+ @Inject
+ TopicPresenter mTopicPresenter;
+
+ @Inject
+ ContentAdapter mContentAdapter;
+
+ @BindView(R.id.topicName)
+ TextView topicName;
+ @BindView(R.id.topicRecycler)
+ RecyclerView mRecyclerView;
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container,
+ Bundle savedInstanceState) {
+ activityComponent().inject(this);
+ View view = inflater.inflate(R.layout.fragment_topic, container, false);
+ ButterKnife.bind(this, view);
+ Bundle args = getArguments();
+ topicId = args.getString("topicId");
+
+ topicName.setText(args.getString("topicName"));
+
+ mRecyclerView.setAdapter(mContentAdapter);
+ mRecyclerView.setLayoutManager(new LinearLayoutManager(getActivity()));
+
+ mTopicPresenter.attachView(this);
+ mTopicPresenter.loadContents(topicId);
+
+ return view;
+ }
+
+ /***** MVP View methods implementation *****/
+
+ @Override
+ public void showContent(List contents) {
+ mContentAdapter.setContent(contents);
+ mContentAdapter.setContext(getActivity());
+ mContentAdapter.notifyDataSetChanged();
+ }
+
+ @Override
+ public void showError() {
+
+ }
+
+ @Override
+ public void goToSignIn() {
+ // Necessary in fragment?
+ }
+}
diff --git a/app/src/main/java/org/schulcloud/mobile/ui/courses/detailed/TopicMvpView.java b/app/src/main/java/org/schulcloud/mobile/ui/courses/detailed/TopicMvpView.java
new file mode 100644
index 00000000..5fc5838c
--- /dev/null
+++ b/app/src/main/java/org/schulcloud/mobile/ui/courses/detailed/TopicMvpView.java
@@ -0,0 +1,13 @@
+package org.schulcloud.mobile.ui.courses.detailed;
+
+import org.schulcloud.mobile.data.model.Contents;
+import org.schulcloud.mobile.ui.base.MvpView;
+
+import java.util.List;
+
+public interface TopicMvpView extends MvpView {
+
+ void showContent(List contents);
+
+ void showError();
+}
diff --git a/app/src/main/java/org/schulcloud/mobile/ui/courses/detailed/TopicPresenter.java b/app/src/main/java/org/schulcloud/mobile/ui/courses/detailed/TopicPresenter.java
new file mode 100644
index 00000000..75ad7f24
--- /dev/null
+++ b/app/src/main/java/org/schulcloud/mobile/ui/courses/detailed/TopicPresenter.java
@@ -0,0 +1,32 @@
+package org.schulcloud.mobile.ui.courses.detailed;
+
+import org.schulcloud.mobile.data.DataManager;
+import org.schulcloud.mobile.injection.ConfigPersistent;
+import org.schulcloud.mobile.ui.base.BasePresenter;
+
+import javax.inject.Inject;
+
+@ConfigPersistent
+public class TopicPresenter extends BasePresenter {
+
+ @Inject
+ public TopicPresenter(DataManager dataManager) {
+ mDataManager = dataManager;
+ }
+
+ @Override
+ public void attachView(TopicMvpView mvpView) {
+ super.attachView(mvpView);
+ }
+
+ @Override
+ public void detachView() {
+ super.detachView();
+ if (mSubscription != null) mSubscription.unsubscribe();
+ }
+
+ public void loadContents(String topicId) {
+ checkViewAttached();
+ getMvpView().showContent(mDataManager.getContents(topicId));
+ }
+}
diff --git a/app/src/main/java/org/schulcloud/mobile/ui/courses/detailed/TopicsAdapter.java b/app/src/main/java/org/schulcloud/mobile/ui/courses/detailed/TopicsAdapter.java
new file mode 100644
index 00000000..36600af3
--- /dev/null
+++ b/app/src/main/java/org/schulcloud/mobile/ui/courses/detailed/TopicsAdapter.java
@@ -0,0 +1,71 @@
+package org.schulcloud.mobile.ui.courses.detailed;
+
+import android.support.v7.widget.CardView;
+import android.support.v7.widget.RecyclerView;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.TextView;
+
+import org.schulcloud.mobile.R;
+import org.schulcloud.mobile.data.model.Topic;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.inject.Inject;
+
+import butterknife.BindView;
+import butterknife.ButterKnife;
+
+public class TopicsAdapter extends RecyclerView.Adapter {
+
+ private List mTopic;
+ private String mUserId;
+
+ @Inject
+ DetailedCoursePresenter mDetailedCoursePresenter;
+
+ @Inject
+ public TopicsAdapter() {
+ mTopic = new ArrayList<>();
+ }
+
+ public void setTopics(List topics) {
+ mTopic = topics;
+ }
+
+ @Override
+ public TopicsViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
+ View itemView = LayoutInflater.from(parent.getContext())
+ .inflate(R.layout.item_topic, parent, false);
+ return new TopicsViewHolder(itemView);
+ }
+
+ @Override
+ public void onBindViewHolder(TopicsViewHolder holder, int position) {
+ Topic topic = mTopic.get(position);
+
+ holder.nameTextView.setText(topic.name);
+
+ holder.cardView.setOnClickListener(v -> mDetailedCoursePresenter.showTopicDetail(topic._id, topic.name));
+ }
+
+ @Override
+ public int getItemCount() {
+ return mTopic.size();
+ }
+
+ class TopicsViewHolder extends RecyclerView.ViewHolder {
+
+ @BindView(R.id.text_name)
+ TextView nameTextView;
+ @BindView(R.id.card_view)
+ CardView cardView;
+
+ public TopicsViewHolder(View itemView) {
+ super(itemView);
+ ButterKnife.bind(this, itemView);
+ }
+ }
+}
diff --git a/app/src/main/java/org/schulcloud/mobile/util/PicassoImageGetter.java b/app/src/main/java/org/schulcloud/mobile/util/PicassoImageGetter.java
new file mode 100644
index 00000000..3a9e5f80
--- /dev/null
+++ b/app/src/main/java/org/schulcloud/mobile/util/PicassoImageGetter.java
@@ -0,0 +1,105 @@
+package org.schulcloud.mobile.util;
+
+
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.Drawable;
+import android.text.Html;
+import android.widget.TextView;
+
+import com.jakewharton.picasso.OkHttp3Downloader;
+import com.squareup.picasso.Picasso;
+import com.squareup.picasso.Target;
+
+import java.io.IOException;
+
+import okhttp3.Interceptor;
+import okhttp3.OkHttpClient;
+import okhttp3.Request;
+import okhttp3.Response;
+
+public class PicassoImageGetter implements Html.ImageGetter {
+
+ private TextView textView = null;
+ private Context context = null;
+ private String accessToken = null;
+
+ public PicassoImageGetter() {
+
+ }
+
+ public PicassoImageGetter(TextView target, Context context, String accessToken) {
+ textView = target;
+ this.context = context;
+ this.accessToken = accessToken;
+ }
+
+ @Override
+ public Drawable getDrawable(String source) {
+ BitmapDrawablePlaceHolder drawable = new BitmapDrawablePlaceHolder();
+
+ if (!source.contains("http://") && !source.contains("https://"))
+ source = "https://schul-cloud.org" + source;
+
+ OkHttpClient okHttpClient = new OkHttpClient().newBuilder()
+ .addInterceptor(new Interceptor() {
+ @Override
+ public Response intercept(Chain chain) throws IOException {
+ final Request original = chain.request();
+
+ final Request authorized = original.newBuilder()
+ .addHeader("Cookie", "jwt=" + accessToken)
+ .build();
+
+ return chain.proceed(authorized);
+ }
+ })
+ .build();
+
+ Picasso picasso = new Picasso.Builder(context)
+ .downloader(new OkHttp3Downloader(okHttpClient))
+ .build();
+ picasso.load(source).into(drawable);
+ return drawable;
+ }
+
+ private class BitmapDrawablePlaceHolder extends BitmapDrawable implements Target {
+
+ protected Drawable drawable;
+
+ @Override
+ public void draw(final Canvas canvas) {
+ if (drawable != null) {
+ drawable.draw(canvas);
+ }
+ }
+
+ public void setDrawable(Drawable drawable) {
+ this.drawable = drawable;
+ int width = drawable.getIntrinsicWidth();
+ int height = drawable.getIntrinsicHeight();
+ drawable.setBounds(0, 0, width, height);
+ setBounds(0, 0, width, height);
+ if (textView != null) {
+ textView.setText(textView.getText());
+ }
+ }
+
+ @Override
+ public void onBitmapLoaded(Bitmap bitmap, Picasso.LoadedFrom from) {
+ setDrawable(new BitmapDrawable(context.getResources(), bitmap));
+ }
+
+ @Override
+ public void onBitmapFailed(Drawable errorDrawable) {
+ }
+
+ @Override
+ public void onPrepareLoad(Drawable placeHolderDrawable) {
+
+ }
+
+ }
+}
diff --git a/app/src/main/res/layouts/courses/layout/activity_course.xml b/app/src/main/res/layouts/courses/layout/activity_course.xml
new file mode 100644
index 00000000..1346ace7
--- /dev/null
+++ b/app/src/main/res/layouts/courses/layout/activity_course.xml
@@ -0,0 +1,26 @@
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layouts/courses/layout/fragment_detailed_course.xml b/app/src/main/res/layouts/courses/layout/fragment_detailed_course.xml
new file mode 100644
index 00000000..8561f7db
--- /dev/null
+++ b/app/src/main/res/layouts/courses/layout/fragment_detailed_course.xml
@@ -0,0 +1,36 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layouts/courses/layout/fragment_topic.xml b/app/src/main/res/layouts/courses/layout/fragment_topic.xml
new file mode 100644
index 00000000..a93afc02
--- /dev/null
+++ b/app/src/main/res/layouts/courses/layout/fragment_topic.xml
@@ -0,0 +1,36 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layouts/courses/layout/item_content.xml b/app/src/main/res/layouts/courses/layout/item_content.xml
new file mode 100644
index 00000000..efd7f10e
--- /dev/null
+++ b/app/src/main/res/layouts/courses/layout/item_content.xml
@@ -0,0 +1,40 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layouts/courses/layout/item_course.xml b/app/src/main/res/layouts/courses/layout/item_course.xml
new file mode 100644
index 00000000..dae1919a
--- /dev/null
+++ b/app/src/main/res/layouts/courses/layout/item_course.xml
@@ -0,0 +1,59 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layouts/courses/layout/item_topic.xml b/app/src/main/res/layouts/courses/layout/item_topic.xml
new file mode 100644
index 00000000..c9ccc212
--- /dev/null
+++ b/app/src/main/res/layouts/courses/layout/item_topic.xml
@@ -0,0 +1,34 @@
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 837bd989..46d1841c 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -5,6 +5,7 @@
Dateien
Hausaufgaben
+ Fächer / Kurse
Einstellungen
OK