package com.dozuki.ifixit.ui.guide.create; import android.content.Intent; import android.content.pm.ActivityInfo; import android.os.Bundle; import android.support.v4.app.Fragment; import android.support.v4.app.FragmentManager; import android.support.v4.app.FixedFragmentStatePagerAdapter; import android.support.v4.view.ViewPager; import android.util.TypedValue; import android.view.View; import android.view.ViewGroup; import android.widget.Button; import com.dozuki.ifixit.App; import com.dozuki.ifixit.R; import com.dozuki.ifixit.model.guide.Guide; import com.dozuki.ifixit.model.guide.GuideStep; import com.dozuki.ifixit.model.guide.StepLine; import com.dozuki.ifixit.model.guide.wizard.AbstractWizardModel; import com.dozuki.ifixit.model.guide.wizard.EditTextPage; import com.dozuki.ifixit.model.guide.wizard.GuideTitlePage; import com.dozuki.ifixit.model.guide.wizard.ModelCallbacks; import com.dozuki.ifixit.model.guide.wizard.Page; import com.dozuki.ifixit.model.guide.wizard.TopicNamePage; import com.dozuki.ifixit.ui.BaseMenuDrawerActivity; import com.dozuki.ifixit.ui.guide.create.wizard.PageFragmentCallbacks; import com.dozuki.ifixit.ui.guide.create.wizard.ReviewFragment; import com.dozuki.ifixit.ui.guide.create.wizard.StepPagerStrip; import com.dozuki.ifixit.util.api.ApiCall; import com.dozuki.ifixit.util.api.ApiError; import com.dozuki.ifixit.util.api.ApiEvent; import com.dozuki.ifixit.util.api.Api; import com.google.analytics.tracking.android.Fields; import com.google.analytics.tracking.android.MapBuilder; import com.squareup.otto.Subscribe; import java.util.ArrayList; import java.util.List; public class GuideIntroActivity extends BaseMenuDrawerActivity implements PageFragmentCallbacks, ReviewFragment.Callbacks, ModelCallbacks { public static final int GUIDE_STEP_EDIT_REQUEST = 1; public static final String STATE_KEY = "STATE_KEY"; private ViewPager mPager; private FormWizardPagerAdapter mPagerAdapter; private AbstractWizardModel mWizardModel; private Button mNextButton; private Button mPrevButton; private List<Page> mCurrentPageSequence; private StepPagerStrip mStepPagerStrip; private Bundle mWizardModelBundle; private Guide mGuide; private boolean mEditingAfterReview; private boolean mEditIntroState = false; private boolean mConsumePageSelectedEvent; private View.OnClickListener mNextButtonClickListener = new View.OnClickListener() { @Override public void onClick(View view) { if (mPager.getCurrentItem() == mCurrentPageSequence.size()) { showLoading(R.id.intro_loading_container, getString(R.string.saving)); Bundle bundle = mWizardModel.save(); if (mEditIntroState) { Api.call(GuideIntroActivity.this, ApiCall.editGuide(bundle, mGuide.getGuideid(), mGuide.getRevisionid())); } else { Api.call(GuideIntroActivity.this, ApiCall.createGuideFromBundle(bundle)); } } else { if (mEditingAfterReview) { mPager.setCurrentItem(mPagerAdapter.getCount() - 1); } else { mPager.setCurrentItem(mPager.getCurrentItem() + 1); } } } }; private View.OnClickListener mPrevButtonClickListener = new View.OnClickListener() { @Override public void onClick(View view) { mPager.setCurrentItem(mPager.getCurrentItem() - 1); } }; private ViewPager.SimpleOnPageChangeListener mPageChangeListener = new ViewPager.SimpleOnPageChangeListener() { @Override public void onPageSelected(int position) { mStepPagerStrip.setCurrentPage(position); if (mConsumePageSelectedEvent) { mConsumePageSelectedEvent = false; return; } mEditingAfterReview = false; updateBottomBar(); } }; private StepPagerStrip.OnPageSelectedListener mPageSelectedListener = new StepPagerStrip.OnPageSelectedListener() { @Override public void onPageStripSelected(int position) { position = Math.min(mPagerAdapter.getCount() - 1, position); if (mPager.getCurrentItem() != position) { mPager.setCurrentItem(position); } } }; ///////////////////////////////////////////////////// // LIFECYCLE ///////////////////////////////////////////////////// @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.guide_create_intro); if (App.get().isScreenLarge()) { setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED); } else { setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT); } Bundle extras = getIntent().getExtras(); if (extras != null) { mGuide = (Guide) extras.getSerializable(StepsActivity.GUIDE_KEY); mEditIntroState = extras.getBoolean(GuideIntroActivity.STATE_KEY); mWizardModelBundle = buildIntroBundle(); } else if (savedInstanceState != null) { mGuide = (Guide) savedInstanceState.getSerializable(StepsActivity.GUIDE_KEY); mWizardModelBundle = savedInstanceState.getBundle("model"); } if (App.get().getSite().mGuideTypes == null) { Api.call(this, ApiCall.siteInfo()); } else { initWizard(); } App.sendScreenView("/user/guides/" + mGuide.getGuideid() + "/details"); } private Bundle buildIntroBundle() { Bundle bundle = new Bundle(); App app = App.get(); String type = mGuide.getType().toLowerCase(); String subjectBundleKey; Bundle topicBundle = new Bundle(); topicBundle.putString(TopicNamePage.TOPIC_DATA_KEY, mGuide.getTopic()); Bundle typeBundle = new Bundle(); typeBundle.putString(Page.SIMPLE_DATA_KEY, type); Bundle titleBundle = new Bundle(); titleBundle.putString(GuideTitlePage.TITLE_DATA_KEY, mGuide.getTitle()); Bundle summaryBundle = new Bundle(); summaryBundle.putString(EditTextPage.TEXT_DATA_KEY, mGuide.getSummary()); Bundle introductionBundle = new Bundle(); introductionBundle.putString(EditTextPage.TEXT_DATA_KEY, mGuide.getIntroductionRaw()); Bundle subjectBundle = new Bundle(); subjectBundle.putString(EditTextPage.TEXT_DATA_KEY, mGuide.getSubject()); if (type.equals("replacement") || type.equals("introduction") || type.equals("disassembly") || type.equals("repair")) { subjectBundleKey = GuideIntroWizardModel.HAS_SUBJECT_KEY + ":" + app.getString(R.string .guide_intro_wizard_guide_subject_title); } else { subjectBundleKey = GuideIntroWizardModel.NO_SUBJECT_KEY + ":" + app.getString(R.string .guide_intro_wizard_guide_subject_title); } String topicBundleKey = app.getString(R.string.guide_intro_wizard_guide_topic_title, app.getTopicName()); bundle.putBundle(subjectBundleKey, subjectBundle); bundle.putBundle(app.getString(R.string.guide_intro_wizard_guide_type_title), typeBundle); bundle.putBundle(topicBundleKey, topicBundle); bundle.putBundle(app.getString(R.string.guide_intro_wizard_guide_title_title), titleBundle); bundle.putBundle(app.getString(R.string.guide_intro_wizard_guide_introduction_title), introductionBundle); bundle.putBundle(app.getString(R.string.guide_intro_wizard_guide_summary_title), summaryBundle); return bundle; } protected void initWizard() { mWizardModel = mEditIntroState ? new GuideIntroEditWizardModel(this) : new GuideIntroWizardModel(this); if (mWizardModelBundle != null) { mWizardModel.load(mWizardModelBundle); } mWizardModel.registerListener(this); mCurrentPageSequence = mWizardModel.getCurrentPageSequence(); mPager = (ViewPager) findViewById(R.id.pager); mStepPagerStrip = (StepPagerStrip) findViewById(R.id.strip); mNextButton = (Button) findViewById(R.id.next_button); mPrevButton = (Button) findViewById(R.id.prev_button); mStepPagerStrip.setOnPageSelectedListener(mPageSelectedListener); mPagerAdapter = new FormWizardPagerAdapter(getSupportFragmentManager()); mPager.setAdapter(mPagerAdapter); mPager.setOnPageChangeListener(mPageChangeListener); mNextButton.setOnClickListener(mNextButtonClickListener); mPrevButton.setOnClickListener(mPrevButtonClickListener); onPageTreeChanged(); updateBottomBar(); // If we're editing an existing guides details, start at the Review page. if (mEditIntroState) { mPager.setCurrentItem(mPagerAdapter.getCount() - 1); } } @Override public void onPageTreeChanged() { mCurrentPageSequence = mWizardModel.getCurrentPageSequence(); recalculateCutOffPage(); mStepPagerStrip.setPageCount(mCurrentPageSequence.size() + 1); // + 1 = review step mPagerAdapter.notifyDataSetChanged(); updateBottomBar(); } @Override public void onDestroy() { super.onDestroy(); // Null check is required to prevent null pointer exceptions when a user is not logged in and cancels login. if (mWizardModel != null) { mWizardModel.unregisterListener(this); } } @Override protected void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); if (mWizardModel != null) { outState.putBundle("model", mWizardModel.save()); } outState.putSerializable(StepsActivity.GUIDE_KEY, mGuide); outState.putBoolean(GuideIntroActivity.STATE_KEY, mEditIntroState); } @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 boolean finishActivityIfLoggedOut() { return true; } @Override public void onPageDataChanged(Page page) { if (page.isRequired()) { if (recalculateCutOffPage()) { mPagerAdapter.notifyDataSetChanged(); updateBottomBar(); } } } @Override public Page onGetPage(String key) { return mWizardModel.findByKey(key); } ///////////////////////////////////////////////////// // NOTIFICATION LISTENERS ///////////////////////////////////////////////////// @Subscribe public void onSiteInfo(ApiEvent.SiteInfo event) { if (!event.hasError()) { App.get().setSite(event.getResult()); initWizard(); } else { Api.getErrorDialog(this, event).show(); } } @Subscribe public void onGuideCreated(ApiEvent.CreateGuide event) { if (!event.hasError()) { Guide guide = event.getResult(); GuideStep step = new GuideStep(); step.addLine(new StepLine()); ArrayList<GuideStep> initialStepList = new ArrayList<GuideStep>(); initialStepList.add(step); guide.setStepList(initialStepList); hideLoading(); Intent intent = new Intent(this, StepEditActivity.class); intent.putExtra(StepsActivity.GUIDE_KEY, guide); intent.putExtra(StepEditActivity.GUIDE_STEP_NUM_KEY, 0); startActivityForResult(intent, GUIDE_STEP_EDIT_REQUEST); finish(); } else { hideChildren(false); Api.getErrorDialog(this, event).show(); } } @Subscribe public void onGuideEdited(ApiEvent.EditGuide event) { if (!event.hasError()) { Guide guide = event.getResult(); hideLoading(); Intent intent = new Intent(this, StepsActivity.class); intent.putExtra(StepsActivity.GUIDE_KEY, guide); startActivityForResult(intent, GUIDE_STEP_EDIT_REQUEST); finish(); } else { if (event.getError().mType == ApiError.Type.CONFLICT) { mGuide = event.getResult(); mWizardModelBundle = buildIntroBundle(); mWizardModel.load(mWizardModelBundle); onPageTreeChanged(); hideLoading(); } Api.getErrorDialog(this, event).show(); } } ///////////////////////////////////////////////////// // ADAPTERS ///////////////////////////////////////////////////// public class FormWizardPagerAdapter extends FixedFragmentStatePagerAdapter { private int mCutOffPage; private Fragment mPrimaryItem; public FormWizardPagerAdapter(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.size() + 1); } public void setCutOffPage(int cutOffPage) { if (cutOffPage < 0) { cutOffPage = Integer.MAX_VALUE; } mCutOffPage = cutOffPage; } public int getCutOffPage() { return mCutOffPage; } } ///////////////////////////////////////////////////// // HELPERS ///////////////////////////////////////////////////// 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; } private void updateBottomBar() { int position = mPager.getCurrentItem(); if (position == mCurrentPageSequence.size()) { mNextButton.setText(R.string.finish); mNextButton.setBackgroundResource(R.drawable.wizard_finish_background); mNextButton.setTextAppearance(this, R.style.WizardFinishTextAppearance); } else { mNextButton.setText(mEditingAfterReview ? R.string.review : R.string.next); mNextButton.setBackgroundResource(R.drawable.wizard_selectable_item_background); TypedValue v = new TypedValue(); getTheme().resolveAttribute(android.R.attr.textAppearanceMedium, v, true); mNextButton.setTextAppearance(this, v.resourceId); mNextButton.setEnabled(position != mPagerAdapter.getCutOffPage()); } mPrevButton.setVisibility(position <= 0 ? View.INVISIBLE : View.VISIBLE); } private void hideChildren(boolean hide) { int visibility = hide ? View.GONE : View.VISIBLE; mStepPagerStrip.setVisibility(visibility); mNextButton.setVisibility(visibility); mPrevButton.setVisibility(visibility); } @Override public void showLoading(int container, String message) { hideChildren(true); if (findViewById(container).getVisibility() == View.GONE) { findViewById(container).setVisibility(View.VISIBLE); } super.showLoading(container, message); } @Override public void hideLoading() { super.hideLoading(); findViewById(R.id.intro_loading_container).setVisibility(View.GONE); hideChildren(false); } }