Added basic structure for API calls, simple login call.

This commit is contained in:
Ryan Bateman 2012-06-26 15:38:17 -04:00
parent 41f8698163
commit 2f8494c3cb
14 changed files with 321 additions and 77 deletions

View file

@ -2,20 +2,26 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.newsblur"
android:versionCode="1"
android:versionName="0.0.1" >
android:versionName="0.0.2" >
<uses-sdk android:minSdkVersion="8" android:targetSdkVersion="15" />
<uses-sdk
android:minSdkVersion="8"
android:targetSdkVersion="15" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<application
android:icon="@drawable/logo"
android:label="@string/newsblur"
android:theme="@style/NewsBlurTheme">
android:theme="@style/NewsBlurTheme" >
<activity
android:name=".activity.Login"
android:label="@string/newsblur" >
<intent-filter>
<category android:name="android.intent.category.LAUNCHER" />
<action android:name="android.intent.action.MAIN" />
<action android:name="android.intent.action.MAIN" />
</intent-filter>
</activity>
<activity

View file

@ -4,15 +4,17 @@ import android.os.Bundle;
import android.support.v4.app.FragmentActivity;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentTransaction;
import android.util.Log;
import android.view.Window;
import com.newsblur.R;
import com.newsblur.fragment.LoginFragment;
public class Login extends FragmentActivity {
public class Login extends FragmentActivity implements LoginFragment.LoginFragmentInterface {
private FragmentManager fragmentManager;
private final static String currentTag = "currentFragment";
private static final String TAG = "LoginActivity";
@Override
protected void onCreate(Bundle savedInstanceState) {
@ -26,7 +28,17 @@ public class Login extends FragmentActivity {
transaction.add(R.id.login_container, new LoginFragment(), currentTag);
transaction.commit();
}
}
@Override
public void loginSuccessful() {
Log.d(TAG, "Login successful");
}
@Override
public void loginUnsuccessful() {
Log.d(TAG, "Login unsuccessful");
}
}

View file

@ -19,43 +19,43 @@ public class BlurDatabase extends SQLiteOpenHelper {
Log.d(TAG, "Initiating database");
}
private final String FOLDER_SQL = "CREATE TABLE " + Constants.FOLDER_TABLE + " (" +
Constants.FOLDER_ID + TEXT + ", " +
Constants.FOLDER_NAME + TEXT +
private final String FOLDER_SQL = "CREATE TABLE " + DatabaseConstants.FOLDER_TABLE + " (" +
DatabaseConstants.FOLDER_ID + TEXT + ", " +
DatabaseConstants.FOLDER_NAME + TEXT +
")";
private final String FEED_SQL = "CREATE TABLE " + Constants.FEED_TABLE + " (" +
Constants.FEED_ID + INTEGER + ", " +
Constants.FEED_ACTIVE + TEXT + ", " +
Constants.FEED_ADDRESS + TEXT + ", " +
Constants.FEED_FAVICON_COLOUR + TEXT + ", " +
Constants.FEED_FAVICON_FADE + TEXT + ", " +
Constants.FEED_LINK + TEXT + ", " +
Constants.FEED_SUBSCRIBERS + TEXT + ", " +
Constants.FEED_TITLE + TEXT + ", " +
Constants.FEED_UPDATED_SECONDS +
private final String FEED_SQL = "CREATE TABLE " + DatabaseConstants.FEED_TABLE + " (" +
DatabaseConstants.FEED_ID + INTEGER + ", " +
DatabaseConstants.FEED_ACTIVE + TEXT + ", " +
DatabaseConstants.FEED_ADDRESS + TEXT + ", " +
DatabaseConstants.FEED_FAVICON_COLOUR + TEXT + ", " +
DatabaseConstants.FEED_FAVICON_FADE + TEXT + ", " +
DatabaseConstants.FEED_LINK + TEXT + ", " +
DatabaseConstants.FEED_SUBSCRIBERS + TEXT + ", " +
DatabaseConstants.FEED_TITLE + TEXT + ", " +
DatabaseConstants.FEED_UPDATED_SECONDS +
")";
private final String STORY_SQL = "CREATE TABLE " + Constants.STORY_TABLE + " (" +
Constants.STORY_AUTHORS + TEXT + ", " +
Constants.STORY_CONTENT + TEXT + ", " +
Constants.STORY_DATE + TEXT + ", " +
Constants.STORY_FEED_ID + INTEGER + ", " +
Constants.STORY_ID + TEXT + ", " +
Constants.STORY_INTELLIGENCE_AUTHORS + INTEGER + ", " +
Constants.STORY_INTELLIGENCE_FEED + INTEGER + ", " +
Constants.STORY_INTELLIGENCE_TAGS + INTEGER + ", " +
Constants.STORY_INTELLIGENCE_TITLE + INTEGER + ", " +
Constants.STORY_PERMALINK + TEXT + ", " +
Constants.STORY_READ + TEXT + ", " +
Constants.STORY_TITLE + TEXT +
private final String STORY_SQL = "CREATE TABLE " + DatabaseConstants.STORY_TABLE + " (" +
DatabaseConstants.STORY_AUTHORS + TEXT + ", " +
DatabaseConstants.STORY_CONTENT + TEXT + ", " +
DatabaseConstants.STORY_DATE + TEXT + ", " +
DatabaseConstants.STORY_FEED_ID + INTEGER + ", " +
DatabaseConstants.STORY_ID + TEXT + ", " +
DatabaseConstants.STORY_INTELLIGENCE_AUTHORS + INTEGER + ", " +
DatabaseConstants.STORY_INTELLIGENCE_FEED + INTEGER + ", " +
DatabaseConstants.STORY_INTELLIGENCE_TAGS + INTEGER + ", " +
DatabaseConstants.STORY_INTELLIGENCE_TITLE + INTEGER + ", " +
DatabaseConstants.STORY_PERMALINK + TEXT + ", " +
DatabaseConstants.STORY_READ + TEXT + ", " +
DatabaseConstants.STORY_TITLE + TEXT +
")";
private final String CLASSIFIER_SQL = "CREATE TABLE " + Constants.CLASSIFIER_TABLE + " (" +
Constants.CLASSIFIER_ID + TEXT + ", " +
Constants.CLASSIFIER_KEY + TEXT + ", " +
Constants.CLASSIFIER_TYPE + TEXT + ", " +
Constants.CLASSIFIER_VALUE + TEXT +
private final String CLASSIFIER_SQL = "CREATE TABLE " + DatabaseConstants.CLASSIFIER_TABLE + " (" +
DatabaseConstants.CLASSIFIER_ID + TEXT + ", " +
DatabaseConstants.CLASSIFIER_KEY + TEXT + ", " +
DatabaseConstants.CLASSIFIER_TYPE + TEXT + ", " +
DatabaseConstants.CLASSIFIER_VALUE + TEXT +
")";
@Override

View file

@ -2,7 +2,7 @@ package com.newsblur.database;
import android.provider.BaseColumns;
public class Constants {
public class DatabaseConstants {
public static String FOLDER_TABLE = "folders";
public static String FOLDER_ID = BaseColumns._ID;

View file

@ -53,25 +53,25 @@ public class FeedProvider extends ContentProvider {
// Inserting a folder
case ALL_FOLDERS:
db.beginTransaction();
db.insert(Constants.FOLDER_TABLE, null, values);
db.insert(DatabaseConstants.FOLDER_TABLE, null, values);
db.setTransactionSuccessful();
db.endTransaction();
resultUri = uri.buildUpon().appendPath(values.getAsString(Constants.FOLDER_ID)).build();
resultUri = uri.buildUpon().appendPath(values.getAsString(DatabaseConstants.FOLDER_ID)).build();
break;
// Inserting a feed
case ALL_FEEDS:
db.beginTransaction();
db.insert(Constants.FEED_TABLE, null, values);
db.insert(DatabaseConstants.FEED_TABLE, null, values);
db.setTransactionSuccessful();
db.endTransaction();
resultUri = uri.buildUpon().appendPath(values.getAsString(Constants.FEED_ID)).build();
resultUri = uri.buildUpon().appendPath(values.getAsString(DatabaseConstants.FEED_ID)).build();
break;
// Inserting a story
case SPECIFIC_FEED:
db.beginTransaction();
db.insert(Constants.STORY_TABLE, null, values);
db.insert(DatabaseConstants.STORY_TABLE, null, values);
db.setTransactionSuccessful();
db.endTransaction();
break;
@ -96,13 +96,13 @@ public class FeedProvider extends ContentProvider {
switch (uriMatcher.match(uri)) {
// Inserting a feed
case ALL_FEEDS:
cursor = db.rawQuery(Constants.FEED_TABLE, null);
cursor = db.rawQuery(DatabaseConstants.FEED_TABLE, null);
break;
// Inserting a story
case SPECIFIC_FEED:
selection = Constants.FEED_ID + " = ?";
selection = DatabaseConstants.FEED_ID + " = ?";
selectionArgs = new String[] { uri.getLastPathSegment() };
cursor = db.query(Constants.FEED_TABLE, projection, selection, selectionArgs, null, null, sortOrder);
cursor = db.query(DatabaseConstants.FEED_TABLE, projection, selection, selectionArgs, null, null, sortOrder);
break;
}
return cursor;

View file

@ -1,6 +1,6 @@
package com.newsblur.domain;
import com.newsblur.database.Constants;
import com.newsblur.database.DatabaseConstants;
import android.content.ContentValues;
@ -9,39 +9,39 @@ public class Feed {
public final ContentValues values = new ContentValues();
public void setId(final String feedId) {
values.put(Constants.FEED_ID, feedId);
values.put(DatabaseConstants.FEED_ID, feedId);
}
public void setActive(final String active) {
values.put(Constants.FEED_ACTIVE, active);
values.put(DatabaseConstants.FEED_ACTIVE, active);
}
public void setAddress(final String address) {
values.put(Constants.FEED_ADDRESS, address);
values.put(DatabaseConstants.FEED_ADDRESS, address);
}
public void setFaviconColour(final String faviconColour) {
values.put(Constants.FEED_FAVICON_COLOUR, faviconColour);
values.put(DatabaseConstants.FEED_FAVICON_COLOUR, faviconColour);
}
public void setFaviconFade(final String faviconFade) {
values.put(Constants.FEED_FAVICON_FADE, faviconFade);
values.put(DatabaseConstants.FEED_FAVICON_FADE, faviconFade);
}
public void setLink(final String feedLink) {
values.put(Constants.FEED_LINK, feedLink);
values.put(DatabaseConstants.FEED_LINK, feedLink);
}
public void setSubscribers(final String feedSubscribers) {
values.put(Constants.FEED_SUBSCRIBERS, feedSubscribers);
values.put(DatabaseConstants.FEED_SUBSCRIBERS, feedSubscribers);
}
public void setTitle(final String title) {
values.put(Constants.FEED_TITLE, title);
values.put(DatabaseConstants.FEED_TITLE, title);
}
public void setLastUpdated(final String lastUpdated) {
values.put(Constants.FEED_UPDATED_SECONDS, lastUpdated);
values.put(DatabaseConstants.FEED_UPDATED_SECONDS, lastUpdated);
}
}

View file

@ -1,6 +1,6 @@
package com.newsblur.domain;
import com.newsblur.database.Constants;
import com.newsblur.database.DatabaseConstants;
import android.content.ContentValues;
@ -9,10 +9,10 @@ public class Folder {
public final ContentValues values = new ContentValues();
public void setName(final String name) {
values.put(Constants.FOLDER_NAME, name);
values.put(DatabaseConstants.FOLDER_NAME, name);
}
public void setId(final String id) {
values.put(Constants.FOLDER_ID, id);
values.put(DatabaseConstants.FOLDER_ID, id);
}
}

View file

@ -2,58 +2,58 @@ package com.newsblur.domain;
import android.content.ContentValues;
import com.newsblur.database.Constants;
import com.newsblur.database.DatabaseConstants;
public class Story {
public final ContentValues values = new ContentValues();
public void setId(final String id) {
values.put(Constants.STORY_ID, id);
values.put(DatabaseConstants.STORY_ID, id);
}
public void setTitle(final String title) {
values.put(Constants.STORY_TITLE, title);
values.put(DatabaseConstants.STORY_TITLE, title);
}
public void setDate(final String date) {
values.put(Constants.STORY_DATE, date);
values.put(DatabaseConstants.STORY_DATE, date);
}
public void setContent(final String content) {
values.put(Constants.STORY_CONTENT, content);
values.put(DatabaseConstants.STORY_CONTENT, content);
}
public void setPermalink(final String permalink) {
values.put(Constants.STORY_PERMALINK, permalink);
values.put(DatabaseConstants.STORY_PERMALINK, permalink);
}
public void setAuthors(final String authors) {
values.put(Constants.STORY_AUTHORS, authors);
values.put(DatabaseConstants.STORY_AUTHORS, authors);
}
public void setIntelligenceAuthors(final String authors) {
values.put(Constants.STORY_INTELLIGENCE_AUTHORS, authors);
values.put(DatabaseConstants.STORY_INTELLIGENCE_AUTHORS, authors);
}
public void setIntelligenceTags(final String tags) {
values.put(Constants.STORY_INTELLIGENCE_TAGS, tags);
values.put(DatabaseConstants.STORY_INTELLIGENCE_TAGS, tags);
}
public void setIntelligenceFeed(final String feed) {
values.put(Constants.STORY_INTELLIGENCE_FEED, feed);
values.put(DatabaseConstants.STORY_INTELLIGENCE_FEED, feed);
}
public void setIntelligenceTitle(final String title) {
values.put(Constants.STORY_INTELLIGENCE_TITLE, title);
values.put(DatabaseConstants.STORY_INTELLIGENCE_TITLE, title);
}
public void setRead(final String read) {
values.put(Constants.STORY_READ, read);
values.put(DatabaseConstants.STORY_READ, read);
}
public void setFeedId(final String feedId) {
values.put(Constants.STORY_FEED_ID, feedId);
values.put(DatabaseConstants.STORY_FEED_ID, feedId);
}
}

View file

@ -1,16 +1,81 @@
package com.newsblur.fragment;
import com.newsblur.R;
import android.os.AsyncTask;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.EditText;
public class LoginFragment extends Fragment {
import com.newsblur.R;
import com.newsblur.network.APIManager;
public class LoginFragment extends Fragment implements OnClickListener {
public APIManager apiManager;
private EditText username, password;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_login, container, false);
final View v = inflater.inflate(R.layout.fragment_login, container, false);
Button loginButton = (Button) v.findViewById(R.id.login_button);
loginButton.setOnClickListener(this);
username = (EditText) v.findViewById(R.id.login_username);
password = (EditText) v.findViewById(R.id.login_password);
return v;
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
apiManager = new APIManager(getActivity().getApplicationContext());
}
@Override
public void onClick(View viewClicked) {
switch (viewClicked.getId()) {
case R.id.login_button:
new LoginTask().execute(username.getText().toString(), password.getText().toString());
}
}
private class LoginTask extends AsyncTask<String, Void, Boolean> {
@Override
protected void onPreExecute() {
}
@Override
protected Boolean doInBackground(String... params) {
final String username = params[0];
final String password = params[1];
return apiManager.login(username, password);
}
@Override
protected void onPostExecute(Boolean result) {
if (result) {
((LoginFragmentInterface) getActivity()).loginSuccessful();
} else {
((LoginFragmentInterface) getActivity()).loginUnsuccessful();
}
}
}
// Interface for Host
public interface LoginFragmentInterface {
public void loginSuccessful();
public void loginUnsuccessful();
}
}

View file

@ -0,0 +1,115 @@
package com.newsblur.network;
import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.UnsupportedEncodingException;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.List;
import java.util.Map.Entry;
import java.util.Scanner;
import android.content.ContentValues;
import android.content.Context;
import android.text.TextUtils;
import android.util.Log;
public class APIClient {
private static final String TAG = "APIClient";
private Context context;
public APIClient(final Context context) {
this.context = context;
// enableHttpResponseCache();
}
public APIResponse get(final URL url) {
HttpURLConnection connection = null;
try {
connection = (HttpURLConnection) url.openConnection();
return extractResponse(url, connection);
} catch (IOException e) {
Log.d(TAG, "Error opening GET connection to " + url.toString(), e.getCause());
return new APIResponse();
} finally {
connection.disconnect();
}
}
private APIResponse extractResponse(final URL url, HttpURLConnection connection) throws IOException {
StringBuilder builder = new StringBuilder();
final Scanner scanner = new Scanner(connection.getInputStream());
while (scanner.hasNextLine()) {
builder.append(scanner.nextLine());
}
final APIResponse response = new APIResponse();
response.responseString = builder.toString();
response.responseCode = connection.getResponseCode();
response.hasRedirected = !TextUtils.equals(url.getHost(), connection.getURL().getHost());
return response;
}
public APIResponse post(final String urlString, final ContentValues values) {
HttpURLConnection connection = null;
List<String> parameters = new ArrayList<String>();
for (Entry<String, Object> entry : values.valueSet()) {
final StringBuilder builder = new StringBuilder();
builder.append((String) entry.getKey());
builder.append("=");
try {
builder.append(URLEncoder.encode((String) entry.getValue(), "UTF-8"));
} catch (UnsupportedEncodingException e) {
Log.d(TAG, "Unable to URLEncode a parameter in a POST");
builder.append((String) entry.getValue());
}
parameters.add(builder.toString());
}
final String parameterString = TextUtils.join("&", parameters);
try {
final URL url = new URL(urlString);
connection = (HttpURLConnection) url.openConnection();
connection.setDoOutput(true);
connection.setRequestMethod("POST");
connection.setFixedLengthStreamingMode(parameterString.getBytes().length);
connection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
final PrintWriter printWriter = new PrintWriter(connection.getOutputStream());
printWriter.print(parameterString);
printWriter.close();
return extractResponse(url, connection);
} catch (IOException e) {
Log.d(TAG, "Error opening POST connection to " + urlString + ": " + e.getLocalizedMessage(), e.getCause());
return new APIResponse();
} finally {
if (connection != null) {
connection.disconnect();
}
}
}
/*
* This method enables HTTP Response cache should the device support it.
* See Android Developer's Blog for more detail: http://android-developers.blogspot.ca/2011/09/androids-http-clients.html
*/
private void enableHttpResponseCache() {
Log.d(TAG, "Enabling HttpResponseCache");
try {
final long httpCacheSize = 10 * 1024 * 1024; //
File httpCacheDir = new File(context.getCacheDir(), "http");
if (httpCacheDir != null) {
Class.forName("android.net.http.HttpResponseCache").getMethod("install", File.class, long.class).invoke(null, httpCacheDir, httpCacheSize);
}
} catch (Exception httpResponseCacheNotAvailable) {
Log.d(TAG, "No HttpResponseCache available", httpResponseCacheNotAvailable.getCause());
}
}
}

View file

@ -0,0 +1,8 @@
package com.newsblur.network;
public class APIConstants {
public static final String URL_LOGIN = "http://newsblur.com/api/login";
public static final String USERNAME = "username";
public static final String PASSWORD = "password";
}

View file

@ -0,0 +1,28 @@
package com.newsblur.network;
import org.apache.http.HttpStatus;
import android.content.ContentValues;
import android.content.Context;
public class APIManager {
private Context context;
public APIManager(final Context context) {
this.context = context;
}
public boolean login(final String username, final String password) {
APIClient client = new APIClient(context);
final ContentValues values = new ContentValues();
values.put(APIConstants.USERNAME, username);
values.put(APIConstants.PASSWORD, password);
final APIResponse response = client.post(APIConstants.URL_LOGIN, values);
// Consider the login complete if we've got a HTTP 200 and we've not been redirected
// This could be made more granular depending on validity requirements.
return (response.responseCode == HttpStatus.SC_OK && response.hasRedirected == false);
}
}

View file

@ -0,0 +1,10 @@
package com.newsblur.network;
public class APIResponse {
public String responseString = "";
public int responseCode = -1;
public boolean hasRedirected;
}

View file

@ -7,7 +7,7 @@ import android.test.ProviderTestCase2;
import android.test.mock.MockContentResolver;
import com.newsblur.database.BlurDatabase;
import com.newsblur.database.Constants;
import com.newsblur.database.DatabaseConstants;
import com.newsblur.database.FeedProvider;
import com.newsblur.domain.Folder;
@ -41,7 +41,7 @@ public class FolderProviderTest extends ProviderTestCase2<FeedProvider> {
Uri uri = contentResolver.insert(FeedProvider.FOLDERS_URI, testFolder.values);
SQLiteDatabase db = dbHelper.getReadableDatabase();
Cursor rawQuery = db.rawQuery("SELECT * FROM " + Constants.FOLDER_TABLE, null);
Cursor rawQuery = db.rawQuery("SELECT * FROM " + DatabaseConstants.FOLDER_TABLE, null);
assertEquals(1, rawQuery.getCount());
assertEquals("1", uri.getLastPathSegment());