Java tutorial
/* * Copyright (C) 2012-2014 Gregory S. Meiste <http://gregmeiste.com> * * 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.meiste.greg.ptw.tab; import java.util.Calendar; import android.app.Activity; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.SharedPreferences; import android.os.Bundle; import android.support.v4.app.Fragment; import android.text.format.DateUtils; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.AdapterView; import android.widget.AdapterView.OnItemClickListener; import android.widget.AdapterView.OnItemSelectedListener; import android.widget.ListView; import android.widget.Spinner; import android.widget.Toast; import com.meiste.greg.ptw.Analytics; import com.meiste.greg.ptw.GAE; import com.meiste.greg.ptw.GAE.GaeListener; import com.meiste.greg.ptw.PTW; import com.meiste.greg.ptw.PlayerAdapter; import com.meiste.greg.ptw.PlayerHistory; import com.meiste.greg.ptw.QuestionAlarm; import com.meiste.greg.ptw.QuestionsRaceAdapter; import com.meiste.greg.ptw.R; import com.meiste.greg.ptw.Race; import com.meiste.greg.ptw.Util; public final class Questions extends TabFragment implements GaeListener { public final static String QCACHE = "question_cache"; public final static String ACACHE = "answer_cache"; public final static String CACACHE = "correct_answer_cache"; private boolean mSetupNeeded; private boolean mChanged = false; private Race mRaceNext; private Race mRaceSelected = null; private boolean mFailedConnect = false; private boolean mSending = false; private long mSubFragmentTime = 0; private long mAccountSetupTime = 0; private Spinner mRaceSpinner = null; private ListView mRaceList = null; private QuestionsRaceAdapter mRaceAdapter; public final static String cachePrefix() { return Calendar.getInstance().get(Calendar.YEAR) + "_race"; } @Override public View onCreateView(final LayoutInflater inflater, final ViewGroup container, final Bundle savedInstanceState) { mRaceNext = Race.getNext(getActivity(), false, true); mSetupNeeded = GAE.isAccountSetupNeeded(getActivity()); mChanged = false; mRaceAdapter = new QuestionsRaceAdapter(getActivity()); mAccountSetupTime = Util.getAccountSetupTime(getActivity()); setRetainInstance(true); if ((mRaceSelected == null) && (mRaceAdapter.getCount() > 0)) { mRaceSelected = mRaceAdapter.getItem(mRaceAdapter.getCount() - 1); } if (mSetupNeeded) { return Util.getAccountSetupView(getActivity(), inflater, container); } else if (mRaceSelected == null) { return inflater.inflate(R.layout.questions_no_race, container, false); } final View v = inflater.inflate(R.layout.questions, container, false); final int position = mRaceAdapter.getPosition(mRaceSelected); mRaceSpinner = (Spinner) v.findViewById(R.id.race_spinner); if (mRaceSpinner != null) { mRaceSpinner.setAdapter(mRaceAdapter); mRaceSpinner.setSelection(position); mRaceSpinner.setOnItemSelectedListener(new RaceSelectedListener()); } else { mRaceList = (ListView) v.findViewById(R.id.race_list); if (mRaceList != null) { mRaceList.setAdapter(mRaceAdapter); mRaceList.setChoiceMode(ListView.CHOICE_MODE_SINGLE); mRaceList.setSelection(position); mRaceList.setItemChecked(position, true); mRaceList.setOnItemClickListener(new RaceSelectedListener()); } } setSubFragment(); return v; } private String getJson(final String from) { final SharedPreferences cache = getActivity().getSharedPreferences(from, Activity.MODE_PRIVATE); return cache.getString(cachePrefix() + mRaceSelected.getId(), null); } public void setSubFragment() { mSubFragmentTime = System.currentTimeMillis(); // Sanity check to verify time has not gone backwards (must likely due // to user manually setting back time). This is needed so the check in // onResume() will not continuously (and incorrectly) set mChanged=true if (mSubFragmentTime < PlayerHistory.getTime(getActivity())) { Util.log("Reseting player history time to " + mSubFragmentTime); PlayerHistory.setTime(getActivity(), mSubFragmentTime); } boolean selectEnable = true; final Fragment f; final String ftag; if (mSending) { selectEnable = false; ftag = QuestionsConnecting.class.getName(); f = QuestionsConnecting.getInstance(getChildFragmentManager(), ftag); } else if (mFailedConnect) { mFailedConnect = false; ftag = QuestionsConnectFail.class.getName(); f = QuestionsConnectFail.getInstance(getChildFragmentManager(), ftag); } else if (mRaceSelected.isRecent() && getJson(QCACHE) != null && getJson(ACACHE) == null) { ftag = QuestionsLate.class.getName(); f = QuestionsLate.getInstance(getChildFragmentManager(), ftag); } else if (mRaceSelected.inProgress() || !mRaceSelected.isFuture()) { final String qjson = getJson(QCACHE); if (qjson == null) { selectEnable = false; ftag = QuestionsConnecting.class.getName(); f = QuestionsConnecting.getInstance(getChildFragmentManager(), ftag); GAE.getInstance(getActivity()).getPage(this, "questions"); } else { final String ajson = getJson(ACACHE); if (ajson == null) { ftag = qjson; f = QuestionsForm.getInstance(getChildFragmentManager(), qjson); } else { final String cajson = getJson(CACACHE); if (cajson == null) { final int raceAfterNum = new PlayerAdapter(getActivity()).getRaceAfterNum(); if (raceAfterNum >= mRaceSelected.getId()) { // Standings are available for race, so correct answers // should be available for download. getSherlockActivity().setSupportProgressBarIndeterminateVisibility(true); selectEnable = false; GAE.getInstance(getActivity()) .getPage(new CorrectAnswersListener(mRaceSelected.getId()), "answers?year=" + mRaceSelected.getStartYear() + "&race_id=" + mRaceSelected.getId()); } } ftag = QuestionsAnswers.getTag(qjson, ajson, cajson); f = QuestionsAnswers.getInstance(getChildFragmentManager(), qjson, ajson, cajson); } } } else { ftag = QuestionsNotYet.getTag(mRaceSelected); f = QuestionsNotYet.getInstance(getChildFragmentManager(), mRaceSelected); } try { getChildFragmentManager().beginTransaction().replace(R.id.race_questions, f, ftag).commit(); } catch (final IllegalStateException e) { // This can occur if the user presses HOME or BACK during transaction // with server. When server response is received, the asynchronous // callback will try to update the fragment and fail, so set changed // flag so UI is updated on next resume. mChanged = true; Util.log("Questions fragment commit failed. Setting changed flag instead."); } if (mRaceSpinner != null) { mRaceSpinner.setEnabled(selectEnable); } else if (mRaceList != null) { mRaceList.setEnabled(selectEnable); } } @Override public void onResume() { super.onResume(); final IntentFilter filter = new IntentFilter(PTW.INTENT_ACTION_SCHEDULE); filter.addAction(PTW.INTENT_ACTION_RACE_ALARM); filter.addAction(PTW.INTENT_ACTION_HISTORY); getActivity().registerReceiver(mBroadcastReceiver, filter); if ((mRaceSelected != null) && mRaceSelected.inProgress()) { QuestionAlarm.clearNotification(getActivity().getApplicationContext()); } // Check if user changed their account status mChanged |= mSetupNeeded != GAE.isAccountSetupNeeded(getActivity()); // Check if user has switched accounts mChanged |= mAccountSetupTime != Util.getAccountSetupTime(getActivity()); if (mSubFragmentTime > 0) { // Check if user submitted answers on a different device mChanged |= mSubFragmentTime < PlayerHistory.getTime(getActivity()); // Check if app has been running for awhile and should be refreshed mChanged |= (System.currentTimeMillis() - mSubFragmentTime) > DateUtils.HOUR_IN_MILLIS; } if (mRaceNext != null) { if (mSubFragmentTime > 0) { // See if race questions are now available but weren't previously mChanged |= (mSubFragmentTime < mRaceNext.getQuestionTimestamp()) && mRaceNext.inProgress(); } // Check if questions form needs to disappear because race started mChanged |= !mRaceNext.isFuture(); } if (mChanged) { Util.log("Questions: onResume: notifyChanged"); resetRaceSelected(); } } @Override public void onPause() { super.onPause(); getActivity().unregisterReceiver(mBroadcastReceiver); } @Override public boolean isChanged() { return mChanged; } @Override protected void notifyChanged() { mChanged = true; super.notifyChanged(); } private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() { @Override public void onReceive(final Context context, final Intent intent) { Util.log("Questions.onReceive: " + intent.getAction()); mChanged = true; resetRaceSelected(); } }; private void resetRaceSelected() { final int lastPos = mRaceAdapter.getCount() - 1; // Workaround to ensure the spinner is set back to next race if ((mRaceSpinner != null) && (mRaceAdapter.getCount() > 0) && (mRaceSpinner.getSelectedItemPosition() != lastPos)) { // Making new selection causes notifyChanged to be called by listener mRaceSpinner.setSelection(lastPos); } else if ((mRaceList != null) && (mRaceAdapter.getCount() > 0) && (mRaceList.getSelectedItemPosition() != lastPos)) { mRaceList.performItemClick(null, lastPos, 0); mRaceList.smoothScrollToPosition(lastPos); mRaceList.setItemChecked(lastPos, true); } else { notifyChanged(); } } private class RaceSelectedListener implements OnItemSelectedListener, OnItemClickListener { @Override public void onItemSelected(final AdapterView<?> parent, final View v, final int pos, final long id) { final Race race = (Race) parent.getItemAtPosition(pos); if (mRaceSelected.getId() != race.getId()) { mRaceSelected = race; Util.log("Questions: Selected race = " + mRaceSelected.getId()); if (mChanged) { notifyChanged(); } else { setSubFragment(); } } } @Override public void onNothingSelected(final AdapterView<?> parent) { } @Override public void onItemClick(final AdapterView<?> parent, final View v, final int pos, final long id) { onItemSelected(parent, v, pos, id); } } private class CorrectAnswersListener implements GaeListener { private final int raceId; public CorrectAnswersListener(final int id) { raceId = id; } @Override public void onFailedConnect(final Context context) { Util.log("CorrectAnswersListener: onFailedConnect"); if (mRaceSpinner != null) { mRaceSpinner.setEnabled(true); } else if (mRaceList != null) { mRaceList.setEnabled(true); } // Verify application wasn't closed before callback returned if (getSherlockActivity() != null) { getSherlockActivity().setSupportProgressBarIndeterminateVisibility(false); } } @Override public void onGet(final Context context, final String json) { Util.log("CorrectAnswersListener: onGet: " + json); final SharedPreferences cache = context.getSharedPreferences(CACACHE, Activity.MODE_PRIVATE); cache.edit().putString(cachePrefix() + raceId, json).apply(); if (mRaceSpinner != null) { mRaceSpinner.setEnabled(true); } else if (mRaceList != null) { mRaceList.setEnabled(true); } // Verify application wasn't closed before callback returned if (getSherlockActivity() != null) { getSherlockActivity().setSupportProgressBarIndeterminateVisibility(false); setSubFragment(); } } @Override public void onConnectSuccess(final Context context, final String json) { } @Override public void onLaunchIntent(final Intent launch) { } } public void onSubmitAnswers(final String json) { mSending = true; setSubFragment(); GAE.getInstance(getActivity()).postPage(this, "questions", json); Analytics.trackEvent(getActivity(), "Questions", "button", "send"); } @Override public void onFailedConnect(final Context context) { Util.log("Questions: onFailedConnect"); // Verify application wasn't closed before callback returned if (getActivity() != null) { mSending = false; mFailedConnect = true; setSubFragment(); } } @Override public void onGet(final Context context, final String json) { Util.log("Questions: onGet: " + json); final SharedPreferences cache = context.getSharedPreferences(QCACHE, Activity.MODE_PRIVATE); cache.edit().putString(cachePrefix() + mRaceNext.getId(), json).apply(); // Verify application wasn't closed before callback returned if (getActivity() != null) { setSubFragment(); } } @Override public void onConnectSuccess(final Context context, final String json) { Util.log("Questions: onConnectSuccess: " + json); Toast.makeText(context, R.string.questions_success, Toast.LENGTH_SHORT).show(); final SharedPreferences cache = context.getSharedPreferences(ACACHE, Activity.MODE_PRIVATE); cache.edit().putString(cachePrefix() + mRaceSelected.getId(), json).apply(); context.sendBroadcast(new Intent(PTW.INTENT_ACTION_ANSWERS)); // Verify application wasn't closed before callback returned if (getActivity() != null) { mSending = false; setSubFragment(); } } @Override public void onLaunchIntent(final Intent launch) { } }