1 package ${packageName}; 2 3 import android.animation.Animator; 4 import android.animation.AnimatorListenerAdapter; 5 import android.annotation.TargetApi; 6 import android.app.Activity; 7 import android.os.AsyncTask; 8 import android.os.Build; 9 import android.os.Bundle; 10 import android.text.TextUtils; 11 import android.view.KeyEvent; 12 import android.view.Menu; 13 import android.view.View; 14 import android.view.inputmethod.EditorInfo; 15 import android.widget.EditText; 16 import android.widget.TextView; 17 <#if parentActivityClass != ""> 18 import android.view.MenuItem; 19 import android.support.v4.app.NavUtils; 20 </#if> 21 22 /** 23 * Activity which displays a login screen to the user, offering registration as 24 * well. 25 */ 26 public class ${activityClass} extends Activity { 27 /** 28 * A dummy authentication store containing known user names and passwords. 29 * TODO: remove after connecting to a real authentication system. 30 */ 31 private static final String[] DUMMY_CREDENTIALS = new String[]{ 32 "foo (a] example.com:hello", 33 "bar (a] example.com:world" 34 }; 35 36 /** 37 * The default email to populate the email field with. 38 */ 39 public static final String EXTRA_EMAIL = "com.example.android.authenticatordemo.extra.EMAIL"; 40 41 /** 42 * Keep track of the login task to ensure we can cancel it if requested. 43 */ 44 private UserLoginTask mAuthTask = null; 45 46 // Values for email and password at the time of the login attempt. 47 private String mEmail; 48 private String mPassword; 49 50 // UI references. 51 private EditText mEmailView; 52 private EditText mPasswordView; 53 private View mLoginFormView; 54 private View mLoginStatusView; 55 private TextView mLoginStatusMessageView; 56 57 @Override 58 protected void onCreate(Bundle savedInstanceState) { 59 super.onCreate(savedInstanceState); 60 61 setContentView(R.layout.${layoutName}); 62 <#if parentActivityClass != ""> 63 setupActionBar(); 64 </#if> 65 66 // Set up the login form. 67 mEmail = getIntent().getStringExtra(EXTRA_EMAIL); 68 mEmailView = (EditText) findViewById(R.id.email); 69 mEmailView.setText(mEmail); 70 71 mPasswordView = (EditText) findViewById(R.id.password); 72 mPasswordView.setOnEditorActionListener(new TextView.OnEditorActionListener() { 73 @Override 74 public boolean onEditorAction(TextView textView, int id, KeyEvent keyEvent) { 75 if (id == R.id.login || id == EditorInfo.IME_NULL) { 76 attemptLogin(); 77 return true; 78 } 79 return false; 80 } 81 }); 82 83 mLoginFormView = findViewById(R.id.login_form); 84 mLoginStatusView = findViewById(R.id.login_status); 85 mLoginStatusMessageView = (TextView) findViewById(R.id.login_status_message); 86 87 findViewById(R.id.sign_in_button).setOnClickListener(new View.OnClickListener() { 88 @Override 89 public void onClick(View view) { 90 attemptLogin(); 91 } 92 }); 93 } 94 95 <#if parentActivityClass != ""> 96 /** 97 * Set up the {@link android.app.ActionBar}, if the API is available. 98 */ 99 @TargetApi(Build.VERSION_CODES.HONEYCOMB) 100 private void setupActionBar() { 101 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) { 102 // Show the Up button in the action bar. 103 getActionBar().setDisplayHomeAsUpEnabled(true); 104 } 105 } 106 107 @Override 108 public boolean onOptionsItemSelected(MenuItem item) { 109 switch (item.getItemId()) { 110 case android.R.id.home: 111 // This ID represents the Home or Up button. In the case of this 112 // activity, the Up button is shown. Use NavUtils to allow users 113 // to navigate up one level in the application structure. For 114 // more details, see the Navigation pattern on Android Design: 115 // 116 // http://developer.android.com/design/patterns/navigation.html#up-vs-back 117 // 118 // TODO: If Settings has multiple levels, Up should navigate up 119 // that hierarchy. 120 NavUtils.navigateUpFromSameTask(this); 121 return true; 122 } 123 return super.onOptionsItemSelected(item); 124 } 125 </#if> 126 127 @Override 128 public boolean onCreateOptionsMenu(Menu menu) { 129 super.onCreateOptionsMenu(menu); 130 getMenuInflater().inflate(R.menu.${menuName}, menu); 131 return true; 132 } 133 134 /** 135 * Attempts to sign in or register the account specified by the login form. 136 * If there are form errors (invalid email, missing fields, etc.), the 137 * errors are presented and no actual login attempt is made. 138 */ 139 public void attemptLogin() { 140 if (mAuthTask != null) { 141 return; 142 } 143 144 // Reset errors. 145 mEmailView.setError(null); 146 mPasswordView.setError(null); 147 148 // Store values at the time of the login attempt. 149 mEmail = mEmailView.getText().toString(); 150 mPassword = mPasswordView.getText().toString(); 151 152 boolean cancel = false; 153 View focusView = null; 154 155 // Check for a valid password. 156 if (TextUtils.isEmpty(mPassword)) { 157 mPasswordView.setError(getString(R.string.error_field_required)); 158 focusView = mPasswordView; 159 cancel = true; 160 } else if (mPassword.length() < 4) { 161 mPasswordView.setError(getString(R.string.error_invalid_password)); 162 focusView = mPasswordView; 163 cancel = true; 164 } 165 166 // Check for a valid email address. 167 if (TextUtils.isEmpty(mEmail)) { 168 mEmailView.setError(getString(R.string.error_field_required)); 169 focusView = mEmailView; 170 cancel = true; 171 } else if (!mEmail.contains("@")) { 172 mEmailView.setError(getString(R.string.error_invalid_email)); 173 focusView = mEmailView; 174 cancel = true; 175 } 176 177 if (cancel) { 178 // There was an error; don't attempt login and focus the first 179 // form field with an error. 180 focusView.requestFocus(); 181 } else { 182 // Show a progress spinner, and kick off a background task to 183 // perform the user login attempt. 184 mLoginStatusMessageView.setText(R.string.login_progress_signing_in); 185 showProgress(true); 186 mAuthTask = new UserLoginTask(); 187 mAuthTask.execute((Void) null); 188 } 189 } 190 191 /** 192 * Shows the progress UI and hides the login form. 193 */ 194 @TargetApi(Build.VERSION_CODES.HONEYCOMB_MR2) 195 private void showProgress(final boolean show) { 196 // On Honeycomb MR2 we have the ViewPropertyAnimator APIs, which allow 197 // for very easy animations. If available, use these APIs to fade-in 198 // the progress spinner. 199 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB_MR2) { 200 int shortAnimTime = getResources().getInteger(android.R.integer.config_shortAnimTime); 201 202 mLoginStatusView.setVisibility(View.VISIBLE); 203 mLoginStatusView.animate() 204 .setDuration(shortAnimTime) 205 .alpha(show ? 1 : 0) 206 .setListener(new AnimatorListenerAdapter() { 207 @Override 208 public void onAnimationEnd(Animator animation) { 209 mLoginStatusView.setVisibility(show ? View.VISIBLE : View.GONE); 210 } 211 }); 212 213 mLoginFormView.setVisibility(View.VISIBLE); 214 mLoginFormView.animate() 215 .setDuration(shortAnimTime) 216 .alpha(show ? 0 : 1) 217 .setListener(new AnimatorListenerAdapter() { 218 @Override 219 public void onAnimationEnd(Animator animation) { 220 mLoginFormView.setVisibility(show ? View.GONE : View.VISIBLE); 221 } 222 }); 223 } else { 224 // The ViewPropertyAnimator APIs are not available, so simply show 225 // and hide the relevant UI components. 226 mLoginStatusView.setVisibility(show ? View.VISIBLE : View.GONE); 227 mLoginFormView.setVisibility(show ? View.GONE : View.VISIBLE); 228 } 229 } 230 231 /** 232 * Represents an asynchronous login/registration task used to authenticate 233 * the user. 234 */ 235 public class UserLoginTask extends AsyncTask<Void, Void, Boolean> { 236 @Override 237 protected Boolean doInBackground(Void... params) { 238 // TODO: attempt authentication against a network service. 239 240 try { 241 // Simulate network access. 242 Thread.sleep(2000); 243 } catch (InterruptedException e) { 244 return false; 245 } 246 247 for (String credential : DUMMY_CREDENTIALS) { 248 String[] pieces = credential.split(":"); 249 if (pieces[0].equals(mEmail)) { 250 // Account exists, return true if the password matches. 251 return pieces[1].equals(mPassword); 252 } 253 } 254 255 // TODO: register the new account here. 256 return true; 257 } 258 259 @Override 260 protected void onPostExecute(final Boolean success) { 261 mAuthTask = null; 262 showProgress(false); 263 264 if (success) { 265 finish(); 266 } else { 267 mPasswordView.setError(getString(R.string.error_incorrect_password)); 268 mPasswordView.requestFocus(); 269 } 270 } 271 272 @Override 273 protected void onCancelled() { 274 mAuthTask = null; 275 showProgress(false); 276 } 277 } 278 } 279