Java tutorial
/* * 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.infine.android.devoxx.ui; import android.content.ContentValues; import android.content.Context; import android.content.Intent; import android.database.Cursor; import android.graphics.RectF; import android.net.Uri; import android.os.Bundle; import android.support.v4.app.Fragment; import android.text.Spanned; import android.text.TextUtils; import android.util.Log; import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; import android.widget.AdapterView; import android.widget.AdapterView.OnItemClickListener; import android.widget.CompoundButton; import android.widget.CursorAdapter; import android.widget.ImageView; import android.widget.ListView; import android.widget.TabHost; import android.widget.TextView; import com.infine.android.devoxx.R; import com.infine.android.devoxx.provider.ScheduleContract; import com.infine.android.devoxx.provider.ScheduleContract.Speakers; import com.infine.android.devoxx.ui.phone.MapActivity; import com.infine.android.devoxx.util.FractionalTouchDelegate; import com.infine.android.devoxx.util.NotifyingAsyncQueryHandler; import com.infine.android.devoxx.util.UIUtils; /** * A fragment that shows detail information for a session, including session * title, abstract, time information, speaker photos and bios, etc. */ public class SessionDetailFragment extends Fragment implements NotifyingAsyncQueryHandler.AsyncQueryListener, CompoundButton.OnCheckedChangeListener { private static final String TAG = "SessionDetailFragment"; /** * Since sessions can belong tracks, the parent activity can send this extra * specifying a track URI that should be used for coloring the title-bar. */ public static final String EXTRA_TRACK = "com.infine.android.devoxx.extra.TRACK"; private static final String TAG_SUMMARY = "summary"; private static final String TAG_SPEAKERS = "speakers"; private String mSessionId; private Uri mSessionUri; private String mTitleString; private String mRoomId; private ViewGroup mRootView; private TabHost mTabHost; private TextView mTitle; private TextView mSubtitle; private TextView mExperience; private TextView mSponsored; private CompoundButton mStarred; private TextView mSummary; private String mSpeakerList; private NotifyingAsyncQueryHandler mHandler; private boolean mSessionCursor = false; private boolean mSpeakersCursor = false; private boolean mHasSummaryContent = false; private CursorAdapter mAdapter; private ListView mListView; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); final Intent intent = BaseActivity.fragmentArgumentsToIntent(getArguments()); mSessionUri = intent.getData(); if (mSessionUri == null) { return; } mSessionId = ScheduleContract.Sessions.getSessionId(mSessionUri); mAdapter = new SpeakerAdapter(getActivity()); setHasOptionsMenu(false); } @Override public void onResume() { super.onResume(); queryForSpeakers(); } @Override public void onPause() { super.onPause(); } @Override public void onActivityCreated(Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); if (mSessionUri == null) { return; } // Start background queries to load session and track details final Uri speakersUri = ScheduleContract.Sessions.buildSpeakersDirUri(mSessionId); mHandler = new NotifyingAsyncQueryHandler(getActivity().getContentResolver(), this); mHandler.startQuery(SessionsQuery._TOKEN, mSessionUri, SessionsQuery.PROJECTION); } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { mRootView = (ViewGroup) inflater.inflate(R.layout.fragment_session_detail, null); mTabHost = (TabHost) mRootView.findViewById(android.R.id.tabhost); mTabHost.setup(); mTitle = (TextView) mRootView.findViewById(R.id.session_title); mSubtitle = (TextView) mRootView.findViewById(R.id.session_subtitle); mSponsored = (TextView) mRootView.findViewById(R.id.session_sponsored); mStarred = (CompoundButton) mRootView.findViewById(R.id.star_button); mStarred.setFocusable(true); mStarred.setClickable(true); // Larger target triggers star toggle final View starParent = mRootView.findViewById(R.id.header_session); FractionalTouchDelegate.setupDelegate(starParent, mStarred, new RectF(0.6f, 0f, 1f, 0.8f)); mSummary = (TextView) mRootView.findViewById(R.id.session_abstract); setupSummaryTab(); return mRootView; } /** * Build and add "summary" tab. */ private void setupSummaryTab() { // Summary content comes from existing layout mTabHost.addTab(mTabHost.newTabSpec(TAG_SUMMARY).setIndicator(buildIndicator(R.string.session_summary)) .setContent(R.id.tab_session_summary)); } /** * Build and add "summary" tab. */ private void setupSpeakersTab() { // Summary content comes from existing layout mTabHost.addTab(mTabHost.newTabSpec(TAG_SPEAKERS).setIndicator(buildIndicator(R.string.session_speakers)) .setContent(R.id.tab_session_speakers)); mListView = (ListView) mRootView.findViewById(R.id.tab_session_speakers); mListView.setClickable(true); mListView.setAdapter(mAdapter); mListView.setOnItemClickListener(new OnItemClickListener() { @Override public void onItemClick(AdapterView<?> list, View view, int position, long id) { // Launch viewer for specific session final Cursor cursor = (Cursor) mAdapter.getItem(position); final String speakerId = cursor.getString(SpeakersQuery.SPEAKER_ID); final Uri speakerUri = ScheduleContract.Speakers.buildSpeakerUri(speakerId); final Intent intent = new Intent(Intent.ACTION_VIEW, speakerUri); ((BaseActivity) getActivity()).openActivityOrFragment(intent); } }); } /** * Build a {@link View} to be used as a tab indicator, setting the requested * string resource as its label. * * @param textRes * @return View */ private View buildIndicator(int textRes) { final TextView indicator = (TextView) getActivity().getLayoutInflater().inflate(R.layout.tab_indicator, (ViewGroup) mRootView.findViewById(android.R.id.tabs), false); indicator.setText(textRes); return indicator; } /** * {@inheritDoc} */ public void onQueryComplete(int token, Object cookie, Cursor cursor) { if (getActivity() == null) { return; } if (token == SessionsQuery._TOKEN) { onSessionQueryComplete(cursor); } else if (token == SpeakersQuery._TOKEN) { onSpeakersQueryComplete(cursor); } else { Log.i("SessionDetailFragment/onQueryComplete", "Query complete, Not Actionable: " + token); cursor.close(); } } /** * Handle {@link SessionsQuery} {@link Cursor}. */ private void onSessionQueryComplete(Cursor cursor) { try { mSessionCursor = true; if (!cursor.moveToFirst()) { return; } setupSpeakersTab(); // execute 2nd request mSpeakerList = cursor.getString(SessionsQuery.SESSION_SPEAKERS); queryForSpeakers(); // Format time block this session occupies final long blockStart = cursor.getLong(SessionsQuery.BLOCK_START); final long blockEnd = cursor.getLong(SessionsQuery.BLOCK_END); final String roomName = cursor.getString(SessionsQuery.ROOM_NAME); final Spanned subtitle = UIUtils.formatSessionSubtitle(blockStart, blockEnd, roomName, getActivity()); mTitleString = cursor.getString(SessionsQuery.TITLE); mTitle.setText(mTitleString); mSubtitle.setText(subtitle); mSubtitle.setOnClickListener(new View.OnClickListener() { public void onClick(View view) { Intent intent = new Intent(getActivity(), MapActivity.class); intent.putExtra(MapHotelFragment.EXTRA_ROOM, MapHotelFragment.roomToFloor(roomName)); startActivity(intent); } }); // String exp = cursor.getString(SessionsQuery.EXPERIENCE); // int resource = getResources().getIdentifier("session_exp_" + exp, "id", // getActivity().getApplicationContext().getPackageName()); // mExperience = (TextView) mRootView.findViewById(resource); // mExperience.setVisibility(View.VISIBLE); // mExperience.setText(exp); boolean isSponsored = cursor.getInt(SessionsQuery.SESSION_SPONSORED) == 1 ? true : false; if (isSponsored) { mSponsored.setText(R.string.session_sponsored); } else { mSponsored.setVisibility(View.GONE); } mRoomId = cursor.getString(SessionsQuery.ROOM_ID); // Unregister around setting checked state to avoid triggering // listener since change isn't user generated. mStarred.setOnCheckedChangeListener(null); mStarred.setChecked(cursor.getInt(SessionsQuery.STARRED) != 0); mStarred.setOnCheckedChangeListener(this); final String sessionAbstract = cursor.getString(SessionsQuery.SUMMARY); if (!TextUtils.isEmpty(sessionAbstract)) { UIUtils.setTextMaybeHtml(mSummary, sessionAbstract); mSummary.setVisibility(View.VISIBLE); mHasSummaryContent = true; } else { mSummary.setVisibility(View.GONE); } // Show empty message when all data is loaded, and nothing to show if (mSpeakersCursor && !mHasSummaryContent) { mRootView.findViewById(android.R.id.empty).setVisibility(View.VISIBLE); } // AnalyticsUtils.getInstance(getActivity()).trackPageView("/Sessions/" + mTitleString); } finally { cursor.close(); } } private void onSpeakersQueryComplete(Cursor cursor) { if (!cursor.moveToFirst()) { return; } mSpeakersCursor = true; mAdapter.changeCursor(cursor); } private void queryForSpeakers() { mHandler.startQuery(SpeakersQuery._TOKEN, Speakers.CONTENT_URI, SpeakersQuery.PROJECTION, Speakers.SPEAKER_ID + " in (" + mSpeakerList + ")"); } @Override public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { inflater.inflate(R.menu.session_detail_menu_items, menu); super.onCreateOptionsMenu(menu, inflater); } @Override public boolean onOptionsItemSelected(MenuItem item) { // final String shareString; final Intent intent; switch (item.getItemId()) { case R.id.menu_map: intent = new Intent(getActivity().getApplicationContext(), UIUtils.getMapActivityClass(getActivity())); intent.putExtra(MapFragment.EXTRA_ROOM, mRoomId); startActivity(intent); return true; case R.id.menu_share: // TODO: consider bringing in shortlink to session // shareString = getString(R.string.share_template, mTitleString, // getHashtagsString(), // mUrl); // intent = new Intent(Intent.ACTION_SEND); // intent.setType("text/plain"); // intent.putExtra(Intent.EXTRA_TEXT, shareString); // startActivity(Intent.createChooser(intent, // getText(R.string.title_share))); // return true; return false; } return super.onOptionsItemSelected(item); } /** * Handle toggling of starred checkbox. */ public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { final ContentValues values = new ContentValues(); values.put(ScheduleContract.Sessions.SESSION_STARRED, isChecked ? 1 : 0); mHandler.startUpdate(mSessionUri, values); // Because change listener is set to null during initialization, these // won't fire on // pageview. // AnalyticsUtils.getInstance(getActivity()).trackEvent("Sandbox", isChecked ? "Starred" : "Unstarred", mTitleString, 0); } /* * Event structure: Category -> "Session Details" Action -> "Create Note", * "View Note", etc Label -> Session's Title Value -> 0. */ public void fireNotesEvent(int actionId) { // AnalyticsUtils.getInstance(getActivity()).trackEvent("Session Details", getActivity().getString(actionId), mTitleString, 0); } /* * Event structure: Category -> "Session Details" Action -> Link Text Label * -> Session's Title Value -> 0. */ public void fireLinkEvent(int actionId) { // AnalyticsUtils.getInstance(getActivity()).trackEvent("Link Details", getActivity().getString(actionId), mTitleString, 0); } private class SpeakerAdapter extends CursorAdapter { public SpeakerAdapter(Context context) { super(context, null); } @Override public void bindView(View view, Context context, Cursor cursor) { ((TextView) view.findViewById(R.id.speaker_title)) .setText(cursor.getString(SpeakersQuery.SPEAKER_NAME)); ((TextView) view.findViewById(R.id.speaker_subtitle)) .setText(cursor.getString(SpeakersQuery.SPEAKER_COMPANY)); ImageView image = (ImageView) view.findViewById(R.id.image_item_speaker); UIUtils.displayImageLazily(context, cursor.getString(SpeakersQuery.SPEAKER_IMAGE_URL), image); } @Override public View newView(Context context, Cursor cursor, ViewGroup parent) { return getActivity().getLayoutInflater().inflate(R.layout.list_item_speaker, parent, false); } } /** * {@link com.infine.android.devoxx.provider.ScheduleContract.Sessions} * query parameters. */ private interface SessionsQuery { int _TOKEN = 0x1; String[] PROJECTION = { ScheduleContract.Blocks.BLOCK_START, ScheduleContract.Blocks.BLOCK_END, ScheduleContract.Sessions.SESSION_EXPERIENCE, ScheduleContract.Sessions.SESSION_TITLE, ScheduleContract.Sessions.SESSION_TYPE, ScheduleContract.Sessions.SESSION_THEME, ScheduleContract.Sessions.SESSION_SUMMARY, ScheduleContract.Sessions.SESSION_STARRED, ScheduleContract.Sessions.ROOM_ID, ScheduleContract.Rooms.ROOM_NAME, ScheduleContract.Sessions.SESSION_SPEAKERS, ScheduleContract.Sessions.SESSION_SPONSORED }; int BLOCK_START = 0; int BLOCK_END = 1; int EXPERIENCE = 2; int TITLE = 3; int TYPE = 4; int THEME = 5; int SUMMARY = 6; int STARRED = 7; int ROOM_ID = 8; int ROOM_NAME = 9; int SESSION_SPEAKERS = 10; int SESSION_SPONSORED = 11; } private interface SpeakersQuery { int _TOKEN = 0x3; String[] PROJECTION = { ScheduleContract.Speakers._ID, ScheduleContract.Speakers.SPEAKER_ID, ScheduleContract.Speakers.SPEAKER_NAME, ScheduleContract.Speakers.SPEAKER_COMPANY, ScheduleContract.Speakers.SPEAKER_IMAGE_URL }; int _ID = 0; int SPEAKER_ID = 1; int SPEAKER_NAME = 2; int SPEAKER_COMPANY = 3; int SPEAKER_IMAGE_URL = 4; } }