com.reddyetwo.hashmypass.app.EditProfileActivity.java Source code

Java tutorial

Introduction

Here is the source code for com.reddyetwo.hashmypass.app.EditProfileActivity.java

Source

/*
 * Copyright 2014 Red Dye No. 2
 *
 * This file is part of Twik.
 *
 * Twik is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Twik is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Twik.  If not, see <http://www.gnu.org/licenses/>.
 */

package com.reddyetwo.hashmypass.app;

import android.app.AlertDialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.graphics.Typeface;
import android.os.Bundle;
import android.support.v4.app.NavUtils;
import android.support.v7.app.ActionBar;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.Toolbar;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Spinner;
import android.widget.Toast;

import com.reddyetwo.hashmypass.app.adapter.ColorPaletteAdapter;
import com.reddyetwo.hashmypass.app.data.PasswordType;
import com.reddyetwo.hashmypass.app.data.Profile;
import com.reddyetwo.hashmypass.app.data.ProfileSettings;
import com.reddyetwo.hashmypass.app.dialog.PasswordLengthDialogFragment;
import com.reddyetwo.hashmypass.app.util.Constants;
import com.reddyetwo.hashmypass.app.util.KeyboardManager;
import com.reddyetwo.hashmypass.app.util.ProfileFormInflater;
import com.reddyetwo.hashmypass.app.util.ProfileFormWatcher;

/**
 * Activity which allows editing or adding a profile
 */
public class EditProfileActivity extends AppCompatActivity {

    /**
     * Key for Profile ID extra received in the startActivity {@link android.content.Intent}
     */
    public static final String EXTRA_PROFILE_ID = "profile_id";

    /**
     * Activity result key which contains the ID of the added profile
     */
    public static final String RESULT_KEY_PROFILE_ID = "profile_id";

    /**
     * Key for saving/getting password length to/from saved instance state bundle
     */
    private static final String KEY_PASSWORD_LENGTH = "password_length";

    /**
     * Profile name {@link android.widget.EditText}
     */
    private EditText mNameEditText;

    /**
     * Private key {@link android.widget.EditText}
     */
    private EditText mPrivateKeyEditText;

    /**
     * Password length {@link android.widget.Spinner}
     */
    private Spinner mPasswordLengthSpinner;

    /**
     * Password type {@link android.widget.Spinner}
     */
    private Spinner mPasswordTypeSpinner;

    /**
     * {@link android.widget.Button} for saving the profile changes
     */
    private Button mSaveButton;

    /**
     * {@link android.widget.Button} for discarding changes
     */
    private Button mDiscardButton;

    /**
     * Palette to pick a profile color
     */
    private RecyclerView mColorPaletteRecyclerView;

    /**
     * Profile ID
     */
    private long mProfileId;

    /**
     * Name of the profile (before being modified in the form)
     */
    private String mOriginalName;

    /**
     * Password length selected when the activity is shown
     */
    private int mInitialPasswordLength;

    /**
     * Selected color index in the color palette
     */
    private int mColor;

    /**
     * The {@link com.reddyetwo.hashmypass.app.data.Profile} instance to operate on
     */
    private Profile mProfile;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        setContentView(R.layout.activity_edit_profile);

        if (!loadProfileData()) {
            return;
        }

        initializeView();
        initializeSettings(savedInstanceState);
        populateView();
    }

    @Override
    protected void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        outState.putInt(KEY_PASSWORD_LENGTH, Integer.parseInt((String) mPasswordLengthSpinner.getSelectedItem()));
    }

    @Override
    protected void onPause() {
        super.onPause();
        KeyboardManager.hide(this, getCurrentFocus());
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        if (mProfileId != Profile.NO_ID) {
            getMenuInflater().inflate(R.menu.edit_profile, menu);
            return true;
        } else {
            return super.onCreateOptionsMenu(menu);
        }
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        // Handle action bar item clicks here. The action bar will
        // automatically handle clicks on the Home/Up button, so long
        // as you specify a parent activity in AndroidManifest.xml.
        int id = item.getItemId();
        if (id == android.R.id.home) {
            NavUtils.navigateUpFromSameTask(this);
            return true;
        } else if (id == R.id.action_delete) {
            AlertDialog.Builder builder = new AlertDialog.Builder(this);
            builder.setTitle(getString(R.string.confirm_delete, mOriginalName));
            builder.setMessage(R.string.warning_lose_settings);
            builder.setPositiveButton(R.string.action_delete, new DialogInterface.OnClickListener() {
                @Override
                public void onClick(DialogInterface dialog, int which) {
                    if (ProfileSettings.deleteProfile(EditProfileActivity.this, mProfileId)) {
                        NavUtils.navigateUpFromSameTask(EditProfileActivity.this);
                    } else {
                        Log.e(TwikApplication.LOG_TAG, "Error deleting profile");
                    }
                }
            });
            builder.setNegativeButton(android.R.string.cancel, null);
            builder.create().show();

            return true;
        }
        return super.onOptionsItemSelected(item);
    }

    /**
     * Load the profile data.
     *
     * @return true if data has been successfully loaded, false otherwise
     */
    private boolean loadProfileData() {
        mProfileId = getIntent().getLongExtra(EXTRA_PROFILE_ID, Profile.NO_ID);

        if (mProfileId != Profile.NO_ID) {
            // Load profile from database
            mProfile = ProfileSettings.getProfile(this, mProfileId);
            if (mProfile == null) {
                // Profile not found
                NavUtils.navigateUpFromSameTask(EditProfileActivity.this);
                return false;
            }
        } else {
            mProfile = new Profile();
            setTitle(R.string.action_add_profile);
        }

        mColor = mProfile.getColorIndex();
        mOriginalName = mProfile.getName();
        return true;
    }

    private void initializeView() {
        addToolbar();

        mPasswordTypeSpinner = (Spinner) findViewById(R.id.password_type_spinner);
        mPasswordLengthSpinner = (Spinner) findViewById(R.id.password_length_spinner);
        mDiscardButton = (Button) findViewById(R.id.discard_button);
        mSaveButton = (Button) findViewById(R.id.save_button);
        mNameEditText = (EditText) findViewById(R.id.profile_name_text);
        mColorPaletteRecyclerView = (RecyclerView) findViewById(R.id.profile_color_palette);

        mPrivateKeyEditText = (EditText) findViewById(R.id.private_key_text);
        // Use a monospaced typeface for private key which allows distinguishing 0 from O.
        Typeface monospacedTypeface = Typeface.createFromAsset(getAssets(), Constants.FONT_MONOSPACE);
        mPrivateKeyEditText.setTypeface(monospacedTypeface);

        initializeColorPalette();
        addPasswordLengthTouchListener();
        addColorPaletteSelectedListener();
        addSaveButtonClickListener();
        addDiscardButtonClickListener();
        addFormChangedListener();
    }

    private void initializeColorPalette() {
        // Create adapter
        int[] normalColorList = getResources().getIntArray(R.array.color_palette_normal);
        int[] pressedColorList = getResources().getIntArray(R.array.color_palette_pressed);
        int[] rippleColorList = getResources().getIntArray(R.array.color_palette_ripple);
        ColorPaletteAdapter adapter = new ColorPaletteAdapter(normalColorList, pressedColorList, rippleColorList);
        mColorPaletteRecyclerView.setAdapter(adapter);
        mColorPaletteRecyclerView.setHasFixedSize(true);

        // Setup layout manager
        mColorPaletteRecyclerView
                .setLayoutManager(new LinearLayoutManager(this, LinearLayoutManager.HORIZONTAL, false));
    }

    private void initializeSettings(Bundle savedInstanceState) {
        mInitialPasswordLength = savedInstanceState != null ? savedInstanceState.getInt(KEY_PASSWORD_LENGTH)
                : mProfile.getPasswordLength();
    }

    private void populateView() {
        mNameEditText.setText(mOriginalName);
        mPrivateKeyEditText.setText(mProfile.getPrivateKey());
        ProfileFormInflater.populatePasswordLengthSpinner(this, mPasswordLengthSpinner, mInitialPasswordLength);
        ProfileFormInflater.populatePasswordTypeSpinner(this, mPasswordTypeSpinner, mProfile.getPasswordType());

        ColorPaletteAdapter adapter = (ColorPaletteAdapter) mColorPaletteRecyclerView.getAdapter();
        adapter.selectItem(mColor);
        mColorPaletteRecyclerView.scrollToPosition(mColor);
    }

    private void addPasswordLengthTouchListener() {
        mPasswordLengthSpinner
                .setOnTouchListener(new MovementTouchListener(this, new MovementTouchListener.OnPressedListener() {
                    @Override
                    public void onPressed() {
                        showPasswordLengthDialog();
                    }
                }));
    }

    private void addColorPaletteSelectedListener() {
        ColorPaletteAdapter adapter = (ColorPaletteAdapter) mColorPaletteRecyclerView.getAdapter();
        adapter.setOnColorSelectedListener(new ColorPaletteAdapter.OnColorSelectedListener() {
            @Override
            public void onColorSelected(int color) {
                mColor = color;
            }
        });
    }

    private void addFormChangedListener() {
        ProfileFormWatcher profileFormWatcher = new ProfileFormWatcher(getApplicationContext(), mProfileId,
                mNameEditText, mPrivateKeyEditText, mSaveButton);
        mNameEditText.addTextChangedListener(profileFormWatcher);
        mPrivateKeyEditText.addTextChangedListener(profileFormWatcher);
    }

    private void addSaveButtonClickListener() {
        mSaveButton.setOnClickListener(new SaveButtonClickListener());
    }

    private void addDiscardButtonClickListener() {
        mDiscardButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                setResult(RESULT_CANCELED);
                NavUtils.navigateUpFromSameTask(EditProfileActivity.this);
            }
        });
    }

    /**
     * Adds the toolbar to the activity. The toolbar is configured so it shows the navigation
     * control and the subtitle.
     */
    private void addToolbar() {
        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);
        ActionBar actionBar = getSupportActionBar();
        if (actionBar != null) {
            actionBar.setDisplayHomeAsUpEnabled(true);
            actionBar.setSubtitle(mProfile.getName());
        }
    }

    /**
     * Shows a number picker dialog for choosing the password length
     */
    private void showPasswordLengthDialog() {
        PasswordLengthDialogFragment dialogFragment = new PasswordLengthDialogFragment();
        dialogFragment.setPasswordLength(Integer.parseInt((String) mPasswordLengthSpinner.getSelectedItem()));
        dialogFragment.setOnSelectedListener(new PasswordLengthDialogFragment.OnSelectedListener() {
            @Override
            public void onPasswordLengthSelected(int length) {
                ProfileFormInflater.populatePasswordLengthSpinner(EditProfileActivity.this, mPasswordLengthSpinner,
                        length);
            }
        });

        dialogFragment.show(getFragmentManager(), "passwordLength");
    }

    private class SaveButtonClickListener implements View.OnClickListener {
        @Override
        public void onClick(View v) {
            // Update profile in the database
            Profile profile = new Profile(mProfileId, mNameEditText.getText().toString(),
                    mPrivateKeyEditText.getText().toString(),
                    Integer.decode((String) mPasswordLengthSpinner.getSelectedItem()),
                    PasswordType.values()[mPasswordTypeSpinner.getSelectedItemPosition()], mColor);
            if (mProfileId == Profile.NO_ID) {
                long profileId = ProfileSettings.insertProfile(EditProfileActivity.this, profile);
                if (profileId == -1) {
                    setResult(RESULT_CANCELED);
                } else {
                    Intent resultIntent = new Intent();
                    resultIntent.putExtra(RESULT_KEY_PROFILE_ID, profileId);
                    setResult(RESULT_OK, resultIntent);
                }
            } else if (!ProfileSettings.updateProfile(EditProfileActivity.this, profile)) {
                Log.e(TwikApplication.LOG_TAG, "Error updating profile");
                Toast.makeText(EditProfileActivity.this, R.string.error, Toast.LENGTH_LONG).show();
            }

            // Go to the parent activity
            NavUtils.navigateUpFromSameTask(EditProfileActivity.this);
        }
    }
}