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