org.gnucash.android.ui.wizard.FirstRunWizardActivity.java Source code

Java tutorial

Introduction

Here is the source code for org.gnucash.android.ui.wizard.FirstRunWizardActivity.java

Source

/*
 * Copyright 2012 Roman Nurik
 * Copyright 2012 Ngewi Fet
 *
 * 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 org.gnucash.android.ui.wizard;

import android.Manifest;
import android.app.Activity;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.PackageManager;
import android.content.res.Resources;
import android.graphics.drawable.ColorDrawable;
import android.os.Build;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentStatePagerAdapter;
import android.support.v4.view.ViewPager;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.AppCompatButton;
import android.util.TypedValue;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;

import com.tech.freak.wizardpager.model.AbstractWizardModel;
import com.tech.freak.wizardpager.model.ModelCallbacks;
import com.tech.freak.wizardpager.model.Page;
import com.tech.freak.wizardpager.model.ReviewItem;
import com.tech.freak.wizardpager.ui.PageFragmentCallbacks;
import com.tech.freak.wizardpager.ui.ReviewFragment;
import com.tech.freak.wizardpager.ui.StepPagerStrip;

import org.gnucash.android.R;
import org.gnucash.android.app.GnuCashApplication;
import org.gnucash.android.db.adapter.BooksDbAdapter;
import org.gnucash.android.ui.account.AccountsActivity;
import org.gnucash.android.ui.util.TaskDelegate;

import java.util.ArrayList;
import java.util.List;

import butterknife.BindView;
import butterknife.ButterKnife;

/**
 * Activity for managing the wizard displayed upon first run of the application
 */
public class FirstRunWizardActivity extends AppCompatActivity
        implements PageFragmentCallbacks, ReviewFragment.Callbacks, ModelCallbacks {

    @BindView(R.id.pager)
    ViewPager mPager;
    private MyPagerAdapter mPagerAdapter;

    private boolean mEditingAfterReview;

    private AbstractWizardModel mWizardModel;

    private boolean mConsumePageSelectedEvent;

    @BindView(R.id.btn_save)
    AppCompatButton mNextButton;
    @BindView(R.id.btn_cancel)
    Button mPrevButton;
    @BindView(R.id.strip)
    StepPagerStrip mStepPagerStrip;

    private List<Page> mCurrentPageSequence;
    private String mAccountOptions;
    private String mCurrencyCode;

    public void onCreate(Bundle savedInstanceState) {
        // we need to construct the wizard model before we call super.onCreate, because it's used in
        // onGetPage (which is indirectly called through super.onCreate if savedInstanceState is not
        // null)
        mWizardModel = createWizardModel(savedInstanceState);

        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_first_run_wizard);
        ButterKnife.bind(this);

        setTitle(getString(R.string.title_setup_gnucash));

        mPagerAdapter = new MyPagerAdapter(getSupportFragmentManager());
        mPager.setAdapter(mPagerAdapter);
        mStepPagerStrip.setOnPageSelectedListener(new StepPagerStrip.OnPageSelectedListener() {
            @Override
            public void onPageStripSelected(int position) {
                position = Math.min(mPagerAdapter.getCount() - 1, position);
                if (mPager.getCurrentItem() != position) {
                    mPager.setCurrentItem(position);
                }
            }
        });

        mPager.setOnPageChangeListener(new ViewPager.SimpleOnPageChangeListener() {
            @Override
            public void onPageSelected(int position) {
                mStepPagerStrip.setCurrentPage(position);

                if (mConsumePageSelectedEvent) {
                    mConsumePageSelectedEvent = false;
                    return;
                }

                mEditingAfterReview = false;
                updateBottomBar();
            }
        });

        mNextButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                if (mPager.getCurrentItem() == mCurrentPageSequence.size()) {
                    ArrayList<ReviewItem> reviewItems = new ArrayList<>();
                    for (Page page : mCurrentPageSequence) {
                        page.getReviewItems(reviewItems);
                    }

                    mCurrencyCode = GnuCashApplication.getDefaultCurrencyCode();
                    mAccountOptions = getString(R.string.wizard_option_let_me_handle_it); //default value, do nothing
                    String feedbackOption = getString(R.string.wizard_option_disable_crash_reports);
                    for (ReviewItem reviewItem : reviewItems) {
                        String title = reviewItem.getTitle();
                        if (title.equals(getString(R.string.wizard_title_default_currency))) {
                            mCurrencyCode = reviewItem.getDisplayValue();
                        } else if (title.equals(getString(R.string.wizard_title_select_currency))) {
                            mCurrencyCode = reviewItem.getDisplayValue();
                        } else if (title.equals(getString(R.string.wizard_title_account_setup))) {
                            mAccountOptions = reviewItem.getDisplayValue();
                        } else if (title.equals(getString(R.string.wizard_title_feedback_options))) {
                            feedbackOption = reviewItem.getDisplayValue();
                        }
                    }

                    GnuCashApplication.setDefaultCurrencyCode(mCurrencyCode);
                    SharedPreferences preferences = PreferenceManager
                            .getDefaultSharedPreferences(FirstRunWizardActivity.this);
                    SharedPreferences.Editor preferenceEditor = preferences.edit();

                    if (feedbackOption.equals(getString(R.string.wizard_option_auto_send_crash_reports))) {
                        preferenceEditor.putBoolean(getString(R.string.key_enable_crashlytics), true);
                    } else {
                        preferenceEditor.putBoolean(getString(R.string.key_enable_crashlytics), false);
                    }
                    preferenceEditor.apply();

                    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                        requestPermissions(
                                new String[] { Manifest.permission.WRITE_EXTERNAL_STORAGE,
                                        Manifest.permission.READ_EXTERNAL_STORAGE },
                                AccountsActivity.REQUEST_PERMISSION_WRITE_SD_CARD);
                    } else { //on other version of Android, just proceed with processing. On Android M, we import when permission grant returns
                        createAccountsAndFinish();
                    }
                } else {
                    if (mEditingAfterReview) {
                        mPager.setCurrentItem(mPagerAdapter.getCount() - 1);
                    } else {
                        mPager.setCurrentItem(mPager.getCurrentItem() + 1);
                    }
                }
            }
        });

        mPrevButton.setText(R.string.wizard_btn_back);
        TypedValue v = new TypedValue();
        getTheme().resolveAttribute(android.R.attr.textAppearanceMedium, v, true);
        mPrevButton.setTextAppearance(this, v.resourceId);
        mNextButton.setTextAppearance(this, v.resourceId);

        mPrevButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                mPager.setCurrentItem(mPager.getCurrentItem() - 1);
            }
        });

        onPageTreeChanged();
        updateBottomBar();
    }

    /**
     * Create the wizard model for the activity, taking into accoun the savedInstanceState if it
     * exists (and if it contains a "model" key that we can use).
     * @param savedInstanceState    the instance state available in {{@link #onCreate(Bundle)}}
     * @return  an appropriate wizard model for this activity
     */
    private AbstractWizardModel createWizardModel(Bundle savedInstanceState) {
        AbstractWizardModel model = new FirstRunWizardModel(this);
        if (savedInstanceState != null) {
            Bundle wizardModel = savedInstanceState.getBundle("model");
            if (wizardModel != null) {
                model.load(wizardModel);
            }
        }
        model.registerListener(this);
        return model;
    }

    /**
     * Create accounts depending on the user preference (import or default set) and finish this activity
     * <p>This method also removes the first run flag from the application</p>
     */
    private void createAccountsAndFinish() {
        AccountsActivity.removeFirstRunFlag();

        if (mAccountOptions.equals(getString(R.string.wizard_option_create_default_accounts))) {
            //save the UID of the active book, and then delete it after successful import
            String bookUID = BooksDbAdapter.getInstance().getActiveBookUID();
            AccountsActivity.createDefaultAccounts(mCurrencyCode, FirstRunWizardActivity.this);
            BooksDbAdapter.getInstance().deleteBook(bookUID); //a default book is usually created
            finish();
        } else if (mAccountOptions.equals(getString(R.string.wizard_option_import_my_accounts))) {
            AccountsActivity.startXmlFileChooser(this);
        } else { //user prefers to handle account creation themselves
            AccountsActivity.start(this);
            finish();
        }
    }

    @Override
    public void onPageTreeChanged() {
        mCurrentPageSequence = mWizardModel.getCurrentPageSequence();
        recalculateCutOffPage();
        mStepPagerStrip.setPageCount(mCurrentPageSequence.size() + 1); // + 1 =
        // review
        // step
        mPagerAdapter.notifyDataSetChanged();
        updateBottomBar();
    }

    private void updateBottomBar() {
        int position = mPager.getCurrentItem();
        final Resources res = getResources();
        if (position == mCurrentPageSequence.size()) {
            mNextButton.setText(R.string.btn_wizard_finish);

            mNextButton.setBackgroundDrawable(new ColorDrawable(res.getColor(R.color.theme_accent)));
            mNextButton.setTextColor(res.getColor(android.R.color.white));
        } else {
            mNextButton.setText(mEditingAfterReview ? R.string.review : R.string.btn_wizard_next);
            mNextButton.setBackgroundDrawable(new ColorDrawable(res.getColor(android.R.color.transparent)));
            mNextButton.setTextColor(res.getColor(R.color.theme_accent));
            mNextButton.setEnabled(position != mPagerAdapter.getCutOffPage());
        }

        mPrevButton.setVisibility(position <= 0 ? View.INVISIBLE : View.VISIBLE);
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        switch (requestCode) {
        case AccountsActivity.REQUEST_PICK_ACCOUNTS_FILE:
            if (resultCode == Activity.RESULT_OK && data != null) {
                AccountsActivity.importXmlFileFromIntent(this, data, new TaskDelegate() {
                    @Override
                    public void onTaskComplete() {
                        finish();
                    }
                });
            }
            break;
        }
    }

    @Override
    public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
        switch (requestCode) {
        case AccountsActivity.REQUEST_PERMISSION_WRITE_SD_CARD: {
            if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                createAccountsAndFinish();
            } else {
                // permission denied, boo!
                // nothing to see here, move along
                finish();
                AccountsActivity.removeFirstRunFlag();
            }
        }
        }

    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        mWizardModel.unregisterListener(this);
    }

    @Override
    protected void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        outState.putBundle("model", mWizardModel.save());
    }

    @Override
    public AbstractWizardModel onGetModel() {
        return mWizardModel;
    }

    @Override
    public void onEditScreenAfterReview(String key) {
        for (int i = mCurrentPageSequence.size() - 1; i >= 0; i--) {
            if (mCurrentPageSequence.get(i).getKey().equals(key)) {
                mConsumePageSelectedEvent = true;
                mEditingAfterReview = true;
                mPager.setCurrentItem(i);
                updateBottomBar();
                break;
            }
        }
    }

    @Override
    public void onPageDataChanged(Page page) {
        if (page.isRequired()) {
            if (recalculateCutOffPage()) {
                mPagerAdapter.notifyDataSetChanged();
                updateBottomBar();
            }
        }
    }

    @Override
    public Page onGetPage(String key) {
        return mWizardModel.findByKey(key);
    }

    private boolean recalculateCutOffPage() {
        // Cut off the pager adapter at first required page that isn't completed
        int cutOffPage = mCurrentPageSequence.size() + 1;
        for (int i = 0; i < mCurrentPageSequence.size(); i++) {
            Page page = mCurrentPageSequence.get(i);
            if (page.isRequired() && !page.isCompleted()) {
                cutOffPage = i;
                break;
            }
        }

        if (mPagerAdapter.getCutOffPage() != cutOffPage) {
            mPagerAdapter.setCutOffPage(cutOffPage);
            return true;
        }

        return false;
    }

    public class MyPagerAdapter extends FragmentStatePagerAdapter {
        private int mCutOffPage;
        private Fragment mPrimaryItem;

        public MyPagerAdapter(FragmentManager fm) {
            super(fm);
        }

        @Override
        public Fragment getItem(int i) {
            if (i >= mCurrentPageSequence.size()) {
                return new ReviewFragment();
            }

            return mCurrentPageSequence.get(i).createFragment();
        }

        @Override
        public int getItemPosition(Object object) {
            // TODO: be smarter about this
            if (object == mPrimaryItem) {
                // Re-use the current fragment (its position never changes)
                return POSITION_UNCHANGED;
            }

            return POSITION_NONE;
        }

        @Override
        public void setPrimaryItem(ViewGroup container, int position, Object object) {
            super.setPrimaryItem(container, position, object);
            mPrimaryItem = (Fragment) object;
        }

        @Override
        public int getCount() {
            return Math.min(mCutOffPage + 1, mCurrentPageSequence == null ? 1 : mCurrentPageSequence.size() + 1);
        }

        public void setCutOffPage(int cutOffPage) {
            if (cutOffPage < 0) {
                cutOffPage = Integer.MAX_VALUE;
            }
            mCutOffPage = cutOffPage;
        }

        public int getCutOffPage() {
            return mCutOffPage;
        }
    }
}