private package com.mobmonkey.mobmonkeyandroid; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Arrays; import java.util.Calendar; import java.util.Date; import java.util.HashMap; import java.util.Locale; import org.json.JSONException; import org.json.JSONObject; import com.facebook.Request; import com.facebook.Response; import com.facebook.Session; import com.facebook.SessionState; import com.facebook.model.GraphUser; import com.mobmonkey.mobmonkeyandroid.R; import com.mobmonkey.mobmonkeyandroid.utils.MMConstants; import com.mobmonkey.mobmonkeyandroid.utils.MMUtility; import com.mobmonkey.mobmonkeysdk.adapters.MMUserAdapter; import com.mobmonkey.mobmonkeysdk.utils.MMSDKConstants; import com.mobmonkey.mobmonkeysdk.utils.MMCallback; import com.mobmonkey.mobmonkeysdk.utils.MMProgressDialog; import android.os.Bundle; import android.os.StrictMode; import android.app.Activity; import android.app.AlertDialog; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; import android.content.SharedPreferences; import android.util.Log; import android.view.KeyEvent; import android.view.LayoutInflater; import android.view.MotionEvent; import android.view.View; import android.view.View.OnKeyListener; import android.view.View.OnTouchListener; import android.view.inputmethod.InputMethodManager; import android.widget.Button; import android.widget.CheckBox; import android.widget.DatePicker; import android.widget.EditText; import android.widget.Toast; import android.widget.DatePicker.OnDateChangedListener; import android.text.TextUtils; /** * Android {@link Activity} screen that allows user to sign up his/her account through MobMonkey, Facebook or Twitter. * @author Dezapp, LLC * */ public class SignUpScreen extends Activity implements OnKeyListener, OnDateChangedListener, OnTouchListener { private static final String TAG = "SignUpScreen: "; private SharedPreferences userPrefs; private SharedPreferences.Editor userPrefsEditor; private InputMethodManager inputMethodManager; private EditText etFirstName; private EditText etLastName; private EditText etEmailAddress; private EditText etPassword; private EditText etPasswordConfirm; private EditText etBirthdate; private EditText etGender; private CheckBox cbAcceptedToS; private Calendar birthdate; private boolean requestEmail; private GraphUser facebookUser; /* * (non-Javadoc) * @see android.app.Activity#onCreate(android.os.Bundle) */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); Log.d(TAG, TAG + "onCreate"); overridePendingTransition(R.anim.slide_right_in, R.anim.slide_hold); setContentView(R.layout.signup_screen); if (android.os.Build.VERSION.SDK_INT > 9) { StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder().permitAll().build(); StrictMode.setThreadPolicy(policy); } init(); } /** * {@link OnKeyListener} handle when user finished entering confirmed password and go to the birthdate {@link EditText}, removes the soft keyboard */ /* (non-Javadoc) * @see android.view.View.OnKeyListener#onKey(android.view.View, int, android.view.KeyEvent) */ @Override public boolean onKey(View v, int keyCode, KeyEvent event) { if(event.getAction() == KeyEvent.ACTION_UP && keyCode == KeyEvent.KEYCODE_ENTER) { inputMethodManager.hideSoftInputFromWindow(etPasswordConfirm.getWindowToken(), 0); return true; } return false; } /** * {@link OnTouchListener} handler for birthdate and gender {@link EditText}. When the {@link EditText}s are touched, it will prompt the user to select his/her birthdate or gender. */ /* * (non-Javadoc) * @see android.view.View.OnTouchListener#onTouch(android.view.View, android.view.MotionEvent) */ @Override public boolean onTouch(View v, MotionEvent event) { if(event.getAction() == MotionEvent.ACTION_UP) { switch(v.getId()) { case R.id.etbirthdate: inputMethodManager.hideSoftInputFromWindow(etBirthdate.getWindowToken(), 0); promptUserBirthdate(); return true; case R.id.etgender: inputMethodManager.hideSoftInputFromWindow(etGender.getWindowToken(), 0); promptUserGender(); return true; } } return false; } /** * Handle events when the date changes on the {@link DatePicker} */ /* * (non-Javadoc) * @see android.widget.DatePicker.OnDateChangedListener#onDateChanged(android.widget.DatePicker, int, int, int) */ @Override public void onDateChanged(DatePicker view, int year, int monthOfYear, int dayOfMonth) { } /* * (non-Javadoc) * @see android.app.Activity#onActivityResult(int, int, android.content.Intent) */ @Override public void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); Log.d(TAG, TAG + "onActivityResult"); switch(requestCode) { case MMSDKConstants.REQUEST_CODE_SIGN_UP_TWITTER_AUTH: MMProgressDialog.dismissDialog(); if(resultCode == MMSDKConstants.RESULT_CODE_SUCCESS) { Toast.makeText(SignUpScreen.this, R.string.toast_sign_up_in_successful, Toast.LENGTH_LONG).show(); startActivity(new Intent(SignUpScreen.this, MainScreen.class)); finish(); } else if(resultCode == MMSDKConstants.RESULT_CODE_NOT_FOUND) { Toast.makeText(SignUpScreen.this, R.string.toast_new_twitter_user, Toast.LENGTH_SHORT).show(); Intent signUpTwitterIntent = (Intent) data.clone(); signUpTwitterIntent.setClass(SignUpScreen.this, SignUpTwitterScreen.class); startActivityForResult(signUpTwitterIntent, MMSDKConstants.REQUEST_CODE_SIGN_UP_TWITTER); } break; case MMSDKConstants.REQUEST_CODE_SIGN_UP_TWITTER: Log.d(TAG, TAG + "coming back from twitter sign up"); if(resultCode == RESULT_OK) { startActivity(new Intent(SignUpScreen.this, MainScreen.class)); finish(); } break; default: // TODO: Find the Facebook requestCode Session.getActiveSession().onActivityResult(this, requestCode, resultCode, data); if(!requestEmail) { userPrefsEditor.putString(MMSDKConstants.KEY_USER, Session.getActiveSession().getAccessToken()); userPrefsEditor.putString(MMSDKConstants.KEY_AUTH, (String) facebookUser.getProperty(MMSDKConstants.FACEBOOK_REQ_PERM_EMAIL)); userPrefsEditor.putString(MMSDKConstants.KEY_OAUTH_PROVIDER, MMSDKConstants.OAUTH_PROVIDER_FACEBOOK); MMUserAdapter.signUpNewUserFacebook(new SignUpCallback(), MMConstants.PARTNER_ID, Session.getActiveSession().getAccessToken(), (String) facebookUser.getProperty(MMSDKConstants.FACEBOOK_REQ_PERM_EMAIL), facebookUser.getFirstName(), facebookUser.getLastName(), convertBirthdate(facebookUser.getBirthday()), convertGender((String) facebookUser.getProperty(MMSDKConstants.FACEBOOK_REQ_PERM_GENDER))); MMProgressDialog.displayDialog(SignUpScreen.this, MMSDKConstants.DEFAULT_STRING_EMPTY, getString(R.string.pd_signing_up_facebook)); } break; } } /* (non-Javadoc) * @see android.app.Activity#onBackPressed() */ @Override public void onBackPressed() { super.onBackPressed(); overridePendingTransition(R.anim.slide_hold, R.anim.slide_right_out); } /** * Handler for when {@link Button}s or any other {@link View}s are clicked. * @param view {@link View} that is clicked */ public void viewOnClick(View view) { switch(view.getId()) { case R.id.tvtos: openToS(); break; case R.id.btnsignup: signUpNormal(); break; case R.id.btnsignupfacebook: signUpFacebook(); break; case R.id.btnsignuptwitter: signUpTwitter(); break; } } /** * Initialize all the variables to be used in {@link SignUpScreen}. */ private void init() { userPrefs = getSharedPreferences(MMSDKConstants.USER_PREFS, MODE_PRIVATE); userPrefsEditor = userPrefs.edit(); inputMethodManager = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE); etFirstName = (EditText) findViewById(R.id.etfirstname); etLastName = (EditText) findViewById(R.id.etlastname); etEmailAddress = (EditText) findViewById(R.id.etemailaddress); etPassword = (EditText) findViewById(R.id.etpassword); etPasswordConfirm = (EditText) findViewById(R.id.etpasswordconfirm); etBirthdate = (EditText) findViewById(R.id.etbirthdate); etGender = (EditText) findViewById(R.id.etgender); cbAcceptedToS = (CheckBox) findViewById(R.id.cbagreetos); etPasswordConfirm.setOnKeyListener(SignUpScreen.this); etBirthdate.setOnTouchListener(SignUpScreen.this); etGender.setOnTouchListener(SignUpScreen.this); requestEmail = true; } /** * Function that opens the Terms of Use {@link Activity} */ private void openToS() { startActivity(new Intent(SignUpScreen.this, TermsofuseScreen.class)); } /** * Function that handles normal user sign up with email through MobMonkey */ private void signUpNormal() { if(checkFirstName()) { userPrefsEditor.putString(MMSDKConstants.KEY_USER, etEmailAddress.getText().toString()); userPrefsEditor.putString(MMSDKConstants.KEY_AUTH, etPassword.getText().toString()); userPrefsEditor.putString(MMSDKConstants.KEY_OAUTH_PROVIDER, MMSDKConstants.DEFAULT_STRING_EMPTY); MMUserAdapter.signUpNewUser(new SignUpCallback(), etFirstName.getText().toString(), etLastName.getText().toString(), etEmailAddress.getText().toString(), etPassword.getText().toString(), Long.toString(birthdate.getTimeInMillis()), convertGender(etGender.getText().toString()), cbAcceptedToS.isChecked(), MMConstants.PARTNER_ID); MMProgressDialog.displayDialog(SignUpScreen.this, MMSDKConstants.DEFAULT_STRING_EMPTY, getString(R.string.pd_signing_up)); } } /** * Function that handles the user sign up with Facebook API */ private void signUpFacebook() { if(checkAcceptedToS()) { Session.openActiveSession(SignUpScreen.this, true, new SessionStatusCallback()); userPrefsEditor.putBoolean(MMSDKConstants.SHARED_PREFS_KEY_TOS_FACEBOOK, true); userPrefsEditor.commit(); } } /** * Function that handles the user sign up with Twitter. Go to the {@link TwitterAuthScreen} and allows the user there to authenticate himself/herself call the MM SignUp with Twitter on that screen. * If user already exist in MobMonkey database, it will sign user in to the application. If not, it will come back to this screen and be transported to the {@link SignUpTwitterScreen} for user to enter * his or her information. * NOTE: Not launching the browser on this screen because the app need to authenticate the user via Twitter on two different instance, SignIn and SignUp. Normal procedure requires current {@link Activity} on * the Manifest to have launchMode 'singleTask'. This causes the {@link SignInScreen} onActivityResult callback handling to be invoked before this {@link Activity} is even created. Another problem with * launchMode singleTask is that this {@link Activity} can only be created once, if it was destroyed and recreated, it will cause an {@link IllegalStateException} error. */ private void signUpTwitter() { if(checkAcceptedToS()) { userPrefsEditor.putBoolean(MMSDKConstants.SHARED_PREFS_KEY_TOS_TWITTER, true); userPrefsEditor.commit(); MMProgressDialog.displayDialog(SignUpScreen.this, MMSDKConstants.DEFAULT_STRING_EMPTY, getString(R.string.pd_launch_twitter_auth_screen)); Intent twitterAuthIntent = new Intent(SignUpScreen.this, TwitterAuthScreen.class); twitterAuthIntent.putExtra(MMSDKConstants.REQUEST_CODE, MMSDKConstants.REQUEST_CODE_SIGN_UP_TWITTER_AUTH); startActivityForResult(twitterAuthIntent, MMSDKConstants.REQUEST_CODE_SIGN_UP_TWITTER_AUTH); } } /** * Function that check if the first name {@link EditText} field is valid and is not empty and stored the value into a {@link HashMap}. * @return <code>false</code> otherwise */ private boolean checkFirstName() { if(!TextUtils.isEmpty(etFirstName.getText())) { return checkLastName(); } else { displayAlert(R.string.ad_message_invalid_first_name); return false; } } /** * Function that check if the last name {@link EditText} field is valid and is not empty and stored the value into a {@link HashMap}. * @return <code>false</code> otherwise */ private boolean checkLastName() { if(!TextUtils.isEmpty(etLastName.getText().toString())) { return checkEmailAddress(); } else { displayAlert(R.string.ad_message_invalid_last_name); return false; } } /** * Function that check if the email address {@link EditText} field is valid and is not empty and stored the value into a {@link HashMap}. * @return <code>false</code> otherwise */ private boolean checkEmailAddress() { if(!TextUtils.isEmpty(etEmailAddress.getText())) { userPrefsEditor.putString(MMSDKConstants.KEY_EMAIL_ADDRESS, etEmailAddress.getText().toString()); userPrefsEditor.commit(); return checkPassword(); } else { displayAlert(R.string.ad_message_invalid_email_address); return false; } } /** * Function that check if the password {@link EditText} fields are valid and are not empty. In addition, it compare the passwords to determine if they are equal and and stored the value into a {@link HashMap}. * @return <code>false</code> otherwise */ private boolean checkPassword() { if(!TextUtils.isEmpty(etPassword.getText()) && !TextUtils.isEmpty(etPasswordConfirm.getText())) { if(etPassword.getText().toString().equals(etPasswordConfirm.getText().toString())) { userPrefsEditor.putString(MMSDKConstants.KEY_PASSWORD, etPassword.getText().toString()); userPrefsEditor.commit(); return checkBirthdate(); } else { displayAlert(R.string.ad_message_invalid_password_not_match); return false; } } else { displayAlert(R.string.ad_message_invalid_password); return false; } } /** * Function that check if the birthdate {@link EditText} field is valid and is not empty and stored the value into a {@link HashMap}. * @return <code>false</code> otherwise */ private boolean checkBirthdate() { if(!TextUtils.isEmpty(etBirthdate.getText())) { return checkGender(); } else { displayAlert(R.string.ad_message_invalid_birthdate); return false; } } /** * Function that check if the gender {@link EditText} field is valid and is not empty and stored the value into a {@link HashMap}. * @return <code>false</code> otherwise */ private boolean checkGender() { if(!TextUtils.isEmpty(etGender.getText())) { return checkAcceptedToS(); } else { displayAlert(R.string.ad_message_invalid_gender); return false; } } /** * Function that check if the {@link CheckBox} accept term of use is checked and stored the value into a {@link HashMap}. * @return <code>false</code> otherwise */ private boolean checkAcceptedToS() { if(cbAcceptedToS.isChecked()) { return true; } else { displayAlert(R.string.ad_message_invalid_tos); return false; } } /** * Display an {@link AlertDialog} with the associated message informing user that they forgot enter a certain input field. * @param messageId String resource id of the message to be displayed */ private void displayAlert(int messageId) { new AlertDialog.Builder(SignUpScreen.this) .setTitle(R.string.app_name) .setMessage(messageId) .setNeutralButton(android.R.string.ok, null) .show(); } /** * Prompt the user with an {@link AlertDialog} to select his/her birthdate. */ private void promptUserBirthdate() { LayoutInflater layoutInflator = LayoutInflater.from(SignUpScreen.this); View vBirthdate = layoutInflator.inflate(R.layout.birthdate_picker, null); final DatePicker dpBirthdate = (DatePicker) vBirthdate.findViewById(R.id.dpbirthdate); if(birthdate == null) { birthdate = Calendar.getInstance(); birthdate.setTimeInMillis(System.currentTimeMillis()); birthdate.set(birthdate.get(Calendar.YEAR) - 21, birthdate.get(Calendar.MONTH), birthdate.get(Calendar.DAY_OF_MONTH)); dpBirthdate.init(birthdate.get(Calendar.YEAR), birthdate.get(Calendar.MONTH), birthdate.get(Calendar.DAY_OF_MONTH), SignUpScreen.this); } else { dpBirthdate.init(birthdate.get(Calendar.YEAR), birthdate.get(Calendar.MONTH), birthdate.get(Calendar.DAY_OF_MONTH), SignUpScreen.this); } new AlertDialog.Builder(SignUpScreen.this) .setTitle(R.string.ad_title_birthdate) .setView(vBirthdate) .setCancelable(false) .setPositiveButton(R.string.ad_btn_choose, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { birthdate.set(dpBirthdate.getYear(), dpBirthdate.getMonth(), dpBirthdate.getDayOfMonth()); etBirthdate.setText(MMUtility.getDate(birthdate.getTimeInMillis(), MMSDKConstants.DATE_FORMAT_MMM_DD_COMMA_YYYY)); } }) .setNegativeButton(R.string.ad_btn_cancel, null) .show(); } /** * Prompt the user with an {@link AlertDialog} for his/her gender. */ private void promptUserGender() { new AlertDialog.Builder(SignUpScreen.this) .setTitle(R.string.ad_title_gender) .setItems(R.array.ad_list_gender, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { etGender.setText(getResources().getStringArray(R.array.ad_list_gender)[which]); } }) .setNegativeButton(R.string.ad_btn_cancel, null) .setCancelable(false) .show(); } private String convertBirthdate(String date) { try { Date birthdate = new SimpleDateFormat("MM/dd/yyyy", Locale.ENGLISH).parse(date); return Long.toString(birthdate.getTime()); } catch (ParseException e) { e.printStackTrace(); } return null; } /** * Function that converts the gender of the user from {@link String} representation to {@link Integer} representation. * @return */ private String convertGender(String sex) { int gender = MMSDKConstants.DEFAULT_INT; if(sex.equalsIgnoreCase(MMSDKConstants.TEXT_MALE)) { gender = MMSDKConstants.NUM_MALE; } else if(sex.equalsIgnoreCase(MMSDKConstants.TEXT_FEMALE)) { gender = MMSDKConstants.NUM_FEMALE; } return Integer.toString(gender); } /** * Custom {@link Session.StatusCallback} specifically for {@link SignInScreen} to handle the {@link Session} state change. * @author Dezapp, LLC * */ private class SessionStatusCallback implements Session.StatusCallback { @Override public void call(Session session, SessionState state, Exception exception) { Log.d(TAG, TAG + "sign in with facebook"); Log.d(TAG, TAG + "requestEmail: " + requestEmail); Log.d(TAG, TAG + "session opened: " + session.isOpened()); if(session.isOpened() && requestEmail) { Session.NewPermissionsRequest request = new Session.NewPermissionsRequest(SignUpScreen.this, Arrays.asList(MMSDKConstants.FACEBOOK_REQ_PERM_EMAIL, MMSDKConstants.FACEBOOK_REQ_PERM_BIRTHDAY)); session.requestNewReadPermissions(request); Request.executeMeRequestAsync(session, new RequestGraphUserCallback()); } } } /** * Custom {@link Request.GraphUserCallback} specifically for {@link SignInScreen} to the completion of the {@link Request}.executeMeRequestAsync({@link Session}, {@link Request.GraphUserCallback}). * @author Dezapp, LLC * */ private class RequestGraphUserCallback implements Request.GraphUserCallback { @Override public void onCompleted(GraphUser user, Response response) { Log.d(TAG, TAG + "onCompleted"); if(user != null) { requestEmail = false; facebookUser = user; } } } /** * Custom {@link MMCallback} specifically for {@link SignUpScreen} to be processed after receiving response from MobMonkey server. * @author Dezapp, LLC * */ private class SignUpCallback implements MMCallback { @Override public void processCallback(Object obj) { MMProgressDialog.dismissDialog(); if(obj != null) { if(((String) obj).equals(MMSDKConstants.CONNECTION_TIMED_OUT)) { Toast.makeText(SignUpScreen.this, getString(R.string.toast_connection_timed_out), Toast.LENGTH_SHORT).show(); } else { try { JSONObject response = new JSONObject((String) obj); if(response.getString(MMSDKConstants.JSON_KEY_STATUS).equals(MMSDKConstants.RESPONSE_STATUS_SUCCESS)) { inputMethodManager.hideSoftInputFromWindow(etPasswordConfirm.getWindowToken(), 0); Toast.makeText(SignUpScreen.this, R.string.toast_sign_up_successful, Toast.LENGTH_SHORT).show(); userPrefsEditor.commit(); startActivity(new Intent(SignUpScreen.this, MainScreen.class)); finish(); overridePendingTransition(R.anim.slide_hold, R.anim.slide_right_out); } else { Toast.makeText(SignUpScreen.this, response.getString(MMSDKConstants.JSON_KEY_DESCRIPTION), Toast.LENGTH_LONG).show(); } } catch (JSONException e) { e.printStackTrace(); } } } Log.d(TAG, TAG + "response: " + (String) obj); } } }