Skip to content

Commit c5ee44d

Browse files
Merge pull request #47 from SChakravorti21/master
Release 1.1.0
2 parents 5ce8513 + 58b4042 commit c5ee44d

File tree

93 files changed

+1321
-1136
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

93 files changed

+1321
-1136
lines changed

.idea/assetWizardSettings.xml

Lines changed: 47 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

app/build.gradle

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,8 @@ android {
3232
applicationId "com.sakaimobile.development.sakaiclient20"
3333
minSdkVersion 16
3434
targetSdkVersion 28
35-
versionCode 6
36-
versionName "1.0"
35+
versionCode 8
36+
versionName "1.1.0"
3737
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
3838
javaCompileOptions {
3939
annotationProcessorOptions {
@@ -107,9 +107,6 @@ dependencies {
107107
api 'com.google.code.gson:gson:2.8.4'
108108
api 'com.squareup.retrofit2:converter-gson:2.3.0'
109109

110-
//Common-Lang3 is used for toString of POJOs
111-
api 'org.apache.commons:commons-lang3:3.6'
112-
113110
//Android TreeView library for generating tree-like structures
114111
api 'com.github.bmelnychuk:atv:1.2.9'
115112

@@ -129,6 +126,8 @@ dependencies {
129126

130127
// Crashlytics for crash/error reporting
131128
implementation 'com.crashlytics.sdk.android:crashlytics:2.9.8'
129+
// Google Play Services used for Smart Lock integration
130+
implementation 'com.google.android.gms:play-services-auth:16.0.1'
132131

133132
// LeakCanary for memory leak detection
134133
debugImplementation 'com.squareup.leakcanary:leakcanary-android:1.6.3'

app/src/main/AndroidManifest.xml

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,12 @@
1010
android:name=".ui.SakaiApplication"
1111
android:allowBackup="true"
1212
android:icon="@mipmap/ic_launcher"
13-
android:label="@string/app_name"
1413
android:roundIcon="@mipmap/ic_launcher_round"
14+
android:label="@string/app_name"
1515
android:supportsRtl="true"
1616
android:theme="@style/AppTheme">
1717
<activity
18-
android:name=".ui.activities.WebViewActivity"
18+
android:name=".ui.activities.LoginActivity"
1919
android:screenOrientation="portrait">
2020
<intent-filter>
2121
<action android:name="android.intent.action.MAIN" />
@@ -30,6 +30,8 @@
3030
android:screenOrientation="portrait"/>
3131
<activity android:name=".ui.activities.CreditsActivity"
3232
android:screenOrientation="portrait"/>
33+
<activity android:name=".ui.activities.LoadingActivity"
34+
android:screenOrientation="portrait"/>
3335

3436
<provider
3537
android:name="android.support.v4.content.FileProvider"

app/src/main/ic_launcher-web.png

-24.3 KB
Loading

app/src/main/java/com/sakaimobile/development/sakaiclient20/dependency_injection/SakaiApplicationModule.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import com.sakaimobile.development.sakaiclient20.ui.fragments.AllCoursesFragment;
66
import com.sakaimobile.development.sakaiclient20.ui.fragments.AllGradesFragment;
77
import com.sakaimobile.development.sakaiclient20.ui.fragments.AnnouncementsFragment;
8+
import com.sakaimobile.development.sakaiclient20.ui.activities.LoadingActivity;
89
import com.sakaimobile.development.sakaiclient20.ui.fragments.SiteChatFragment;
910
import com.sakaimobile.development.sakaiclient20.ui.fragments.assignments.AssignmentsFragment;
1011
import com.sakaimobile.development.sakaiclient20.ui.fragments.SiteGradesFragment;
@@ -28,6 +29,9 @@ abstract class SakaiApplicationModule {
2829
@ContributesAndroidInjector
2930
abstract SitePageActivity contributeSitePageActivityInjector();
3031

32+
@ContributesAndroidInjector
33+
abstract LoadingActivity contributeLoadingActivityInjector();
34+
3135
@ContributesAndroidInjector
3236
abstract AllCoursesFragment contributesFragmentInjector();
3337

app/src/main/java/com/sakaimobile/development/sakaiclient20/dependency_injection/ViewModelModule.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import com.sakaimobile.development.sakaiclient20.ui.viewmodels.AssignmentViewModel;
88
import com.sakaimobile.development.sakaiclient20.ui.viewmodels.CourseViewModel;
99
import com.sakaimobile.development.sakaiclient20.ui.viewmodels.GradeViewModel;
10+
import com.sakaimobile.development.sakaiclient20.ui.viewmodels.LoadingPageViewModel;
1011
import com.sakaimobile.development.sakaiclient20.ui.viewmodels.ResourceViewModel;
1112
import com.sakaimobile.development.sakaiclient20.ui.viewmodels.ViewModelFactory;
1213

@@ -42,6 +43,11 @@ abstract class ViewModelModule {
4243
@ViewModelKey(GradeViewModel.class)
4344
abstract ViewModel bindGradeViewModel(GradeViewModel gradeViewModel);
4445

46+
@Binds
47+
@IntoMap
48+
@ViewModelKey(LoadingPageViewModel.class)
49+
abstract ViewModel bindLoadingPageViewModel(LoadingPageViewModel loadingPageViewModel);
50+
4551
@Binds
4652
abstract ViewModelProvider.Factory bindViewModelFactory(ViewModelFactory factory);
4753

app/src/main/java/com/sakaimobile/development/sakaiclient20/networking/utilities/CASWebViewClient.java

Lines changed: 47 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
package com.sakaimobile.development.sakaiclient20.networking.utilities;
22

3-
import android.util.Log;
3+
import android.os.Build;
44
import android.webkit.CookieManager;
55
import android.webkit.WebResourceResponse;
66
import android.webkit.WebView;
@@ -9,7 +9,6 @@
99
import java.io.IOException;
1010

1111
import okhttp3.Call;
12-
import okhttp3.Headers;
1312
import okhttp3.OkHttpClient;
1413
import okhttp3.Request;
1514
import okhttp3.Response;
@@ -21,10 +20,10 @@
2120
public class CASWebViewClient extends WebViewClient {
2221

2322
public interface SakaiLoadedListener {
24-
void onSakaiMainPageLoaded(Headers h);
23+
void onLoginSuccess(String username, String password);
2524
}
2625

27-
// This listener let's our WebViewActivity know that
26+
// This listener let's our LoginActivity know that
2827
// login was successful, and a new activity can be started
2928
private SakaiLoadedListener sakaiLoadedListener;
3029

@@ -39,6 +38,7 @@ public interface SakaiLoadedListener {
3938
private OkHttpClient httpClient;
4039
// Keeping track of relevant headers
4140
private boolean gotHeaders;
41+
private String username, password;
4242

4343
public CASWebViewClient(String url, SakaiLoadedListener loadedListener) {
4444
super();
@@ -68,12 +68,12 @@ public WebResourceResponse shouldInterceptRequest(WebView view, String url) {
6868
// We only need to intercept once authentication is complete, as the
6969
// the cookies do not change afterwards
7070
if (url.startsWith("https://sakai.rutgers.edu/portal") && !gotHeaders)
71-
return handleRequest(url);
71+
return handleRequest(view, url);
7272

7373
return super.shouldInterceptRequest(view, url);
7474
}
7575

76-
private WebResourceResponse handleRequest(String url) {
76+
private WebResourceResponse handleRequest(WebView view, String url) {
7777
// After intercepting the request, we need to handle it
7878
// ourselves. This is done by creating an OkHttp3 Call,
7979
// which we add the Sakai cookies to. Without the cookies,
@@ -92,21 +92,53 @@ private WebResourceResponse handleRequest(String url) {
9292
return null;
9393
}
9494

95+
// onPageStarted is not a good place to intercept credentials because
96+
// it is called too late in the release build variant. Instead, perform that
97+
// operation here. Call to post() is necessary for eval to run on the UI thread.
98+
view.post(() -> {
99+
// It is possible that we are moving away from this page,
100+
// so in case it is the login page, extract the username and password
101+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
102+
// We get both credentials as an array to prevent synchronization issues
103+
// with getting username and password separately.
104+
view.evaluateJavascript(
105+
"[document.querySelector('#username').value, document.querySelector('#password').value]",
106+
credentials -> {
107+
// JS-evaluated strings have double quotes surrounding them, necessitating
108+
// all the null and length checks, and odd substring indices.
109+
if(credentials != null && !"null".equals(credentials)
110+
&& !"undefined".equals(credentials) && credentials.length() > 7) {
111+
final String separator = "\",\"";
112+
int separatorIndex = credentials.indexOf(separator);
113+
this.username = credentials.substring(2, separatorIndex);
114+
this.password = credentials.substring(separatorIndex + separator.length(), credentials.length() - 2);
115+
}
116+
// Whether we got username and password or not, the authentication was
117+
// successful and this must be indicated to the LoginActivity
118+
tryCompleteLogin(response);
119+
}
120+
);
121+
} else {
122+
// Can't evaluate JS, just login as usual
123+
tryCompleteLogin(response);
124+
}
125+
});
126+
127+
// We need to return a WebResourceResponse, otherwise the
128+
// WebView will think that the request is hanging. The WebView
129+
// renders this response.
130+
// We do not need to modify the mimeType or encoding of the response.
131+
return new WebResourceResponse(null, null, response.body().byteStream());
132+
}
133+
134+
private void tryCompleteLogin(Response response) {
95135
// After getting the response from Sakai, we can get the
96136
// headers if it has what we want. Specifically, we need
97137
// the X-Sakai-Session cookie.
98138
String sakaiSessionHeader = response.headers().get("x-sakai-session");
99139
if (sakaiSessionHeader != null && !gotHeaders) {
100140
gotHeaders = true;
101-
sakaiLoadedListener.onSakaiMainPageLoaded(null);
141+
sakaiLoadedListener.onLoginSuccess(username, password);
102142
}
103-
104-
// We need to return a WebResourceResponse, otherwise the
105-
// WebView will think that the request is hanging. The WebView
106-
// renders this response.
107-
// We do not need to modify the mimeType or encoding of the response.
108-
return new WebResourceResponse(null, null,
109-
response.body().byteStream()
110-
);
111143
}
112144
}

app/src/main/java/com/sakaimobile/development/sakaiclient20/persistence/entities/Announcement.java

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@
2525
@Index(value = "siteId"),
2626
@Index(value = "announcementId")
2727
})
28-
// TODO: implement parcelable
2928
public class Announcement implements Serializable {
3029

3130
@Ignore
@@ -44,12 +43,14 @@ public class Announcement implements Serializable {
4443
public String createdBy;
4544
public long createdOn;
4645

46+
@Ignore public int subjectCode;
47+
@Ignore public String courseTitle;
48+
4749
// Formatted dates are generated at runtime to be shown to the user
4850
// since we use relative terms like "Today"
49-
@Ignore
50-
private String shortFormattedDate;
51-
@Ignore
52-
private String longFormattedDate;
51+
@Ignore private String shortFormattedDate;
52+
@Ignore private String longFormattedDate;
53+
5354

5455
public Announcement(@NonNull String announcementId) {
5556
this.announcementId = announcementId;

app/src/main/java/com/sakaimobile/development/sakaiclient20/repositories/CourseRepository.java

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -46,18 +46,19 @@ public Single<Course> getCourse(String siteId) {
4646
.map(this::flattenCompositeToEntity);
4747
}
4848

49-
public Flowable<List<List<Course>>> getCoursesSortedByTerm() {
49+
public Flowable<List<Course>> getAllCourses() {
5050
return courseDao.getAllCourses()
51-
//.firstOrError()
52-
//.toObservable()
5351
.debounce(300, TimeUnit.MILLISECONDS)
5452
.map(courses -> {
5553
List<Course> flattened = new ArrayList<>(courses.size());
5654
for(CourseWithAllData course : courses)
5755
flattened.add(flattenCompositeToEntity(course));
5856
return flattened;
59-
})
60-
.map(this::sortCoursesByTerm);
57+
});
58+
}
59+
60+
public Flowable<List<List<Course>>> getCoursesSortedByTerm() {
61+
return this.getAllCourses().map(this::sortCoursesByTerm);
6162
}
6263

6364
public Completable refreshCourse(String siteId) {

app/src/main/java/com/sakaimobile/development/sakaiclient20/ui/SakaiApplication.java

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
import com.sakaimobile.development.sakaiclient20.BuildConfig;
1818
import com.sakaimobile.development.sakaiclient20.dependency_injection.DaggerSakaiApplicationComponent;
1919
import com.sakaimobile.development.sakaiclient20.networking.services.SessionService;
20-
import com.sakaimobile.development.sakaiclient20.ui.activities.WebViewActivity;
20+
import com.sakaimobile.development.sakaiclient20.ui.activities.LoginActivity;
2121
import com.sakaimobile.development.sakaiclient20.ui.custom_components.DownloadCompleteReceiver;
2222
import com.sakaimobile.development.sakaiclient20.ui.helpers.CourseIconProvider;
2323
import com.squareup.leakcanary.LeakCanary;
@@ -29,7 +29,6 @@
2929
import dagger.android.HasActivityInjector;
3030
import dagger.android.support.HasSupportFragmentInjector;
3131
import io.fabric.sdk.android.Fabric;
32-
import io.reactivex.android.plugins.RxAndroidPlugins;
3332
import io.reactivex.android.schedulers.AndroidSchedulers;
3433
import io.reactivex.disposables.CompositeDisposable;
3534
import io.reactivex.plugins.RxJavaPlugins;
@@ -120,7 +119,7 @@ public void onMoveToForeground() {
120119

121120
// Otherwise, session has become inactive and user must be prompted
122121
// to log in
123-
Intent intent = new Intent(SakaiApplication.this, WebViewActivity.class);
122+
Intent intent = new Intent(SakaiApplication.this, LoginActivity.class);
124123
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NEW_TASK);
125124
startActivity(intent);
126125
}, Throwable::printStackTrace)

0 commit comments

Comments
 (0)