Many applications require users to login in order to access all the available features. In this tutorial we will secure an Android activity by requiring users to enter their username and password.
We will start by creating a new blank activity with fragment, LoginActivity.
We will use the following layout for the login fragment (2 edit texts for username & password, 1 button to submit the login form and one text view to show any errors that could happen):
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin" android:paddingBottom="@dimen/activity_vertical_margin" tools:context="com.sendgrid.android.sendgrid.app.LoginActivity$PlaceholderFragment"> <EditText android:layout_width="wrap_content" android:layout_height="wrap_content" android:inputType="textEmailAddress" android:ems="10" android:id="@+id/username" android:hint="Username" android:layout_centerVertical="true" android:layout_centerHorizontal="true" /> <EditText android:layout_width="wrap_content" android:layout_height="wrap_content" android:inputType="textPassword" android:ems="10" android:id="@+id/password" android:layout_below="@+id/username" android:layout_alignLeft="@+id/editText" android:layout_alignStart="@+id/editText" android:hint="Password" /> <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Login" android:id="@+id/button" android:layout_below="@+id/password" android:layout_alignLeft="@+id/editText2" android:layout_alignStart="@+id/editText2" android:layout_alignRight="@+id/editText2" android:layout_alignEnd="@+id/editText2" android:onClick="tryLogin"/> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="" android:id="@+id/error" android:layout_above="@+id/username" android:layout_centerHorizontal="true" android:textColor="#ffff0000" /> </RelativeLayout>
Before writing the actual login code, we will create a helper class to read & save related values to Android’s SharedPreferences:
public class Utility { public static Boolean isUserLoggedIn(Context context) { SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); return prefs.getBoolean("isUserLoggedIn", false); } public static void setUserLoggedIn(Context context, Boolean isLoggedIn) { SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); SharedPreferences.Editor editor = prefs.edit(); editor.putBoolean("isUserLoggedIn", isLoggedIn); editor.commit(); } public static void logout(Context context) { SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); SharedPreferences.Editor editor = prefs.edit(); editor.putBoolean("isUserLoggedIn", false); editor.commit(); } public static void saveUsernameAndPassword(Context context, String username, String password) { SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); SharedPreferences.Editor editor = prefs.edit(); editor.putString("username", username); editor.putString("password", password); editor.commit(); } }
Now let’s see the actual login code:
public class LoginActivity extends ActionBarActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_login); if (savedInstanceState == null) { getSupportFragmentManager().beginTransaction() .add(R.id.container, new LoginFragment()) .commit(); } } @Override public boolean onCreateOptionsMenu(Menu menu) { return false; } public void tryLogin(View view) { LoginFragment fragment = (LoginFragment) getSupportFragmentManager().findFragmentById(R.id.container); fragment.tryLogin(view); } /** * A placeholder fragment containing a simple view. */ public static class LoginFragment extends Fragment { public LoginFragment() { } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View rootView = inflater.inflate(R.layout.fragment_login, container, false); return rootView; } public void login(Boolean result) { Utility.setUserLoggedIn(getActivity(), result); if(result) { EditText user = (EditText)getActivity().findViewById(R.id.username); EditText pass = (EditText)getActivity().findViewById(R.id.password); String username = user.getText().toString(); String password = pass.getText().toString(); Utility.saveUsernameAndPassword(getActivity(), username, password); getActivity().finish(); } else { Utility.saveUsernameAndPassword(getActivity(), null, null); TextView error = (TextView)getActivity().findViewById(R.id.error); error.setText("Login failed! Please try again."); } } public void tryLogin(View view) { EditText user = (EditText)getActivity().findViewById(R.id.username); EditText pass = (EditText)getActivity().findViewById(R.id.password); String username = username.getText().toString(); String password = password.getText().toString(); if(!username.isEmpty() && !password.isEmpty()) { TextView error = (TextView)getActivity().findViewById(R.id.error); error.setText(""); CheckLoginTask loginTask = new CheckLoginTask(); loginTask.execute(username, password); } } // we will use an AsyncTask to connect to an API service to check the username and the password // the doInBackground method will return true if the login succeeds public class CheckLoginTask extends AsyncTask<String, Void, Boolean> { @Override protected Boolean doInBackground(String... params) { HttpsURLConnection urlConnection = null; BufferedReader reader = null; String responseJsonStr = null; try { // Construct the URL for the get User query final String GET_PROFILE_BASE_URL ="https://api.domain.com/user?"; Uri builtUri = Uri.parse(GET_PROFILE_BASE_URL).buildUpon().build(); URL url = new URL(builtUri.toString()); // Create the request to server and open the connection urlConnection = (HttpsURLConnection) url.openConnection(); // Create the SSL connection SSLContext sc; sc = SSLContext.getInstance("TLS"); sc.init(null, null, new SecureRandom()); urlConnection.setSSLSocketFactory(sc.getSocketFactory()); // Add API credentials String user = params[0]; String password = params[1]; String userpass = user + ":" + password; // Create the Authentication Token String basicAuth = "Basic " + Base64.encodeToString(userpass.getBytes(), Base64.DEFAULT); // Add the required Headers. urlConnection.addRequestProperty("Authorization", basicAuth); urlConnection.addRequestProperty("Content-Type", "application/json"); urlConnection.setRequestProperty("accept", "application/json"); // Method urlConnection.setRequestMethod("GET"); // Connect urlConnection.connect(); int status = urlConnection.getResponseCode(); String reason = urlConnection.getResponseMessage(); Log.v("LOGIN", status + reason); // Read the input stream into a String InputStream inputStream = urlConnection.getInputStream(); StringBuffer buffer = new StringBuffer(); if (inputStream == null) { // Nothing to do here return null; } reader = new BufferedReader(new InputStreamReader(inputStream)); String line; while ((line = reader.readLine()) != null) { // Since it's JSON, adding a newline isn't necessary (it won't affect parsing) // But it does make debugging a *lot* easier if you print out the completed // buffer for debugging. buffer.append(line + "\n"); } if (buffer.length() == 0) { // Stream was empty. No point in parsing. return null; } responseJsonStr = buffer.toString(); getNameDataFromJson(responseJsonStr); } catch (IOException | NoSuchAlgorithmException | JSONException | KeyManagementException e) { Log.e("LOGIN", "Error", e); return false; } finally { if (urlConnection != null) { urlConnection.disconnect(); } if (reader != null) { try { reader.close(); } catch (final IOException e) { } } } // if we reach here it means we successfully logged in return true; } @Override protected void onPostExecute(Boolean result) { super.onPostExecute(result); login(result); } } } }
In the end, all that’s left is to start this activity whenever we need the user to login:
public class MainActivity extends Activity { // ... @Override protected void onResume() { super.onResume(); if(!Utility.isUserLoggedIn(this)){ startActivity(new Intent(this, LoginActivity.class)); } } // ... }