Skip to content

Commit 5a1d83f

Browse files
Merge pull request #45 from SChakravorti21/session-management
Smart Lock Integration 🎉
2 parents e4759b7 + eef8f7e commit 5a1d83f

File tree

82 files changed

+1062
-876
lines changed

Some content is hidden

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

82 files changed

+1062
-876
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: 27 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package com.sakaimobile.development.sakaiclient20.networking.utilities;
22

3-
import android.util.Log;
3+
import android.graphics.Bitmap;
4+
import android.os.Build;
45
import android.webkit.CookieManager;
56
import android.webkit.WebResourceResponse;
67
import android.webkit.WebView;
@@ -9,7 +10,6 @@
910
import java.io.IOException;
1011

1112
import okhttp3.Call;
12-
import okhttp3.Headers;
1313
import okhttp3.OkHttpClient;
1414
import okhttp3.Request;
1515
import okhttp3.Response;
@@ -21,10 +21,10 @@
2121
public class CASWebViewClient extends WebViewClient {
2222

2323
public interface SakaiLoadedListener {
24-
void onSakaiMainPageLoaded(Headers h);
24+
void onLoginSuccess(String username, String password);
2525
}
2626

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

@@ -39,6 +39,7 @@ public interface SakaiLoadedListener {
3939
private OkHttpClient httpClient;
4040
// Keeping track of relevant headers
4141
private boolean gotHeaders;
42+
private String username, password;
4243

4344
public CASWebViewClient(String url, SakaiLoadedListener loadedListener) {
4445
super();
@@ -98,7 +99,7 @@ private WebResourceResponse handleRequest(String url) {
9899
String sakaiSessionHeader = response.headers().get("x-sakai-session");
99100
if (sakaiSessionHeader != null && !gotHeaders) {
100101
gotHeaders = true;
101-
sakaiLoadedListener.onSakaiMainPageLoaded(null);
102+
sakaiLoadedListener.onLoginSuccess(username, password);
102103
}
103104

104105
// We need to return a WebResourceResponse, otherwise the
@@ -109,4 +110,25 @@ private WebResourceResponse handleRequest(String url) {
109110
response.body().byteStream()
110111
);
111112
}
113+
114+
@Override
115+
public void onPageStarted(WebView view, String url, Bitmap favicon) {
116+
super.onPageStarted(view, url, favicon);
117+
// It is possible that we are moving away from this page,
118+
// so in case it is the login page, extract the username and password
119+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
120+
// JS-evaluated strings have double quotes surrounding them, necessitating
121+
// all the null and length checks
122+
view.evaluateJavascript("document.querySelector('#username').value", username -> {
123+
if(username != null && !"null".equals(username) && username.length() > 2) {
124+
this.username = username.substring(1, username.length() - 1);
125+
}
126+
});
127+
view.evaluateJavascript("document.querySelector('#password').value", password -> {
128+
if(password != null && !"null".equals(password) && password.length() > 2) {
129+
this.password = password.substring(1, password.length() - 1);
130+
}
131+
});
132+
}
133+
}
112134
}

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)

app/src/main/java/com/sakaimobile/development/sakaiclient20/ui/activities/CreditsActivity.java

Lines changed: 4 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@ public class CreditsActivity extends AppCompatActivity {
1616
private static final String[] creditsList = {
1717
"Rutgers Sakai",
1818
"Icons8",
19-
"Logomakr",
2019
"RxAndroid",
2120
"AndroidTreeView",
2221
"Retrofit",
@@ -26,14 +25,12 @@ public class CreditsActivity extends AppCompatActivity {
2625
private static final String[] creditsURLs = {
2726
"https://sakai.rutgers.edu/direct/describe",
2827
"https://icons8.com/",
29-
"https://logomakr.com/",
3028
"https://github.com/ReactiveX/RxAndroid",
3129
"https://github.com/bmelnychuk/AndroidTreeView",
3230
"https://github.com/square/retrofit",
3331
"https://github.com/square/okhttp"
3432
};
3533

36-
3734
@Override
3835
protected void onCreate(Bundle savedInstanceState) {
3936
super.onCreate(savedInstanceState);
@@ -44,21 +41,17 @@ protected void onCreate(Bundle savedInstanceState) {
4441
toolbar.setNavigationOnClickListener((v) -> onBackPressed());
4542

4643
ListView creditsListView = findViewById(R.id.credits_list_view);
47-
creditsListView.setOnItemClickListener(
48-
((parent, view, position, id) -> {
49-
String url = creditsURLs[position];
50-
openURL(url);
51-
})
52-
);
44+
creditsListView.setOnItemClickListener((parent, view, position, id) -> {
45+
String url = creditsURLs[position];
46+
openURL(url);
47+
});
5348

5449
ArrayAdapter<String> adapter = new ArrayAdapter<>(this, R.layout.credits_item, R.id.creditNameTxt, creditsList);
5550
creditsListView.setAdapter(adapter);
56-
5751
}
5852

5953

6054
private void openURL(String url) {
61-
6255
Intent viewIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
6356
viewIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
6457
viewIntent.setPackage("com.android.chrome");
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
package com.sakaimobile.development.sakaiclient20.ui.activities
2+
3+
4+
import android.arch.lifecycle.Observer
5+
import android.arch.lifecycle.ViewModelProviders
6+
import android.content.Intent
7+
import android.os.Bundle
8+
import android.support.v4.app.Fragment
9+
import android.support.v7.app.AppCompatActivity
10+
11+
import com.sakaimobile.development.sakaiclient20.R
12+
import com.sakaimobile.development.sakaiclient20.ui.viewmodels.LoadingPageViewModel
13+
import com.sakaimobile.development.sakaiclient20.ui.viewmodels.ViewModelFactory
14+
import dagger.android.AndroidInjection
15+
import kotlinx.android.synthetic.main.activity_loading.*
16+
import javax.inject.Inject
17+
import android.view.animation.DecelerateInterpolator
18+
import android.animation.ObjectAnimator
19+
import android.widget.Toast
20+
21+
22+
/**
23+
* A simple [Fragment] subclass.
24+
*
25+
*/
26+
class LoadingActivity : AppCompatActivity() {
27+
28+
@Inject lateinit var viewModelFactory: ViewModelFactory
29+
private lateinit var loadingPageViewModel: LoadingPageViewModel
30+
private var hasShowedContent = false
31+
32+
override fun onCreate(savedInstanceState: Bundle?) {
33+
AndroidInjection.inject(this)
34+
super.onCreate(savedInstanceState)
35+
setContentView(R.layout.activity_loading)
36+
37+
this.loadingPageViewModel =
38+
ViewModelProviders.of(this, viewModelFactory)[LoadingPageViewModel::class.java]
39+
}
40+
41+
override fun onResume() {
42+
super.onResume()
43+
44+
// Just in case network connectivity changes as refresh is occurring,
45+
// make sure the user is notified of this.
46+
loadingPageViewModel.errorState.observe(this, Observer {
47+
Toast.makeText(this, "We had trouble reaching Sakai, please restart this application", Toast.LENGTH_LONG).show()
48+
})
49+
50+
loadingPageViewModel.getRefreshProgress(true).observe(this, Observer { progress ->
51+
updateProgress(progress)
52+
// Use hasShowedContent to prevent double-loading the next page
53+
// If progress equals NUM_REFRESH_REQUESTS then we finished loading everything
54+
if(progress == LoadingPageViewModel.NUM_REFRESH_REQUESTS && !hasShowedContent) {
55+
hasShowedContent = true
56+
val intent = Intent(this, MainActivity::class.java)
57+
startActivity(intent)
58+
finish()
59+
}
60+
})
61+
}
62+
63+
private fun updateProgress(progress: Int?) {
64+
// Create an animation for smooth update of progress bar
65+
val displayProgress = progress?.times((100 / LoadingPageViewModel.NUM_REFRESH_REQUESTS)) ?: 0
66+
val animation = ObjectAnimator.ofInt(progressbar, "progress", displayProgress)
67+
animation.duration = 500 // 0.5 second
68+
animation.interpolator = DecelerateInterpolator()
69+
animation.start()
70+
}
71+
}

0 commit comments

Comments
 (0)