Java tutorial
/* * 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; } } }