Due to high demand, I have attached the source project files for this little app! See here:
BothAsyncLogins
AsyncLogin Eclipse Project Files
The Newspapers’ Folly
To young Americans, newspapers are a dying medium. We don’t necessarily have a set of reasons – only a pragmatic recognition of the smartphone’s dominance in our personal and professional lives.
As a young professional, finding time to read a newspaper is rare – practically the only time being that calm period when everyone is grumbling about taxiing and waiting in line to depart prior to a flight. I (and I suspect many of my twenty-something business professional cohorts would also cop-out to after a bit of coaxing) relish this stopgap period. The stewardess asks if I would like a newspaper – and while I reluctantly accept – the back of my mind says ‘I wonder what these things are up to these days!’
Sure enough, the quality of the columns, the precision of argument, the scope of topic – all remains untouched by specialty news and blogs based solely or largely online. After only a five-minute period, perhaps two articles, I immediately realize that the only reason newspapers are dying medium? Because I won’t give them a second glance – because I won’t devote time to them. Not to labor the point, but at the same time I grumble about the lack of in-depth, solid, accountable, piercing journalism at the electronic sources I regularly visit – I actively ignore the medium through which these journalistic endeavors continue to persist, if slightly less powerfully than in past eras.
Yet newspapers find themselves playing to the same counter-productive dividing lines of American politics as any other news source, including online.
Two articles within the same page of the International Tribune, highlight this perfectly (watch as your political biases flash alive for the first time): Triumph of the wrong? by Paul Krugman and The generation war by David Brooks (let’s ignore the third useless dividing line – age). Krugman speaks of the folly of modern American politics in which the most contradictory views of the modern Republican Party ‘Keep your government hands off my Medicare!’ controls the pragmatic and reasonable elements of the Grand Old Party without ever mentioning the distinction and instead burns the bridges of compromise. In his own article, Brooks speaks as a Conservative who has openly mourned the loss of reasonable Republicans, and can even be heard in this very article saying, “He took it to Representative Paul Ryan on the inexplicability of the Republican tax plan.” Lest we be in-equal: Brooks takes his time to line up enough across the bow shots at the party with which he does not identify as well.
Two sides, speaking past each other. There are less-than-moderate elements to each party. Heck, there are more than two parties (a topic, like age, for another article). But only one party recently won a wave election, running nearly 130 Tea Party candidates. You don’t see democratic socialists (save Bernie) or Greens carrying the party mantle the way Eric Cantor enthusiastically supports and links the Tea Party agenda to that of the Republican Party, and you certainly don’t see the Arlen Spector’s of yesterday’s Republican Party any longer. Compromise is no longer possible.
Perhaps some of it is due to each party, perhaps some of the lack of compromise is due to feckless representatives refusing to have a drink, or inviting for a drink, a member across the aisle.
But to speak past each other in the public sector? We do not need the Democratic soapbox on the left-hand corner of the street and the Republican soapbox on the right-hand street corner. I send my representatives to Washington DC to vote and compromise. I do not send my representatives to fall over and give in to people with different principles and opinions. Something tells me Mr. Krugman and Mr. Brooks feel quite similar.
How shall we get back to a point where our representatives can work together, when the talking heads on the closest thing we have in America to the ‘Fourth Estate’ are too busy extolling opinion like this, “It will be the crowning irony of the No Drama Obama campaign that it took a man who exudes more drama than a decade of Latin American soap operas to get Democrats out of their funk” [Brooks]? While Krugman’s article at least is based on an IMF document, rather than solely opinion, in his standard dry humor, “ And what this analysis concludes is that a disproportionate share of the bad news is coming from countries pursuing the kind of austerity policies Republicans want to impose on America.”
Perhaps Paul and David would better serve American political discourse if they sat down, had a drink, and came up with something logical that they agree on and think can be done – then publish that, preferably with a random probability determining whose name is placed first.
Detecting If a User is Logged In; Detecting Other SQL Attributes
Detecting if the user is logged in is an important aspect of how the UI functions and what it should look like. Here’s a snippet of my AsyncTask that helps load questions from my SQL db:
protected Integer doInBackground(String... params) { DatabaseHandler dbHandler = activity.getDB(); questionList = userFunctions.getQuestions(category); for (int j = 0; j < questionList.length(); j++) { try {//run up the list of JSON objects //check if the user is logged in AND has answered the question previously, mark each JSON object appropriately if (userFunctions.hasUserAnswered(questionList.getJSONObject(j), dbHandler.getUserDetails().get("email"))) { questionList.getJSONObject(j).put("result", true); } else { questionList.getJSONObject(j).put("result", false); } } catch (JSONException e) { e.printStackTrace(); } } return questionList.length(); } protected void onPostExecute(Integer numQuestions) { for (int j = 0; j < questionList.length(); j++){ JSONObject question; try { question = questionList.getJSONObject(j); if (userFunctions.isUserLoggedIn(activity)) { DatabaseHandler dbHandler = activity.getDB(); //ensure again the user is logged in, then run the method to populate the question (second param determines how it will look on screen) fragment.buildQuestions(question, question.getBoolean("result")); } } catch (JSONException e) { e.printStackTrace(); } } progressRow.setVisibility(View.GONE); }
And here’s how buildQuestions() looks (remember to look at the second boolean parameter and how it’s used in conditionals inside the method):
public void buildQuestions(JSONObject question, boolean isAnswered) throws JSONException { if (isAnswered){ questionContainer = (TableLayout) layout.findViewById(R.id.questionContainer); View questionBox = inflater.inflate(R.layout.question, null); questionBox.setId(Integer.parseInt(question.getString("id"))); TextView title = (TextView) questionBox.findViewById(R.id.questionTextView); title.setText("Answered: " + question.getString("title")); Button chartsButton = (Button) questionBox.findViewById(R.id.chartsButton); chartsButton.setTag(question); Button submitButton = (Button) questionBox.findViewById(R.id.submitButton); chartsButton.setOnClickListener(chartsListener); //submitButton.setOnClickListener(submitListener); submitButton.setText("Submitted"); submitButton.setClickable(false); TableRow tr = (TableRow) questionBox; TableLayout.LayoutParams trParams = new TableLayout.LayoutParams( TableLayout.LayoutParams.MATCH_PARENT, TableLayout.LayoutParams.MATCH_PARENT); trParams.setMargins(leftMargin, topMargin, rightMargin, bottomMargin); //tr.setLayoutParams(trParams); tableList.add(tr); questionContainer.addView(tr); TagObj tagObj = new TagObj(question, radioGroup, tr); submitButton.setTag(tagObj); }else { questionContainer = (TableLayout) layout.findViewById(R.id.questionContainer); View questionBox = inflater.inflate(R.layout.question, null); questionBox.setId(Integer.parseInt(question.getString("id"))); TextView title = (TextView) questionBox.findViewById(R.id.questionTextView); title.setText(question.getString("title")); radioGroup = (RadioGroup) questionBox.findViewById(R.id.responseRadioGroup); String typeFromTable = question.getString("default"); int responseType = Integer.parseInt(typeFromTable); if (responseType == 1) { //populate default radio buttons Resources res = getResources(); String[] defaultAnswers = res.getStringArray(R.array.defaultAnswers); int j = 0; while (j < 5) { RadioButton rb = new RadioButton(activity); rb.setText(defaultAnswers[j]); rb.setId(j); radioGroup.addView(rb); j++; } } else if (responseType == 0) { for (int j = 0; j < 5; j++) { if (question.getString("answer" + Integer.toString(j)) != "null") { RadioButton rb; rb = new RadioButton(activity); rb.setId(j); rb.setText(question.getString("answer" + Integer.toString(j))); if (question.getString("answer" + Integer.toString(j)).length() != 0) { radioGroup.addView(rb); } } else { if (question.getString("answer" + Integer.toString(j)) == "null") { RadioButton rb; rb = new RadioButton(activity); rb.setText("null"); rb.setId(j);//may not work radioGroup.addView(rb); } } } } Button chartsButton = (Button) questionBox.findViewById(R.id.chartsButton); chartsButton.setTag(question); Button submitButton = (Button) questionBox.findViewById(R.id.submitButton); chartsButton.setOnClickListener(chartsListener); submitButton.setOnClickListener(submitListener); TableRow tr = (TableRow) questionBox; TableLayout.LayoutParams trParams = new TableLayout.LayoutParams( TableLayout.LayoutParams.MATCH_PARENT, TableLayout.LayoutParams.MATCH_PARENT); trParams.setMargins(leftMargin, topMargin, rightMargin, bottomMargin); //tr.setLayoutParams(trParams); tableList.add(tr); questionContainer.addView(tr); TagObj tagObj = new TagObj(question, radioGroup, tr); submitButton.setTag(tagObj); } }
Working AsyncLogin Eclipse Project
As per the request of a number of viewers of my previous tutorial, I’ve uploaded an example AsyncLogin project to help users see the big picture. You need change only two lines to get the registration/login to function with your own database:
private static String loginURL = "http://yourWebsite.net/your_Database/"; private static String registerURL = "http://yourWebsite.net/your_Database/";
Change those lines to represent the URL to your database (or http://localhost/your_Database).
Developing a login/register system for Android 4.0 – extending ASyncTask
EDIT~ EDIT~ Due to high demand, I have attached the source project files for this little app! See here:
BothAsyncLogins
First off, credit where it’s due. In the past couple of weeks, I tasked myself with completing the register and login systems for my upcoming Android app. After a couple of days of reading up on SQLite and the purely local (to the device) nature of this database structure, I determined that I would need to utilize a MySQL database with PHP scripts hosted from the same location in order to build a fully functioning system. Originally, I followed this tutorial to get the basic understanding: http://www.androidhive.info/2012/01/android-login-and-registration-with-php-mysql-and-sqlite/ by Ravi Tamada. Once I completed that, there was still more to understand, including how to make a login/register system that would be supported by Android 4.0 (by using ASyncTask). For that bit, I followed this tutorial: http://256design.com/blog/android-login-asynctask/ by Spencer.
My end result certainly shows the techniques employed by Ravi and Spencer, but the result is unique. So let’s go over what pieces you need to complete a similar login/register system.
On your localhost SQL dB/web-based SQL dB:
- Folder /android_api
- android_api/index.php
- Folder /android_api/include
- /include/config.php
- /include/DB_Connect.php
- /include/DB_Functions.php
Android Project Classes:
- LoginTask.java
- RegisterTask.java
- MainActivity.java
- Separate Package ‘library’
- Classes in library: DatabaseHandler.java, JSONParser.java, UserFunctions.java
I recognize that this is a fairly large amount of stuff, but it’s worth it. When you’ve completed this guide, you will have the ability to create a user account with a unique ID and encrypted password. So, let’s get started!
Step 1: Deploying the MySQL database
Enter PHPMyAdmin, either on your local server (EasyPHP is a great tool for this) or your webserver. Next, create a database named “android_api”. Then, under the SQL tab of that table on PHPMyAdmin, enter in this code:
create table USERS(
uid int(11) primary key auto_increment, unique_id varchar(23) not null unique, name varchar(50) not null, email varchar(100) not null unique, encrypted_password varchar(80) not null, salt varchar(10) not null, created_at datetime, updated_at datetime null);This bit of SQL code will create the USERS table as we will require it in the later steps of the tutorial. Here’s what it should look like when you’ve created the table:
Step 2: Building the PHP script folders
In the root directory of your localhost/web server, add a new folder, “android_api”. Within that folder, add another folder, “include”.
We will now construct all of the PHP scripts needed to communicate between the webserver and Android, with PHP being the go-between.
Index.php:
<?php /** * File to handle all API requests * Accepts GET and POST * * Each request will be identified by TAG * Response will be JSON data /** * check for POST request */ if (isset($_POST['tag']) && $_POST['tag'] != '') { // get tag $tag = $_POST['tag']; // include db handler require_once 'include/DB_Functions.php'; $db = new DB_Functions(); // response Array $response = array("tag" => $tag, "success" => 0, "error" => 0); // check for tag type if ($tag == 'login') { // Request type is check Login $email = $_POST['email']; $password = $_POST['password']; // check for user $user = $db->getUserByEmailAndPassword($email, $password); if ($user != false) { // user found // echo json with success = 1 $response["success"] = 1; $response["uid"] = $user["unique_id"]; $response["user"]["name"] = $user["name"]; $response["user"]["email"] = $user["email"]; $response["user"]["created_at"] = $user["created_at"]; $response["user"]["updated_at"] = $user["updated_at"]; echo json_encode($response); } else { // user not found // echo json with error = 0 $response["error"] = 1; $response["error_msg"] = "Incorrect email or password!"; echo json_encode($response); } } else if ($tag == 'register') { // Request type is Register new user $name = $_POST['name']; $email = $_POST['email']; $password = $_POST['password']; // check if user is already existed if ($db->isUserExisted($email)) { // user is already existed - error response $response["error"] = 2; $response["error_msg"] = "User already existed"; echo json_encode($response); } else { // store user $user = $db->storeUser($name, $email, $password); if ($user) { // user stored successfully $response["success"] = 1; $response["uid"] = $user["unique_id"]; $response["user"]["name"] = $user["name"]; $response["user"]["email"] = $user["email"]; $response["user"]["created_at"] = $user["created_at"]; $response["user"]["updated_at"] = $user["updated_at"]; echo json_encode($response); } else { // user failed to store $response["error"] = 1; $response["error_msg"] = "Error occured in Registartion"; echo json_encode($response); } } } else { echo "Invalid Request"; } } else { echo "Access Denied"; } ?>
Next up is Config.php, and this is where many folks may trip-up a bit. I suggest you look at the above image displaying the Database’s table. See how it says localhost -> frehud_android_api? The frehud_ is an added prefix due to my host. You should make sure to check to see if your situation is similar, if so, make sure to specify it wherever it is needed (in the case of my server, user accounts are prefixed xx_account and the database is as well: xx_databaseName:
<?php define("DB_HOST", "localhost"); define("DB_USER", "yourUser"); define("DB_PASSWORD", "yourPass"); define("DB_DATABASE", "yourDbName"); ?>
DBConnect.php:
<?php class DB_Connect { // constructor function __construct() { } // destructor function __destruct() { // $this->close(); } // Connecting to database public function connect() { require_once 'config.php'; // connecting to mysql $con = mysql_connect(DB_HOST, DB_USER, DB_PASSWORD); // selecting database mysql_select_db(DB_DATABASE); // return database handler return $con; } // Closing database connection public function close() { mysql_close(); } } ?>
DB_Functions.php:
db = new DB_Connect();
$this->db->connect();
}
// destructor
function __destruct() {
}
/**
* Storing new user
* returns user details
*/
public function storeUser($name, $email, $password) {
$uuid = uniqid('', true);
$hash = $this->hashSSHA($password);
$encrypted_password = $hash["encrypted"]; // encrypted password
$salt = $hash["salt"]; // salt
$result = mysql_query("INSERT INTO USERS(unique_id, name, email, encrypted_password, salt, created_at) VALUES('$uuid', '$name', '$email', '$encrypted_password', '$salt', NOW())");
// check for successful store
if ($result) {
// get user details
$uid = mysql_insert_id(); // last inserted id
$result = mysql_query("SELECT * FROM USERS WHERE uid = $uid");
// return user details
return mysql_fetch_array($result);
} else {
return false;
}
}
/**
* Get user by email and password
*/
public function getUserByEmailAndPassword($email, $password) {
$result = mysql_query("SELECT * FROM USERS WHERE email = '$email'") or die(mysql_error());
// check for result
$no_of_rows = mysql_num_rows($result);
if ($no_of_rows > 0) {
$result = mysql_fetch_array($result);
$salt = $result['salt'];
$encrypted_password = $result['encrypted_password'];
$hash = $this->checkhashSSHA($salt, $password);
// check for password equality
if ($encrypted_password == $hash) {
// user authentication details are correct
return $result;
}
} else {
// user not found
return false;
}
}
/**
* Check user is existed or not
*/
public function isUserExisted($email) {
$result = mysql_query("SELECT email from USERS WHERE email = '$email'");
$no_of_rows = mysql_num_rows($result);
if ($no_of_rows > 0) {
// user existed
return true;
} else {
// user not existed
return false;
}
}
/**
* Encrypting password
* @param password
* returns salt and encrypted password
*/
public function hashSSHA($password) {
$salt = sha1(rand());
$salt = substr($salt, 0, 10);
$encrypted = base64_encode(sha1($password . $salt, true) . $salt);
$hash = array("salt" => $salt, "encrypted" => $encrypted);
return $hash;
}
/**
* Decrypting password
* @param salt, password
* returns hash string
*/
public function checkhashSSHA($salt, $password) {
$hash = base64_encode(sha1($password . $salt, true) . $salt);
return $hash;
}
}
?>
Step 3: Android Project/Classes
We now can begin the work of developing the Android Program. You will need to develop a class and corresponding login file that plays host to a Email EditText and Password EditText. How you design this screen is up to you and what your program should look like. As such, I will focus on the AsyncTask classes first.
Each AsyncTask can use up to three methods. onPreExecute(), doInBackground, onPostExecute(). So how does the AsyncTask get used in Android? Say the user clicks a login button in your main activity/login fragment. An OnClickListener for that button runs the following methods:
ProgressDialog progressDialog = new ProgressDialog(getActivity()); progressDialog.setMessage("Logging in..."); LoginTask loginTask = new LoginTask(getActivity(), progressDialog); loginTask.execute();
ProgressDialog is created to show the user that the task of connecting to and logging in with the server is underway. When we construct a new loginTask on line 3, we pass it the main activity (in this case we use getActivity() because the class we are working in is actually a fragment: if the class is simply an activity, you can change getActivity() to this), as well as the progressDialog. We then tell the loginTask to execute() – which begins the AsyncTask by running onPreExecute().
public class LoginTask extends AsyncTask { private ProgressDialog progressDialog; private Polling activity; private JSONParser jsonParser; private static String loginURL = "http://davidjkelley.net/android_api/"; private static String registerURL = "http://davidjkelley.net/android_api/"; private static String KEY_SUCCESS = "success"; private static String KEY_ERROR = "error"; private static String KEY_ERROR_MSG = "error_msg"; private static String KEY_UID = "uid"; private static String KEY_NAME = "name"; private static String KEY_EMAIL = "email"; private static String KEY_CREATED_AT = "created_at"; private int responseCode = 0; public LoginTask(Polling activity, ProgressDialog progressDialog) { this.activity = activity; this.progressDialog = progressDialog; }
As you can see in the constructor above, public LoginTask(…) {} we have two parameters, Polling activity, ProgressDialog progressDialog that both immediately are employed by linking the class-wide fields, lines 2-3, to their respective parameters. This allows the other methods in AsyncTask to access all the necessary components.
As I mentioned, when we run execute(), the AsyncTask actually runs onPreExecute(). Here is that method:
protected void onPreExecute() { progressDialog.show(); }
Very simple and straightforward: this method places the progressDialog on the screen and doInBackground is automatically launched.
doInBackground() is where the majority of the work is done. Let’s look at the code:
protected Integer doInBackground(String... arg0) { EditText userName = (EditText)activity.findViewById(R.id.emailEditText); EditText passwordEdit = (EditText)activity.findViewById(R.id.passEditText); String email = userName.getText().toString(); String password = passwordEdit.getText().toString(); UserFunctions userFunction = new UserFunctions(); JSONObject json = userFunction.loginUser(email, password); // check for login response try { if (json.getString(KEY_SUCCESS) != null) { String res = json.getString(KEY_SUCCESS); if(Integer.parseInt(res) == 1){ //user successfully logged in // Store user details in SQLite Database DatabaseHandler db = new DatabaseHandler(activity.getApplicationContext()); JSONObject json_user = json.getJSONObject("user"); // Clear all previous data in database userFunction.logoutUser(activity.getApplicationContext()); db.addUser(json_user.getString(KEY_NAME), json_user.getString(KEY_EMAIL), json.getString(KEY_UID), json_user.getString(KEY_CREATED_AT)); //Login Success responseCode = 1; }else{ responseCode = 0; //Error in login } } } catch (NullPointerException e) { e.printStackTrace(); } catch (JSONException e) { e.printStackTrace(); } return responseCode; }
And finally, there is onPostExecute(). This method sends the result of doInBackground back to your login/main class/activity or fragment.
protected void onPostExecute(Integer responseCode) { EditText userName = (EditText)activity.findViewById(R.id.emailEditText); EditText passwordEdit = (EditText)activity.findViewById(R.id.passEditText); if (responseCode == 1) { //return a successful login progressDialog.dismiss(); activity.loginReport(responseCode); userName.setText(""); passwordEdit.setText(""); //shared prefences, store name } if (responseCode == 0) { //return an error in login progressDialog.dismiss(); activity.loginReport(responseCode); } }
And rather than the step by step, let’s look at RegisterTask.java in full. And remember, it’s called in the exact same way as LoginTask was up above, with the change being the class/variable name being RegisterTask/registerTask rather than LoginTask/loginTask. Here’s the full class:
package com.davekelley.polling; import java.io.BufferedReader; import java.io.InputStreamReader; import java.util.ArrayList; import java.util.List; import library.DatabaseHandler; import library.JSONParser; import library.UserFunctions; import org.apache.http.HttpResponse; import org.apache.http.NameValuePair; import org.apache.http.client.HttpClient; import org.apache.http.client.entity.UrlEncodedFormEntity; import org.apache.http.client.methods.HttpPost; import org.apache.http.impl.client.DefaultHttpClient; import org.apache.http.message.BasicNameValuePair; import org.json.JSONException; import org.json.JSONObject; import com.actionbarsherlock.R; import android.app.ProgressDialog; import android.content.Context; import android.content.Intent; import android.os.AsyncTask; import android.support.v4.app.FragmentActivity; import android.util.Log; import android.view.ViewGroup; import android.widget.Button; import android.widget.EditText; import android.widget.TextView; import android.widget.Toast; public class RegisterTask extends AsyncTask { private ProgressDialog progressDialog; private Polling activity; private int id = -1; private JSONParser jsonParser; private static String loginURL = "http://davidjkelley.net/android_api/"; private static String registerURL = "http://davidjkelley.net/android_api/"; private static String KEY_SUCCESS = "success"; private static String KEY_ERROR = "error"; private static String KEY_ERROR_MSG = "error_msg"; private static String KEY_UID = "uid"; private static String KEY_NAME = "name"; private static String KEY_EMAIL = "email"; private static String KEY_CREATED_AT = "created_at"; //TextView loginErrorMsg = (EditText)activity.findViewById(R.id.loginErrorMsg); private int responseCode = 0; public RegisterTask(Polling activity, ProgressDialog progressDialog){ this.activity = activity; this.progressDialog = progressDialog; } @Override protected void onPreExecute(){ progressDialog.show(); } @Override protected Integer doInBackground(String... arg0) { EditText userName = (EditText)activity.findViewById(R.id.emailEditText); EditText passwordEdit = (EditText)activity.findViewById(R.id.passEditText); String email = userName.getText().toString(); String password = passwordEdit.getText().toString(); Log.v(email, password); UserFunctions userFunction = new UserFunctions(); JSONObject json = userFunction.registerUser( email, password); // check for login response try { if (json.getString(KEY_SUCCESS) != null) { //registerErrorMsg.setText(""); String res = json.getString(KEY_SUCCESS); if(Integer.parseInt(res) == 1){ // user successfully registred // Store user details in SQLite Database DatabaseHandler db = new DatabaseHandler(activity.getApplicationContext()); JSONObject json_user = json.getJSONObject("user"); // Clear all previous data in database userFunction.logoutUser(activity.getApplicationContext()); db.addUser(json_user.getString(KEY_NAME), json_user.getString(KEY_EMAIL), json.getString(KEY_UID), json_user.getString(KEY_CREATED_AT)); //successful registration responseCode = 1; }else{ // Error in registration responseCode = 0; } } } catch (JSONException e) { e.printStackTrace(); } return responseCode; } @Override protected void onPostExecute(Integer responseCode){ EditText userName = (EditText)activity.findViewById(R.id.emailEditText); EditText passwordEdit = (EditText)activity.findViewById(R.id.passEditText); String s = userName.getText().toString(); if (responseCode == 1) { progressDialog.dismiss(); activity.registerReport(responseCode, s); userName.setText(""); passwordEdit.setText(""); } if (responseCode == 0) { progressDialog.dismiss(); activity.registerReport(responseCode, null); } } }
Don’t forget that there are a number of other classes utilized in this project, such as JSONParser, DatabaseHandler, and UserFunctions. These classes were created by Ravi Tamada in his excellent tutorial, and you can find the source in the opening paragraph of this post. If all goes well, between following the two above tutorials, and looking at my implementation of AsyncTask, you should have a fully functioning login system to a web-based SQL database right in your very own Android App.
EDIT~ EDIT~ Due to high demand, I have attached the source project files for this little app! See here:
BothAsyncLogins
Fully Functioning ViewPager + TabListener for Android 4.0 (ICS) ActionBar Tabs
I spent about two weeks trying to develop a system of tabs for Android based on the ActionBar as can be seen in ICS’ People, GMail, Play Music, and Play Store. Google has said that this is the future of Android’s UI paradigm. Without further ado, here’s my implementation:
import java.util.ArrayList; import android.app.ActionBar; import android.app.ActionBar.Tab; import android.content.Context; import android.content.Intent; import android.os.Bundle; import android.util.Log; import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; import android.view.MenuItem.OnMenuItemClickListener; import android.view.View; import android.widget.Button; import android.widget.EditText; import android.widget.TextView; import android.support.v4.app.Fragment; import android.support.v4.app.FragmentActivity; import android.support.v4.app.FragmentManager; import android.support.v4.app.FragmentPagerAdapter; import android.support.v4.app.FragmentTransaction; import android.support.v4.view.ViewPager; import android.support.v4.view.ViewPager.OnPageChangeListener; public class Polling extends FragmentActivity { private ViewPager mViewPager; private TabsAdapter mTabsAdapter; private final static String TAG = "21st Polling:"; public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); mViewPager = new ViewPager(this); mViewPager.setId(R.id.pager); setContentView(mViewPager); final ActionBar bar = getActionBar(); bar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS); bar.setDisplayShowTitleEnabled(false); bar.setDisplayShowHomeEnabled(false); mTabsAdapter = new TabsAdapter(this, mViewPager); mTabsAdapter.addTab(bar.newTab().setText(R.string.login), LoginFragment.class, null); mTabsAdapter.addTab(bar.newTab().setText(R.string.economics), EconFragment.class, null); mTabsAdapter.addTab(bar.newTab().setText(R.string.elections), ElectionsFragment.class, null); mTabsAdapter.addTab(bar.newTab().setText(R.string.politics), PoliticsFragment.class, null); mTabsAdapter.addTab(bar.newTab().setText(R.string.science), ScienceFragment.class, null); mTabsAdapter.addTab(bar.newTab().setText(R.string.finance), FinanceFragment.class, null); mTabsAdapter.addTab(bar.newTab().setText(R.string.religion), ReligionFragment.class, null); mTabsAdapter.addTab(bar.newTab().setText(R.string.military), MilitaryFragment.class, null); mTabsAdapter.addTab(bar.newTab().setText(R.string.international), InternationalFragment.class, null); }
And here’s the implementation of a subclass that both manages the ViewPager and sets up the TabListener:
public static class TabsAdapter extends FragmentPagerAdapter implements ActionBar.TabListener, ViewPager.OnPageChangeListener { private final Context mContext; private final ActionBar mActionBar; private final ViewPager mViewPager; private final ArrayList<TabInfo> mTabs = new ArrayList<TabInfo>(); static final class TabInfo { private final Class<?> clss; private final Bundle args; TabInfo(Class<?> _class, Bundle _args) { clss = _class; args = _args; } } public TabsAdapter(FragmentActivity activity, ViewPager pager) { super(activity.getSupportFragmentManager()); mContext = activity; mActionBar = activity.getActionBar(); mViewPager = pager; mViewPager.setAdapter(this); mViewPager.setOnPageChangeListener(this); } public void addTab(ActionBar.Tab tab, Class<?> clss, Bundle args) { TabInfo info = new TabInfo(clss, args); tab.setTag(info); tab.setTabListener(this); mTabs.add(info); mActionBar.addTab(tab); notifyDataSetChanged(); } public int getCount() { return mTabs.size(); } public Fragment getItem(int position) { TabInfo info = mTabs.get(position); return Fragment.instantiate(mContext, info.clss.getName(), info.args); } public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { } public void onPageSelected(int position) { mActionBar.setSelectedNavigationItem(position); } public void onPageScrollStateChanged(int state) { } public void onTabSelected(Tab tab, FragmentTransaction ft) { mViewPager.setCurrentItem(tab.getPosition()); Log.v(TAG, "clicked"); Object tag = tab.getTag(); for (int i=0; i<mTabs.size(); i++) { if (mTabs.get(i) == tag) { mViewPager.setCurrentItem(i); } } } public void onTabUnselected(Tab tab, FragmentTransaction ft) {} public void onTabReselected(Tab tab, FragmentTransaction ft) {} public void onTabReselected(Tab tab, android.app.FragmentTransaction ft) {} @Override public void onTabSelected(Tab tab, android.app.FragmentTransaction ft) { Object tag = tab.getTag(); for (int i=0; i<mTabs.size(); i++) { if (mTabs.get(i) == tag) { mViewPager.setCurrentItem(i); } } } public void onTabUnselected(Tab tab, android.app.FragmentTransaction ft) {} }
SQL-based user account system
Developing a system to log in and log out of an Android application (and with any future versions of 21st Century Polling, such as Browser or iOS) requires an implementation of an SQL database hosted online. Today, the first bit of success came my way in terms of implementing this system. Unfortunately, the code presented below runs on the UI thread. The next step (and my current project) will be converting this code and slimming it down to run as an AsyncTask.
import java.util.ArrayList; import java.util.List; import org.apache.http.NameValuePair; import org.apache.http.message.BasicNameValuePair; import org.json.JSONObject; import android.content.Context; public class UserFunctions { private JSONParser jsonParser; private static String loginURL = "http://davidjkelley.net/android_api/"; private static String registerURL = "http://davidjkelley.net/android_api/"; private static String login_tag = "login"; private static String register_tag = "register"; // constructor public UserFunctions(){ jsonParser = new JSONParser(); } //login with user provided email/pass public JSONObject loginUser(String email, String password){ // Building Parameters List params = new ArrayList(); params.add(new BasicNameValuePair("tag", login_tag)); params.add(new BasicNameValuePair("email", email)); params.add(new BasicNameValuePair("password", password)); JSONObject json = jsonParser.getJSONFromUrl(loginURL, params); // return json return json; } //register a new user with name/email/pass public JSONObject registerUser(String name, String email, String password){ // Building Parameters List params = new ArrayList(); params.add(new BasicNameValuePair("tag", register_tag)); params.add(new BasicNameValuePair("name", name)); params.add(new BasicNameValuePair("email", email)); params.add(new BasicNameValuePair("password", password)); // getting JSON Object JSONObject json = jsonParser.getJSONFromUrl(registerURL, params); // return json return json; } //determine if the user is logged in public boolean isUserLoggedIn(Context context){ DatabaseHandler db = new DatabaseHandler(context); int count = db.getRowCount(); if(count > 0){ // user logged in return true; } return false; } //logout the user public boolean logoutUser(Context context){ DatabaseHandler db = new DatabaseHandler(context); db.resetTables(); return true; } }
