{"id":1693,"date":"2024-04-26T09:49:28","date_gmt":"2024-04-26T04:19:28","guid":{"rendered":"http:\/\/codetheory.in\/?p=1693---7edb7eae-fdfe-4696-b223-8a734d6b170a"},"modified":"2024-04-26T09:49:28","modified_gmt":"2024-04-26T04:19:28","slug":"android-login-signup-with-parse","status":"publish","type":"post","link":"https:\/\/codetheory.in\/android-login-signup-with-parse\/","title":{"rendered":"Building a Single Integrated Registration and Login System on Android with Parse.com"},"content":{"rendered":"
Update:<\/strong> Parse is shutting down<\/a>. So you should start migrating.<\/p>\n In this article we’ll build a login screen which will be somewhat similar to whatsapp’s login screen. So the login and registration screens will actually be the same and the unique identifier for each user will be their phone numbers. So to quickly summarize, here are the fields we’ll have:<\/p>\n <\/p>\n As the backend, we won’t build our custom solution from scratch but leverage Parse.com<\/a> to store all the user details. Parse’s SDK will help us with logging in and signing up the user as well as with other features like logout by maintaining the session on disk.<\/p>\n Let’s quickly install and setup parse for our application with the following steps:<\/p>\n Note:<\/strong> We’ll need the You can test the library with a simple piece of code:<\/p>\n This code creates a new object of class Parse is up and running so we should start coding the user interface and functionality into our app. We’ll start off with a very basic UI for our login cum registration\/signup screen.<\/p>\n Nothing complicated, all very simple XML objects for our different views. One interesting thing to notice is the Here are the different input types we’ve used:<\/p>\n Since we’re done with defining our views, we’ll move onto writing the code in our In the This is the flow of the queries made to Parse logically:<\/p>\n If you notice, I’ve used 2 intent flags in the Those flags help with eliminating the Let me enumerate a couple of strange decisions taken in the code or some optimization that could be done or standards followed for the better:<\/p>\n To make sure that the user is always logged in before using the app, we can add this piece of code to redirect the user to the login screen (when he launches the app) in the “main” activity’s This is how the We can add a logout button to the overflow menu that’ll execute this piece of code in the Easy peasy!<\/p>\n We discussed how to make a single screen to server login’s and registration’s purpose. It’s always good, safe and secure to validate the user on signup like sending him an email to activate his account. Since we don’t ask the user for his email but phone number, what we could do is send him an SMS with an activation code that he’ll need to put in the signup flow after submitting the form. I’ll discuss how to do that in the next post.<\/p>\n","protected":false},"excerpt":{"rendered":" Update: Parse is shutting down. So you should start migrating. In this article we’ll build a login screen which will be somewhat similar to whatsapp’s login screen. So the login and registration screens will actually be the same and the unique identifier for each user will be their phone numbers. So to quickly summarize, here … Continue reading “Building a Single Integrated Registration and Login System on Android with Parse.com”<\/span><\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[121],"tags":[105,123,89,122,125,124],"_links":{"self":[{"href":"https:\/\/codetheory.in\/wp-json\/wp\/v2\/posts\/1693"}],"collection":[{"href":"https:\/\/codetheory.in\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/codetheory.in\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/codetheory.in\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/codetheory.in\/wp-json\/wp\/v2\/comments?post=1693"}],"version-history":[{"count":17,"href":"https:\/\/codetheory.in\/wp-json\/wp\/v2\/posts\/1693\/revisions"}],"predecessor-version":[{"id":2288,"href":"https:\/\/codetheory.in\/wp-json\/wp\/v2\/posts\/1693\/revisions\/2288"}],"wp:attachment":[{"href":"https:\/\/codetheory.in\/wp-json\/wp\/v2\/media?parent=1693"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/codetheory.in\/wp-json\/wp\/v2\/categories?post=1693"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/codetheory.in\/wp-json\/wp\/v2\/tags?post=1693"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}\n
Installing and Setting Up Parse<\/h2>\n
\n
Parse-*.jar<\/code> file to the `libs` directory of your project.<\/li>\n
Parse.initialize(context, PARSE_APPLICATION_ID, PARSE_CLIENT_KEY)<\/code> once to set our application ID and client key. We can do this in the
onCreate<\/code> method of our Application<\/a> class. Here’s<\/a> a really good article that
\nexplains what an Application class is and how to set it up.<\/li>\nINTERNET<\/code> and
ACCESS_NETWORK_STATE<\/code> permissions, hence add this in the
AndroidManifest.xml<\/code> file before the
<application><\/code> tag:<\/p>\n
\r\n<uses-permission android:name="android.permission.INTERNET" \/>\r\n<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" \/>\r\n<\/pre>\n
\r\nParseObject testObject = new ParseObject("TestObject");\r\ntestObject.put("foo", "bar");\r\ntestObject.saveInBackground();\r\n<\/pre>\n
TestObject<\/code> (think of it as a Database Table or Collection) and saves a new row with the value of “bar” under the “foo” column. You can either click the “Test” button on the Parse SDK installation page or examine the data in the Parse Data Browser.<\/p>\n
Coding into the Project<\/h2>\n
\r\n<RelativeLayout xmlns:android="http:\/\/schemas.android.com\/apk\/res\/android"\r\n xmlns:tools="http:\/\/schemas.android.com\/tools"\r\n android:layout_width="match_parent"\r\n android:layout_height="match_parent"\r\n android:paddingLeft="@dimen\/activity_horizontal_margin"\r\n android:paddingRight="@dimen\/activity_horizontal_margin"\r\n android:paddingTop="@dimen\/activity_vertical_margin"\r\n android:paddingBottom="@dimen\/activity_vertical_margin"\r\n tools:context="com.pycitup.pyc.LoginActivity">\r\n\r\n <EditText\r\n android:layout_width="match_parent"\r\n android:layout_height="wrap_content"\r\n android:id="@+id\/firstName"\r\n android:layout_alignParentTop="true"\r\n android:layout_alignParentLeft="true"\r\n android:layout_alignParentStart="true"\r\n android:hint="First Name"\r\n android:inputType="textCapWords" \/>\r\n\r\n <EditText\r\n android:layout_width="match_parent"\r\n android:layout_height="wrap_content"\r\n android:id="@+id\/lastName"\r\n android:layout_below="@+id\/firstName"\r\n android:layout_alignParentLeft="true"\r\n android:layout_alignParentStart="true"\r\n android:hint="Last Name"\r\n android:inputType="textCapWords" \/>\r\n\r\n <Spinner\r\n android:layout_width="match_parent"\r\n android:layout_height="wrap_content"\r\n android:id="@+id\/country"\r\n android:layout_below="@+id\/lastName"\r\n android:layout_alignParentLeft="true"\r\n android:layout_alignParentStart="true" \/>\r\n\r\n <TextView\r\n android:layout_width="wrap_content"\r\n android:layout_height="wrap_content"\r\n android:text="+"\r\n android:id="@+id\/plusSign"\r\n android:layout_alignTop="@+id\/countryCode"\r\n android:layout_alignParentLeft="true"\r\n android:layout_alignParentStart="true"\r\n android:layout_alignBottom="@+id\/countryCode"\r\n android:layout_marginTop="10dp"\r\n \/>\r\n\r\n <EditText\r\n android:layout_width="wrap_content"\r\n android:layout_height="wrap_content"\r\n android:id="@+id\/countryCode"\r\n android:width="50dp"\r\n android:hint="1"\r\n android:layout_below="@+id\/country"\r\n android:layout_toRightOf="@+id\/plusSign"\r\n android:inputType="number"\r\n \/>\r\n\r\n <EditText\r\n android:layout_width="wrap_content"\r\n android:layout_height="wrap_content"\r\n android:id="@+id\/phoneNumber"\r\n android:hint="phone number"\r\n android:layout_below="@+id\/country"\r\n android:layout_alignRight="@+id\/country"\r\n android:layout_alignEnd="@+id\/country"\r\n android:layout_toRightOf="@+id\/countryCode"\r\n android:inputType="phone" \/>\r\n\r\n <Button\r\n android:layout_width="match_parent"\r\n android:layout_height="wrap_content"\r\n android:text="Login"\r\n android:id="@+id\/loginButton"\r\n android:layout_centerVertical="true"\r\n android:layout_alignParentLeft="true"\r\n android:layout_alignParentStart="true" \/>\r\n\r\n<\/RelativeLayout>\r\n<\/pre>\n
android:inputType<\/code> XML attribute. This attribute defines the content type of the text held by the
Editable<\/code> object. It can hold multiple values separated by the bitwise OR (|) operator. For a comprehensive list of content types, check here<\/a>.<\/p>\n
\n
Activity<\/code> file that’ll deal with the functionality (including communication with Parse). So here’s the code that should go into a new Activity file called
LoginActivity<\/code>.<\/p>\n
\r\nimport android.app.Activity;\r\nimport android.app.AlertDialog;\r\nimport android.content.Intent;\r\nimport android.os.Bundle;\r\nimport android.view.Menu;\r\nimport android.view.MenuItem;\r\nimport android.view.View;\r\nimport android.widget.ArrayAdapter;\r\nimport android.widget.Button;\r\nimport android.widget.EditText;\r\nimport android.widget.Spinner;\r\n\r\nimport com.parse.FindCallback;\r\nimport com.parse.LogInCallback;\r\nimport com.parse.ParseException;\r\nimport com.parse.ParseQuery;\r\nimport com.parse.ParseUser;\r\nimport com.parse.SignUpCallback;\r\n\r\nimport java.util.ArrayList;\r\nimport java.util.List;\r\n\r\n\r\npublic class LoginActivity extends Activity {\r\n\r\n @Override\r\n protected void onCreate(Bundle savedInstanceState) {\r\n super.onCreate(savedInstanceState);\r\n setContentView(R.layout.activity_login);\r\n\r\n \/\/ All the views from our login form\r\n final EditText firstNameView = (EditText) findViewById(R.id.firstName);\r\n final EditText lastNameView = (EditText) findViewById(R.id.lastName);\r\n final Spinner countryView = (Spinner) findViewById(R.id.country);\r\n final EditText countryCodeView = (EditText) findViewById(R.id.countryCode);\r\n final EditText phoneNumberView = (EditText) findViewById(R.id.phoneNumber);\r\n Button loginButtonView = (Button) findViewById(R.id.loginButton);\r\n\r\n \/\/ Set items for the Spinner dropdown\r\n ArrayList<String> countries = new ArrayList<String>();\r\n countries.add("Australia");\r\n countries.add("Brazil");\r\n countries.add("China");\r\n countries.add("Canada");\r\n countries.add("India");\r\n countries.add("Russia");\r\n countries.add("Singapore");\r\n countries.add("United States");\r\n\r\n \/\/ Create the adapter for the spinner\r\n ArrayAdapter adapter = new ArrayAdapter(this, android.R.layout.simple_spinner_item, countries);\r\n adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);\r\n\r\n \/\/ Attach the adapter to the spinner\r\n countryView.setAdapter(adapter);\r\n\r\n\r\n \/\/ On login button click\r\n loginButtonView.setOnClickListener(new View.OnClickListener() {\r\n @Override\r\n public void onClick(View v) {\r\n\r\n \/\/ Get the values of all the form fields\r\n\r\n final String phoneNumber = phoneNumberView.getText().toString().trim();\r\n String firstName = firstNameView.getText().toString().trim();\r\n String lastName = lastNameView.getText().toString().trim();\r\n String countryCode = countryCodeView.getText().toString().trim();\r\n String country = countryView.getSelectedItem().toString().trim();\r\n\r\n \/\/ Simple validation: if any field is empty then don't let the form submit\r\n \/\/ and show an alert dialog with error message\r\n if (phoneNumber.isEmpty() || firstName.isEmpty() || lastName.isEmpty() || countryCode.isEmpty() || country.isEmpty()) {\r\n AlertDialog.Builder builder = new AlertDialog.Builder(LoginActivity.this);\r\n builder.setMessage("Please make sure you entered all the fields correctly.")\r\n .setTitle("Oops!")\r\n .setPositiveButton(android.R.string.ok, null);\r\n AlertDialog dialog = builder.create();\r\n dialog.show();\r\n\r\n return;\r\n }\r\n\r\n \/\/ Create a ParseUser object to create a new user\r\n final ParseUser user = new ParseUser();\r\n\r\n user.setUsername(phoneNumber);\r\n user.setPassword("Fake Password");\r\n user.put("firstName", firstName);\r\n user.put("lastName", lastName);\r\n user.put("country", country);\r\n user.put("countryCode", countryCode);\r\n\r\n \/\/ First query to check whether a ParseUser with\r\n \/\/ the given phone number already exists or not\r\n ParseQuery<ParseUser> query = ParseUser.getQuery();\r\n query.whereEqualTo("username", phoneNumber);\r\n\r\n query.findInBackground(new FindCallback<ParseUser>() {\r\n @Override\r\n public void done(List<ParseUser> parseUsers, ParseException e) {\r\n\r\n if (e == null) {\r\n \/\/ Successful Query\r\n\r\n \/\/ User already exists ? then login\r\n if (parseUsers.size() > 0) {\r\n loginUser(phoneNumber, "Fake Password");\r\n }\r\n else {\r\n \/\/ No user found, so signup\r\n signupUser(user);\r\n }\r\n }\r\n else {\r\n \/\/ Shit happened!\r\n AlertDialog.Builder builder = new AlertDialog.Builder(LoginActivity.this);\r\n builder.setMessage(e.getMessage())\r\n .setTitle("Oops!")\r\n .setPositiveButton(android.R.string.ok, null);\r\n AlertDialog dialog = builder.create();\r\n dialog.show();\r\n }\r\n }\r\n });\r\n }\r\n });\r\n }\r\n\r\n private void loginUser(String username, String password) {\r\n ParseUser.logInInBackground(username, password, new LogInCallback() {\r\n public void done(ParseUser user, ParseException e) {\r\n if (user != null) {\r\n \/\/ Hooray! The user is logged in.\r\n\r\n navigateToHome();\r\n } else {\r\n \/\/ Login failed!\r\n AlertDialog.Builder builder = new AlertDialog.Builder(LoginActivity.this);\r\n builder.setMessage(e.getMessage())\r\n .setTitle("Oops!")\r\n .setPositiveButton(android.R.string.ok, null);\r\n AlertDialog dialog = builder.create();\r\n dialog.show();\r\n }\r\n }\r\n });\r\n }\r\n\r\n private void signupUser(ParseUser user) {\r\n user.signUpInBackground(new SignUpCallback() {\r\n @Override\r\n public void done(ParseException e) {\r\n if (e == null) {\r\n \/\/ Signup successful!\r\n\r\n navigateToHome();\r\n } else {\r\n \/\/ Fail!\r\n AlertDialog.Builder builder = new AlertDialog.Builder(LoginActivity.this);\r\n builder.setMessage(e.getMessage())\r\n .setTitle("Oops!")\r\n .setPositiveButton(android.R.string.ok, null);\r\n AlertDialog dialog = builder.create();\r\n dialog.show();\r\n }\r\n }\r\n });\r\n }\r\n\r\n private void navigateToHome() {\r\n \/\/ Let's go to the MainActivity\r\n Intent intent = new Intent(LoginActivity.this, MainActivity.class);\r\n intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);\r\n intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);\r\n startActivity(intent);\r\n }\r\n\r\n\r\n @Override\r\n public boolean onCreateOptionsMenu(Menu menu) {\r\n \/\/ Inflate the menu; this adds items to the action bar if it is present.\r\n getMenuInflater().inflate(R.menu.login, menu);\r\n return true;\r\n }\r\n\r\n @Override\r\n public boolean onOptionsItemSelected(MenuItem item) {\r\n \/\/ Handle action bar item clicks here. The action bar will\r\n \/\/ automatically handle clicks on the Home\/Up button, so long\r\n \/\/ as you specify a parent activity in AndroidManifest.xml.\r\n int id = item.getItemId();\r\n if (id == R.id.action_settings) {\r\n return true;\r\n }\r\n return super.onOptionsItemSelected(item);\r\n }\r\n}\r\n<\/pre>\n
onCreate<\/code> method the views objects are fetched, few countries are assigned to the spinner and an “onclick” listener is set on the login button. According to the code inside the
onClick<\/code> method of the
View.OnClickListener<\/code> interface implemented we take the values of each view and check if any of those is empty or not. If it is then an error is shown else the communication with Parse starts happening.<\/p>\n
\n
ParseQuery<\/code> object is first created to check whether any user with the submitted phone number exists or not. Based on this value we’ll decide whether we need to login this user or signup (register).<\/li>\n
loginUser<\/code> method is called where the handy class method
logInInBackground<\/code> on the
ParseUser<\/code> class is passed the username (phone number) and password (a fake one) to sign in the user.<\/li>\n
signupUser<\/code> method is called where the
ParseUser<\/code> object passed to it is made to signup by calling the
signUpInBackground<\/code> method on it.<\/li>\n
navigateToHome<\/code> method is called that invokes the
MainActivity<\/code> (home screen in our case) using an Intent.\n<\/ul>\n
navigateToHome()<\/code> method:<\/p>\n
\r\nintent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);\r\nintent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);\r\n<\/pre>\n
LoginActivity<\/code> from the tasks back stack, so that when you hit the back button after login, you won’t end up on the login screen again. Another way to achieve this behaviour is to use android:noHistory<\/a> attribute of the relevant
<activity><\/code> tag in the
AndroidManifest.xml<\/code>. When set to true, it won’t leave a historical trace in the activity stack for the task.<\/p>\n
\n
final<\/code> variables, we could probably set them as instance\/member variables.<\/li>\n<\/ul>\n
onCreate<\/code> method.<\/p>\n
\r\n\/\/ Get current user\r\nParseUser currentUser = ParseUser.getCurrentUser();\r\n\r\nif (currentUser == null) {\r\n \/\/ It's an anonymous user, hence show the login screen\r\n navigateToLogin();\r\n}\r\nelse {\r\n \/\/ The user is logged in, yay!!\r\n Log.i(TAG, currentUser.getUsername());\r\n}\r\n<\/pre>\n
navigateToLogin()<\/code> method will look like:<\/p>\n
\r\nprivate void navigateToLogin() {\r\n \/\/ Launch the login activity\r\n \r\n Intent intent = new Intent(this, LoginActivity.class);\r\n intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);\r\n intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);\r\n startActivity(intent);\r\n}\r\n<\/pre>\n
onOptionsItemSelected<\/code> method of the
Activity<\/code> class:<\/p>\n
\r\nParseUser.logOut();\r\nnavigateToLogin();\r\n<\/pre>\n
Next Up<\/h2>\n