package org.edx.mobile.view; import android.app.Activity; import android.content.res.Configuration; import android.os.Bundle; import android.support.annotation.NonNull; import android.support.annotation.StringRes; import android.support.v4.view.ViewCompat; import android.support.v7.widget.AppCompatSpinner; import android.text.Editable; import android.text.TextWatcher; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.AdapterView; import android.widget.EditText; import android.widget.ProgressBar; import android.widget.RadioGroup; import android.widget.TextView; import com.google.inject.Inject; import org.edx.mobile.R; import org.edx.mobile.base.BaseFragment; import org.edx.mobile.discussion.CourseTopics; import org.edx.mobile.discussion.DiscussionService; import org.edx.mobile.discussion.DiscussionThread; import org.edx.mobile.discussion.DiscussionThreadPostedEvent; import org.edx.mobile.discussion.DiscussionTopic; import org.edx.mobile.discussion.DiscussionTopicDepth; import org.edx.mobile.discussion.ThreadBody; import org.edx.mobile.http.CallTrigger; import org.edx.mobile.http.ErrorHandlingCallback; import org.edx.mobile.logger.Logger; import org.edx.mobile.model.api.EnrolledCoursesResponse; import org.edx.mobile.module.analytics.ISegment; import org.edx.mobile.util.SoftKeyboardUtil; import org.edx.mobile.view.adapters.TopicSpinnerAdapter; import org.edx.mobile.view.common.TaskMessageCallback; import org.edx.mobile.view.common.TaskProgressCallback.ProgressViewController; import java.util.ArrayList; import java.util.HashMap; import java.util.Map; import de.greenrobot.event.EventBus; import retrofit2.Call; import roboguice.inject.InjectExtra; import roboguice.inject.InjectView; public class DiscussionAddPostFragment extends BaseFragment { static public String TAG = DiscussionAddPostFragment.class.getCanonicalName(); static public String ENROLLMENT = TAG + ".enrollment"; static public String TOPIC = TAG + ".topic"; protected final Logger logger = new Logger(getClass().getName()); @InjectExtra(Router.EXTRA_COURSE_DATA) private EnrolledCoursesResponse courseData; @InjectExtra(Router.EXTRA_DISCUSSION_TOPIC) private DiscussionTopic discussionTopic; @InjectView(R.id.discussion_question_segmented_group) private RadioGroup discussionQuestionSegmentedGroup; @InjectView(R.id.topics_spinner) private AppCompatSpinner topicsSpinner; @InjectView(R.id.title_edit_text) private EditText titleEditText; @InjectView(R.id.body_edit_text) private EditText bodyEditText; @InjectView(R.id.add_post_button) private ViewGroup addPostButton; @InjectView(R.id.add_post_button_text) private TextView addPostButtonText; @InjectView(R.id.progress_indicator) private ProgressBar addPostProgressBar; @Inject private DiscussionService discussionService; @Inject ISegment segIO; private ViewGroup container; private Call<CourseTopics> getTopicListCall; private Call<DiscussionThread> createThreadCall; private int selectedTopicIndex; @Override public View onCreateView(LayoutInflater inflater, final ViewGroup container, Bundle savedInstanceState) { this.container = container; return inflater.inflate(R.layout.fragment_add_post, container, false); } @Override public void onViewCreated(View view, Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); discussionQuestionSegmentedGroup.setOnCheckedChangeListener(new RadioGroup.OnCheckedChangeListener() { @Override public void onCheckedChanged(RadioGroup group, int checkedId) { @StringRes final int bodyHint; @StringRes final int submitLabel; @StringRes final int submitDescription; if (discussionQuestionSegmentedGroup.getCheckedRadioButtonId() == R.id.discussion_radio_button) { bodyHint = R.string.discussion_body_hint_discussion; submitLabel = R.string.discussion_add_post_button_label; submitDescription = R.string.discussion_add_post_button_description; } else { bodyHint = R.string.discussion_body_hint_question; submitLabel = R.string.discussion_add_question_button_label; submitDescription = R.string.discussion_add_question_button_description; } bodyEditText.setHint(bodyHint); addPostButtonText.setText(submitLabel); addPostButton.setContentDescription(getText(submitDescription)); } }); discussionQuestionSegmentedGroup.check(R.id.discussion_radio_button); getTopicList(); topicsSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { @Override public void onItemSelected(AdapterView<?> parent, View view, int position, long id) { // Even though we disabled topics that aren't supposed to be selected, Android still allows you to select them using keyboard or finger-dragging // So, we have to revert the user's selection if they select a topic that cannot be posted to final DiscussionTopicDepth item = (DiscussionTopicDepth) parent.getItemAtPosition(position); if (null == item || item.isPostable()) { selectedTopicIndex = position; } else { // Revert selection parent.setSelection(selectedTopicIndex); } setPostButtonEnabledState(); } @Override public void onNothingSelected(AdapterView<?> parent) { setPostButtonEnabledState(); } }); ViewCompat.setBackgroundTintList(topicsSpinner, getResources().getColorStateList(R.color.edx_brand_gray_base)); addPostButton.setOnClickListener(new View.OnClickListener() { public void onClick(View v) { Activity activity = getActivity(); if (activity != null) { SoftKeyboardUtil.hide(activity); } final String title = titleEditText.getText().toString(); final String body = bodyEditText.getText().toString(); final DiscussionThread.ThreadType discussionQuestion; if (discussionQuestionSegmentedGroup.getCheckedRadioButtonId() == R.id.discussion_radio_button) { discussionQuestion = DiscussionThread.ThreadType.DISCUSSION; } else { discussionQuestion = DiscussionThread.ThreadType.QUESTION; } ThreadBody threadBody = new ThreadBody(); threadBody.setCourseId(courseData.getCourse().getId()); threadBody.setTitle(title); threadBody.setRawBody(body); threadBody.setTopicId(((DiscussionTopicDepth) topicsSpinner.getSelectedItem()).getDiscussionTopic().getIdentifier()); threadBody.setType(discussionQuestion); addPostButton.setEnabled(false); createThread(threadBody); } }); addPostButton.setEnabled(false); final TextWatcher textWatcher = new TextWatcher() { @Override public void beforeTextChanged(CharSequence s, int start, int count, int after) { } @Override public void onTextChanged(CharSequence s, int start, int before, int count) { } @Override public void afterTextChanged(Editable s) { setPostButtonEnabledState(); } }; titleEditText.addTextChangedListener(textWatcher); bodyEditText.addTextChangedListener(textWatcher); } private void setPostButtonEnabledState() { final String title = titleEditText.getText().toString(); final String body = bodyEditText.getText().toString(); final boolean topicSelected = null != topicsSpinner.getSelectedItem(); addPostButton.setEnabled(topicSelected && title.trim().length() > 0 && body.trim().length() > 0); } protected void createThread(ThreadBody threadBody) { if (createThreadCall != null) { createThreadCall.cancel(); } createThreadCall = discussionService.createThread(threadBody); createThreadCall.enqueue(new ErrorHandlingCallback<DiscussionThread>( getActivity(), CallTrigger.USER_ACTION, new ProgressViewController(addPostProgressBar)) { @Override protected void onResponse(@NonNull final DiscussionThread courseTopics) { EventBus.getDefault().post(new DiscussionThreadPostedEvent(courseTopics)); getActivity().finish(); } @Override protected void onFailure(@NonNull final Throwable error) { addPostButton.setEnabled(true); } }); } protected void getTopicList() { if (getTopicListCall != null) { getTopicListCall.cancel(); } getTopicListCall = discussionService.getCourseTopics(courseData.getCourse().getId()); getTopicListCall.enqueue(new ErrorHandlingCallback<CourseTopics>(getActivity(), CallTrigger.LOADING_UNCACHED, (TaskMessageCallback) null) { @Override protected void onResponse(@NonNull final CourseTopics courseTopics) { final ArrayList<DiscussionTopic> allTopics = new ArrayList<>(); allTopics.addAll(courseTopics.getNonCoursewareTopics()); allTopics.addAll(courseTopics.getCoursewareTopics()); final TopicSpinnerAdapter adapter = new TopicSpinnerAdapter(container.getContext(), DiscussionTopicDepth.createFromDiscussionTopics(allTopics)); topicsSpinner.setAdapter(adapter); { // Attempt to select the topic that we navigated from // Otherwise, leave the default option, which is the first non-courseware topic if (!discussionTopic.isAllType() && !discussionTopic.isFollowingType()) { int selectedTopicIndex = -1; if (discussionTopic.getIdentifier() == null) { // In case of a parent topic, we need to select the first child topic if (!discussionTopic.getChildren().isEmpty()) { selectedTopicIndex = adapter.getPosition(discussionTopic.getChildren().get(0)); } } else { selectedTopicIndex = adapter.getPosition(discussionTopic); } if (selectedTopicIndex >= 0) { topicsSpinner.setSelection(selectedTopicIndex); } } } DiscussionTopic selectedTopic = ((DiscussionTopicDepth) topicsSpinner.getSelectedItem()).getDiscussionTopic(); Map<String, String> values = new HashMap<>(); values.put(ISegment.Keys.TOPIC_ID, selectedTopic.getIdentifier()); segIO.trackScreenView(ISegment.Screens.FORUM_CREATE_TOPIC_THREAD, courseData.getCourse().getId(), selectedTopic.getName(), values); } }); } @Override public void onResume() { super.onResume(); if (getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE) { SoftKeyboardUtil.clearViewFocus(titleEditText); } } }