diff options
author | Andy Doan <andy.doan@linaro.org> | 2011-10-27 00:37:39 -0500 |
---|---|---|
committer | Andy Doan <andy.doan@linaro.org> | 2011-10-27 01:50:33 -0500 |
commit | 46c60edce5506e0b78a0ccb48d9408ea40c7676f (patch) | |
tree | 4b35b30fbc2bc4a7a4a03ff6d060e3e688b7701a | |
parent | bf6f99338f5815c405d78079541c2de1ec63c612 (diff) | |
download | LinaroConnect-46c60edce5506e0b78a0ccb48d9408ea40c7676f.tar.gz |
add support for showing personal schedule
This creates a class that can automate the logic of logging
into summit.linaro.org and getting the user's key which is
used in the url to download their iCal schedule
-rw-r--r-- | AndroidManifest.xml | 9 | ||||
-rw-r--r-- | res/layout/my_schedule_unconfigured.xml | 84 | ||||
-rw-r--r-- | res/values/strings.xml | 13 | ||||
-rw-r--r-- | src/org/linaro/connect/MyScheduleActivity.java | 118 | ||||
-rw-r--r-- | src/org/linaro/connect/OpenIDLogin.java | 134 | ||||
-rw-r--r-- | src/org/linaro/connect/ScheduleActivity.java | 6 |
6 files changed, 364 insertions, 0 deletions
diff --git a/AndroidManifest.xml b/AndroidManifest.xml index 92392a9..2c3c1e9 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -49,6 +49,15 @@ <activity android:name=".ScheduleItemActivity" android:label="@string/schedule_item"> </activity> + <activity android:name=".MyScheduleActivity" + android:label="@string/schedule_mine" + android:configChanges="orientation|keyboardHidden" > + <intent-filter> + <action android:name="org.linaro.connect.MY_SCHEDULE" /> + <category android:name="android.intent.category.DEFAULT" /> + <data android:scheme="http"/> + </intent-filter> + </activity> </application> </manifest>
\ No newline at end of file diff --git a/res/layout/my_schedule_unconfigured.xml b/res/layout/my_schedule_unconfigured.xml new file mode 100644 index 0000000..6d9ef82 --- /dev/null +++ b/res/layout/my_schedule_unconfigured.xml @@ -0,0 +1,84 @@ +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="fill_parent" + android:layout_height="fill_parent" + android:orientation="vertical" + android:background="@color/main_bg" + android:paddingLeft="5px" +> + + <TextView + android:layout_height="wrap_content" + android:layout_width="fill_parent" + android:textSize="14sp" + android:textStyle="bold" + android:gravity="center" + android:text="@string/my_schedule_lp" + /> + + <TextView + android:layout_height="wrap_content" + android:layout_width="fill_parent" + android:text="@string/my_schedule_user" + /> + <EditText + android:id="@+id/my_schedule_user" + android:layout_height="wrap_content" + android:layout_width="fill_parent" + /> + <TextView + android:layout_height="wrap_content" + android:layout_width="fill_parent" + android:text="@string/my_schedule_pass" + /> + <EditText + android:id="@+id/my_schedule_pass" + android:layout_height="wrap_content" + android:layout_width="fill_parent" + android:inputType="textPassword" + /> + + <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:orientation="horizontal" + > + <TextView + android:id="@+id/my_schedule_status" + android:layout_height="wrap_content" + android:layout_width="fill_parent" + android:layout_weight="2" + android:layout_gravity="center" + /> + <Button + android:id="@+id/my_schedule_login" + android:layout_height="wrap_content" + android:layout_width="fill_parent" + android:layout_weight="3" + android:layout_gravity="right" + android:text="@string/my_schedule_login" + /> + </LinearLayout> + + <TextView + android:layout_height="wrap_content" + android:layout_width="fill_parent" + android:textSize="14sp" + android:textStyle="bold" + android:text="@string/my_schedule_login_about" + /> + <TextView + android:layout_height="wrap_content" + android:layout_width="fill_parent" + android:background="@drawable/rounded" + android:paddingLeft="5px" + android:textColor="@color/linaro_green" + android:text="@string/my_schedule_info" + /> + + <WebView + android:id="@+id/my_schedule_webview" + android:layout_width="fill_parent" + android:layout_height="fill_parent" + /> + +</LinearLayout>
\ No newline at end of file diff --git a/res/values/strings.xml b/res/values/strings.xml index d3fb3b3..78057a8 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -3,4 +3,17 @@ <string name="app_name">Linaro Connect</string> <string name="posting_refresh">Refresh</string> <string name="schedule_item">Linaro Connect - Session</string> + + <string name="schedule_mine">Linaro Connect - My Schedule</string> + <string name="my_schedule_lp">Launchpad Login</string> + <string name="my_schedule_user">Email:</string> + <string name="my_schedule_pass">Password:</string> + <string name="my_schedule_info">In order to access your schedule we\'ll need + to log into Launchpad one time. This will give the app a key that it can + retrieve your schedule with. No personal information will be saved. + </string> + <string name="my_schedule_login_about">About</string> + <string name="my_schedule_login">Log In</string> + <string name="my_schedule_login_busy">Authorizing...</string> + <string name="my_schedule_login_failed">Login failed! Try Again.</string> </resources> diff --git a/src/org/linaro/connect/MyScheduleActivity.java b/src/org/linaro/connect/MyScheduleActivity.java new file mode 100644 index 0000000..8de22dd --- /dev/null +++ b/src/org/linaro/connect/MyScheduleActivity.java @@ -0,0 +1,118 @@ +package org.linaro.connect; + +import android.app.Activity; +import android.content.Intent; +import android.content.SharedPreferences.Editor; +import android.net.Uri; +import android.os.Bundle; +import android.os.Handler; +import android.os.Message; +import android.view.View; +import android.view.Window; +import android.webkit.WebView; +import android.widget.Button; +import android.widget.EditText; +import android.widget.TextView; + +/** + * This is a dual-purpose activity. If the user has never used it or logged + * into summit, then it prompts them for authentication information. + * + * Once we have the secret key we need, its just a pass-through that launches + * the ScheduleActivty with the proper URL. + */ +public class MyScheduleActivity extends Activity{ + + private Button mLoginBtn; + private TextView mStatus; + private OpenIDLogin mOil; + + private String mKeyName; + + @Override + protected void onCreate(Bundle savedInstanceState) { + requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS); + super.onCreate(savedInstanceState); + + //we use the url of the event as keyname, so uds-p, uds-o will each + //have their own key + mKeyName = getIntent().getDataString(); + + String secret = getPreferences(MODE_PRIVATE).getString(mKeyName, null); + if( secret != null ) + launchRealCalendar(secret); + else + onCreateUnconfigured(savedInstanceState); + } + + protected void onCreateUnconfigured(Bundle savedInstanceState) { + setContentView(R.layout.my_schedule_unconfigured); + + final WebView wv = (WebView)findViewById(R.id.my_schedule_webview); + wv.setVisibility(View.GONE); + mOil = new OpenIDLogin(getIntent().getDataString(), wv); + + mStatus = (TextView)findViewById(R.id.my_schedule_status); + + mLoginBtn = (Button)findViewById(R.id.my_schedule_login); + mLoginBtn.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + String user = getInput(R.id.my_schedule_user); + String pass = getInput(R.id.my_schedule_pass); + + setProgressBarIndeterminateVisibility(true); + mLoginBtn.setEnabled(false); + mLoginBtn.setText(R.string.my_schedule_login_busy); + + mOil.login(user, pass, mHandler); + } + }); + } + + private String getInput(int id) { + EditText t = (EditText)findViewById(id); + return t.getText().toString(); + } + + private Uri getICalUri(String key) { + String url = getIntent().getDataString()+"participant/my_schedule_"+key+".ical"; + return Uri.parse(url); + } + + private void launchRealCalendar(String key) { + Intent cal = new Intent("org.linaro.connect.SCHEDULE", getICalUri(key)); + cal.putExtra(JSONLayoutItem.INTENT_LABEL, "My Schedule"); + cal.putExtra(ScheduleActivity.INTENT_CACHENAME, "my-schedule"); + startActivity(cal); + finish(); + } + + Handler mHandler = new Handler() { + @Override + public void handleMessage(Message msg) { + int percent = msg.getData().getInt(OpenIDLogin.STATUS, -1); + if( percent != -1 ) { + mStatus.setText("" + percent + "% ..."); + return; + } + + //the login has succeeded or failed + setProgressBarIndeterminateVisibility(false); + mLoginBtn.setText(R.string.my_schedule_login); + + String token = msg.getData().getString(OpenIDLogin.SECRET_KEY); + if( token == null ) { + mStatus.setText(R.string.my_schedule_login_failed); + mLoginBtn.setEnabled(true); + return; + } + + //the login succeed + Editor ed = getPreferences(MODE_PRIVATE).edit(); + ed.putString(mKeyName, token); + ed.commit(); + launchRealCalendar(token); + } + }; +} diff --git a/src/org/linaro/connect/OpenIDLogin.java b/src/org/linaro/connect/OpenIDLogin.java new file mode 100644 index 0000000..7326625 --- /dev/null +++ b/src/org/linaro/connect/OpenIDLogin.java @@ -0,0 +1,134 @@ +package org.linaro.connect; + +import android.os.Bundle; +import android.os.Handler; +import android.os.Message; +import android.util.Log; +import android.webkit.WebView; +import android.webkit.WebViewClient; + +/** + * This class uses some JavaScript trickery via the android webview in order + * to perform an openid login to summit.linaro.org. We need to get the key + * that is used for a users personal ical schedule. + */ +public class OpenIDLogin { + + public final static String SECRET_KEY = "SECRET"; + public final static String STATUS = "STATUS"; + + private final WebView mWebView; + private String mOnSetUserPassJS; + private Handler mHandler; + + private boolean mLoginHit = false; + + public OpenIDLogin(final String destinationURL, WebView v) { + mWebView = v; + + v.getSettings().setJavaScriptEnabled(true); + v.getSettings().setDatabaseEnabled(false); + v.getSettings().setSaveFormData(false); + v.getSettings().setSavePassword(false); + + v.addJavascriptInterface(new JavaScriptInterface(), "ANDROID"); + v.setWebViewClient(new WebViewClient() { + @Override + public void onPageFinished(WebView view, String url) { + // inject the proper js script into what we are loading + if(url.equals(destinationURL)) { + mWebView.loadUrl(onSummitLoggedIn); + sendStatus(90); + } + else if( url.equals("http://summit.linaro.org/openid/login/?next=") ) { + mWebView.loadUrl(onOpenidRedirect); + sendStatus(30); + } + else if( url.startsWith("https://login.launchpad.net/") ) { + if(mLoginHit) { + //this is the second time we hit this page. this means + //the login failed + sendStatus(90); + sendToken(null); + return; + } + + mLoginHit = true; + mWebView.loadUrl(mOnSetUserPassJS); + sendStatus(60); + } + else { + Log.w(LinaroConnect.TAG, "unknown URL: " + url); + sendToken(null); //tell the caller to stop waiting + } + } + }); + } + + //launches a login in a background thread + public void login(String user, String pass, Handler h) { + mOnSetUserPassJS = String.format(SETUSERPASS_JS, new Object[]{user, pass}); + mHandler = h; + mLoginHit = false; + sendStatus(0); + mWebView.loadUrl("http://summit.linaro.org/openid/login?next="); + } + + private void sendToken(String token) { + Bundle b = new Bundle(); + b.putString(SECRET_KEY, token); + + Message m = mHandler.obtainMessage(); + m.setData(b); + m.sendToTarget(); + } + + private void sendStatus(int percent) { + Bundle b = new Bundle(); + b.putInt(STATUS, percent); + + Message m = mHandler.obtainMessage(); + m.setData(b); + m.sendToTarget(); + } + + class JavaScriptInterface { + final static String NEEDLE = "participant/my_schedule_"; + + public void onOpenidRedirect() { + } + + /** + * Called when we are actually logged into summit with the content of + * the page. We search the page for the key used to download the + * user's ical schedule + */ + public void onSummitLoggedIn(String html) { + int idx = html.indexOf(NEEDLE); + String token = null; + if(idx > 0) { + idx+= NEEDLE.length(); + int idx2 = html.indexOf(".ical", idx); + if( idx2 > 0 ) + token = html.substring(idx, idx2); + } + sendToken(token); + } + } + + /** + * This is the JavaScript that we use to automatically fill in the login + * form on login.launchpad.net + */ + private final static String SETUSERPASS_JS = "javascript:(function() { " + + "document.forms[0].elements[1].value = '%s'; " + + "document.forms[0].elements[2].value = '%s'; " + + "document.forms[0].submit(); " + + "})()"; + + /** not currently used */ + private static String onOpenidRedirect = "javascript:window.ANDROID.onOpenidRedirect()"; + /** used to get the HTML of the page we are actually looking for*/ + private static String onSummitLoggedIn = "javascript:window.ANDROID.onSummitLoggedIn(document.getElementsByTagName('body')[0].innerHTML)"; + +} diff --git a/src/org/linaro/connect/ScheduleActivity.java b/src/org/linaro/connect/ScheduleActivity.java index ac68232..7a229e3 100644 --- a/src/org/linaro/connect/ScheduleActivity.java +++ b/src/org/linaro/connect/ScheduleActivity.java @@ -20,6 +20,9 @@ import android.widget.TextView; public class ScheduleActivity extends Activity { + /** used to specify an alternate name for the cache in the intent*/ + public static final String INTENT_CACHENAME = "org.linaro.connect.CACHENAME"; + private static final int REFRESH_ID = Menu.FIRST + 1; private ScheduleAdapter mAdapter; @@ -89,6 +92,9 @@ public class ScheduleActivity extends Activity { if(idx > 0) url = url.substring(0, idx); + String cacheName = getIntent().getStringExtra(INTENT_CACHENAME); + if( cacheName != null ) + setCacheFileName(cacheName); setUrl(url); } |