com.berniesanders.fieldthebern.screens.SignupScreen.java Source code

Java tutorial

Introduction

Here is the source code for com.berniesanders.fieldthebern.screens.SignupScreen.java

Source

/*
 * Copyright (c) 2016 - Bernie 2016, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */

package com.berniesanders.fieldthebern.screens;

import android.content.Intent;
import android.graphics.Bitmap;
import android.location.Location;
import android.os.Build;
import android.os.Bundle;
import android.provider.Settings;
import android.support.design.widget.Snackbar;
import android.view.View;
import android.widget.AutoCompleteTextView;
import android.widget.Toast;
import butterknife.Bind;
import butterknife.BindString;
import butterknife.ButterKnife;
import butterknife.OnClick;
import butterknife.OnTouch;
import com.berniesanders.fieldthebern.FTBApplication;
import com.berniesanders.fieldthebern.R;
import com.berniesanders.fieldthebern.annotations.Layout;
import com.berniesanders.fieldthebern.controllers.ActionBarController;
import com.berniesanders.fieldthebern.controllers.ActionBarService;
import com.berniesanders.fieldthebern.controllers.DialogController;
import com.berniesanders.fieldthebern.controllers.DialogService;
import com.berniesanders.fieldthebern.controllers.LocationService;
import com.berniesanders.fieldthebern.controllers.PermissionService;
import com.berniesanders.fieldthebern.controllers.ProgressDialogService;
import com.berniesanders.fieldthebern.controllers.ToastService;
import com.berniesanders.fieldthebern.dagger.FtbScreenScope;
import com.berniesanders.fieldthebern.dagger.MainComponent;
import com.berniesanders.fieldthebern.events.LoginEvent;
import com.berniesanders.fieldthebern.exceptions.NetworkUnavailableException;
import com.berniesanders.fieldthebern.models.CreateUserRequest;
import com.berniesanders.fieldthebern.models.ErrorResponse;
import com.berniesanders.fieldthebern.models.User;
import com.berniesanders.fieldthebern.models.UserAttributes;
import com.berniesanders.fieldthebern.mortar.FlowPathBase;
import com.berniesanders.fieldthebern.parsing.ErrorResponseParser;
import com.berniesanders.fieldthebern.repositories.UserRepo;
import com.berniesanders.fieldthebern.repositories.specs.UserSpec;
import com.berniesanders.fieldthebern.views.PhotoEditView;
import com.berniesanders.fieldthebern.views.SignupView;
import dagger.Provides;
import flow.Flow;
import flow.History;
import java.util.concurrent.TimeUnit;
import javax.inject.Inject;
import mortar.ViewPresenter;
import org.apache.commons.lang3.StringUtils;
import retrofit2.HttpException;
import rx.Observer;
import rx.Subscription;
import rx.android.schedulers.AndroidSchedulers;
import rx.functions.Action0;
import rx.schedulers.Schedulers;
import timber.log.Timber;

import static com.berniesanders.fieldthebern.parsing.FormValidator.isEmailValid;
import static com.berniesanders.fieldthebern.parsing.FormValidator.isNullOrBlank;

/**
 * Example for creating new Mortar Screen that helps explain how it all works
 *
 * Set the @Layout annotation to the resource id of the layout for the screen
 */
@Layout(R.layout.screen_signup)
public class SignupScreen extends FlowPathBase {

    private final UserAttributes userAttributes;

    /**
     */
    public SignupScreen(UserAttributes userAttributes) {
        this.userAttributes = userAttributes;
    }

    /**
     */
    @Override
    public Object createComponent() {
        return DaggerSignupScreen_Component.builder().mainComponent(FTBApplication.getComponent())
                .module(new Module(userAttributes)).build();
    }

    /**
     */
    @Override
    public String getScopeName() {
        return SignupScreen.class.getName();
    }

    @dagger.Module
    class Module {
        private final UserAttributes userAttributes;

        public Module(UserAttributes userAttributes) {
            this.userAttributes = userAttributes;
        }

        @FtbScreenScope
        @Provides
        public UserAttributes provideUserAttributes() {
            return userAttributes;
        }
    }

    /**
     */
    @FtbScreenScope
    @dagger.Component(dependencies = MainComponent.class, modules = Module.class)
    public interface Component {
        void inject(SignupView t);

        UserRepo userRepo();

        ErrorResponseParser errorParser();
    }

    @FtbScreenScope
    static public class Presenter extends ViewPresenter<SignupView> {

        @BindString(R.string.signup_title)
        String screenTitleString;
        @BindString(R.string.err_email_blank)
        String emailBlank;
        @BindString(R.string.err_password_blank)
        String passwordBlank;
        @BindString(R.string.err_your_first_name_blank)
        String firstNameBlank;
        @BindString(R.string.err_your_last_name_blank)
        String lastNameBlank;
        @BindString(R.string.err_invalid_email)
        String invalidEmailError;
        @BindString(R.string.location_disabled_title)
        String locationDisableTitle;
        @BindString(R.string.location_disabled_message)
        String locationDisabledBody;

        private final UserRepo repo;
        private UserAttributes userAttributes;
        private final ErrorResponseParser errorResponseParser;
        private final MessageScreen messageScreen;
        Bitmap userBitmap;

        private String stateCode;
        private Location location;
        private Subscription stateCodeSubscription;
        private Subscription locationSubscription;

        private boolean stateCodeRequestCompleted = false;
        private boolean locationRequestCompleted = false;

        @Bind(R.id.avatar_buttons)
        View avatarButtons;

        @Bind(R.id.avatar_container)
        View avatarContainer;

        @Bind(R.id.email)
        AutoCompleteTextView emailAutocompleteTV;

        @Bind(R.id.photo_edit)
        PhotoEditView photoEditView;
        private boolean showPleaseWait = false;

        @Inject
        Presenter(UserRepo repo, UserAttributes userAttributes, ErrorResponseParser errorResponseParser,
                MessageScreen messageScreen) {
            this.repo = repo;
            this.userAttributes = userAttributes;
            this.errorResponseParser = errorResponseParser;
            this.messageScreen = messageScreen;
        }

        @Override
        protected void onLoad(Bundle savedInstanceState) {
            Timber.v("onLoad");
            ButterKnife.bind(this, getView());
            setActionBar();

            if (userAttributes.isFacebookUser()) {
                getView().showFacebook(userAttributes);
                photoEditView.load(userAttributes, userBitmap);
            } else if (userBitmap != null) {
                // User may have set their own photo and persist through rotation
                photoEditView.load(userAttributes, userBitmap);
            } else {
                getView().postDelayed(new Runnable() {
                    @Override
                    public void run() {
                        photoEditView.toggleAvatarWidget(true);
                    }
                }, 300);
            }

            photoEditView.setPhotoChangeListener(new PhotoEditView.PhotoChangeListener() {
                @Override
                public void onPhotoChanged(Bitmap bitmap, String base64PhotoData) {
                    userBitmap = bitmap;
                    userAttributes.base64PhotoData(base64PhotoData);
                }
            });

            if (PermissionService.get(getView()).isGranted()) {

                if (!LocationService.get(getView()).isLocationEnabled()) {
                    showEnableLocationDialog();
                }
            } else {
                PermissionService.get(getView()).requestPermission();
            }

            getView().loadUserEmailAccounts(emailAutocompleteTV);

            if (showPleaseWait) {
                ProgressDialogService.get(getView()).show(R.string.please_wait);
            }
        }

        private void showEnableLocationDialog() {
            DialogController.DialogAction confirmAction = new DialogController.DialogAction()
                    .label(android.R.string.ok).action(new Action0() {
                        @Override
                        public void call() {
                            Timber.d("ok button click");
                            Intent myIntent = new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS);
                            getView().getContext().startActivity(myIntent);
                        }
                    });

            DialogService.get(getView()).setDialogConfig(new DialogController.DialogConfig()
                    .title(locationDisableTitle).message(locationDisabledBody).withActions(confirmAction));
        }

        @OnTouch(R.id.email_input_layout)
        boolean showEmailsInputLayout() {
            if (Build.VERSION.SDK_INT >= 21) {
                emailAutocompleteTV.showDropDown();
                emailAutocompleteTV.setFocusable(true);
                emailAutocompleteTV.setShowSoftInputOnFocus(true);
            }
            return false;
        }

        @OnTouch(R.id.email)
        boolean showEmails() {
            if (Build.VERSION.SDK_INT >= 21) {
                emailAutocompleteTV.showDropDown();
                emailAutocompleteTV.setFocusable(true);
                emailAutocompleteTV.setShowSoftInputOnFocus(true);
            }
            return false;
        }

        void setActionBar() {
            ActionBarService.get(getView()).showToolbar().closeAppbar().lockDrawer().setMainImage(null)
                    .setConfig(new ActionBarController.Config(screenTitleString, null));
        }

        @OnClick(R.id.submit)
        void onSubmit(final View v) {
            if (PermissionService.get(v).isGranted()) {
                if (!formIsValid()) {
                    return;
                }
                if (!LocationService.get(getView()).isLocationEnabled()) {
                    showEnableLocationDialog();
                    return;
                }
                ProgressDialogService.get(getView()).show(R.string.please_wait);
                showPleaseWait = true;
                userAttributes = getView().getInput(userAttributes);
                requestLocation();
            } else {
                // Display a SnackBar with an explanation and a button to trigger the request.
                Snackbar.make(getView(), R.string.permission_contacts_rationale, Snackbar.LENGTH_INDEFINITE)
                        .setAction(android.R.string.ok, new View.OnClickListener() {
                            @Override
                            public void onClick(View view) {
                                PermissionService.get(v).requestPermission();
                            }
                        }).show();
            }
        }

        private boolean formIsValid() {
            //last name is optional
            if (isNullOrBlank(getView().getFirstName())) {
                ToastService.get(getView()).bern(firstNameBlank, Toast.LENGTH_SHORT);
                return false;
            } else if (isNullOrBlank(getView().getLastName())) {
                ToastService.get(getView()).bern(lastNameBlank, Toast.LENGTH_SHORT);
                return false;
            } else if (isNullOrBlank(getView().getEmail())) {
                ToastService.get(getView()).bern(emailBlank, Toast.LENGTH_SHORT);
                return false;
            } else if (!isEmailValid(getView().getEmail().getText())) {
                ToastService.get(getView()).bern(invalidEmailError, Toast.LENGTH_SHORT);
                return false;
            } else if (isNullOrBlank(getView().getPassword())) {
                ToastService.get(getView()).bern(passwordBlank, Toast.LENGTH_SHORT);
                return false;
            }
            return true;
        }

        private boolean addressAndLocationSet() {
            return (stateCodeRequestCompleted && locationRequestCompleted);
        }

        private Observer<Location> locationObserver = new Observer<Location>() {
            @Override
            public void onCompleted() {
                locationRequestCompleted = true;
                if (addressAndLocationSet()) {
                    createEmailUser();
                }
            }

            @Override
            public void onError(Throwable e) {
                locationRequestCompleted = true;
                Timber.e(e, "locationObserver onError");
                if (addressAndLocationSet()) {
                    createEmailUser();
                }
            }

            @Override
            public void onNext(Location location) {
                Timber.v("location on next" + location);
                Presenter.this.location = location;
            }
        };

        private Observer<String> stateCodeObserver = new Observer<String>() {
            @Override
            public void onCompleted() {
                stateCodeRequestCompleted = true;
                if (addressAndLocationSet()) {
                    createEmailUser();
                }
            }

            @Override
            public void onError(Throwable e) {
                stateCodeRequestCompleted = true;
                Timber.e(e, "stateCodeObserver onError");
                if (addressAndLocationSet()) {
                    createEmailUser();
                }
            }

            @Override
            public void onNext(String stateCode) {
                Timber.v("stateCodeObserver on next" + stateCode);
                Presenter.this.stateCode = stateCode;
            }
        };

        private void requestLocation() {
            stateCodeSubscription = LocationService.get(getView()).getAddress().subscribe(stateCodeObserver);
            locationSubscription = LocationService.get(getView()).get().subscribe(locationObserver);
        }

        private void createEmailUser() {

            //if validate form...
            Timber.v("createEmailUser");

            if (location != null) {
                userAttributes.lat(location.getLatitude()).lng(location.getLongitude());
            }

            if (StringUtils.isNotBlank(stateCode)) {
                userAttributes.stateCode(stateCode);
            }

            CreateUserRequest createUserRequest = new CreateUserRequest().withAttributes(userAttributes);

            final UserSpec spec = new UserSpec().create(createUserRequest);

            repo.create(spec).subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread())
                    .timeout(15, TimeUnit.SECONDS).subscribe(observer);
        }

        Observer<User> observer = new Observer<User>() {
            @Override
            public void onCompleted() {
                Timber.d("createUserRequest done.");
            }

            @Override
            public void onError(Throwable e) {
                Timber.e(e, "createUserRequest error");
                if (getView() == null) {
                    return;
                }
                ProgressDialogService.get(getView()).dismiss();
                showPleaseWait = false;
                if (e instanceof HttpException) {
                    ErrorResponse errorResponse = errorResponseParser.parse((HttpException) e);
                    ToastService.get(getView()).bern(errorResponse.getAllDetails(), Toast.LENGTH_SHORT);
                }
                if (e instanceof NetworkUnavailableException) {
                    ToastService.get(getView())
                            .bern(getView().getResources().getString(R.string.err_internet_not_available));
                }
            }

            @Override
            public void onNext(User user) {
                Timber.d("user created! user: %s", user.toString());
                ProgressDialogService.get(getView()).dismiss();
                showPleaseWait = false;
                FTBApplication.getEventBus().post(new LoginEvent(LoginEvent.LOGIN, user));

                //Flow.get(getView()).setHistory(History.single(new HomeScreen()), Flow.Direction.FORWARD);

                Flow.get(getView()).setHistory(History.single(messageScreen.getMessageOrHome(location, stateCode)),
                        Flow.Direction.FORWARD);
            }
        };

        @Override
        protected void onSave(Bundle outState) {
        }

        @Override
        public void dropView(SignupView view) {
            super.dropView(view);
            ButterKnife.unbind(this);
        }
    }
}