/* * CourseDetailFragment * * Main fragment that populates the course detail screen. The course card fragment is created first * and then the additional items are added if given. */ package org.edx.mobile.view; import android.app.Activity; import android.content.Intent; import android.net.Uri; import android.os.Bundle; import android.os.Handler; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.Button; import android.widget.FrameLayout; import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.TextView; import android.widget.Toast; import com.bumptech.glide.Glide; import com.google.inject.Inject; import com.joanzapata.iconify.fonts.FontAwesomeIcons; import com.joanzapata.iconify.widget.IconImageView; import org.apache.http.protocol.HTTP; import org.edx.mobile.R; import org.edx.mobile.core.IEdxEnvironment; import org.edx.mobile.course.CourseAPI; import org.edx.mobile.course.CourseDetail; 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.services.ServiceManager; import org.edx.mobile.task.EnrollForCourseTask; import org.edx.mobile.task.GetEnrolledCourseTask; import org.edx.mobile.util.WebViewUtil; import org.edx.mobile.util.images.CourseCardUtils; import org.edx.mobile.util.images.TopAnchorFillWidthTransformation; import org.edx.mobile.view.custom.EdxWebView; import org.edx.mobile.view.custom.URLInterceptorWebViewClient; import java.util.List; import org.edx.mobile.base.BaseFragment; import retrofit2.Call; import roboguice.inject.InjectExtra; public class CourseDetailFragment extends BaseFragment { private static final int LOG_IN_REQUEST_CODE = 42; @Nullable private Call<CourseDetail> getCourseDetailCall; private TextView mCourseTextName; private TextView mCourseTextDetails; private ImageView mHeaderImageView; private ImageView mHeaderPlayIcon; private LinearLayout mCourseDetailLayout; private TextView mShortDescription; private LinearLayout courseDetailFieldLayout; private FrameLayout courseAbout; private EdxWebView courseAboutWebView; private Button mEnrollButton; private boolean mEnrolled = false; boolean emailOptIn = true; static public final String COURSE_DETAIL = "course_detail"; @InjectExtra(COURSE_DETAIL) CourseDetail courseDetail; protected final Logger logger = new Logger(getClass().getName()); @Inject private CourseAPI courseApi; @Inject IEdxEnvironment environment; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); Bundle bundle = this.getArguments(); if (bundle != null) { courseDetail = bundle.getParcelable(Router.EXTRA_COURSE_DETAIL); } } /** * Sets the view for the Course Card and play button if there is an intro video. */ @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { // Course Card View View view; view = inflater.inflate(R.layout.fragment_course_dashboard, container, false); mCourseTextName = (TextView) view.findViewById(R.id.course_detail_name); mCourseTextDetails = (TextView) view.findViewById(R.id.course_detail_extras); mHeaderImageView = (ImageView) view.findViewById(R.id.header_image_view); mHeaderPlayIcon = (ImageView) view.findViewById(R.id.header_play_icon); mCourseDetailLayout = (LinearLayout) view.findViewById(R.id.dashboard_detail); mHeaderPlayIcon.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Uri uri = Uri.parse(courseDetail.media.course_video.uri); Intent intent = new Intent(Intent.ACTION_VIEW, uri); startActivity(intent); } }); return view; } /** * Populates the course details. * Creates and populates the short description if given, * Creates and populates fields such as "effort", "duration" if any. This is handled on a case * by case basis rather than using a list view. * Sets the view for About this Course which is retrieved in a later api call. */ @Override public void onViewCreated(View view, Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); // Short Description final LayoutInflater inflater = LayoutInflater.from(getActivity()); final View child = inflater.inflate(R.layout.fragment_course_detail, mCourseDetailLayout, false); mShortDescription = (TextView) child.findViewById(R.id.course_detail_short_description); if (courseDetail.short_description == null || courseDetail.short_description.isEmpty()) { ((ViewGroup) mShortDescription.getParent()).removeView(mShortDescription); } mCourseDetailLayout.addView(child); // Enrollment Button mEnrollButton = (Button) child.findViewById(R.id.button_enroll_now); configureEnrollButton(); // Course Detail Fields - Each field will be created manually. courseDetailFieldLayout = (LinearLayout) view.findViewById(R.id.course_detail_fields); if (courseDetail.effort != null && !courseDetail.effort.isEmpty()) { ViewHolder holder = createCourseDetailFieldViewHolder(inflater, mCourseDetailLayout); holder.rowIcon.setIcon(FontAwesomeIcons.fa_dashboard); holder.rowFieldName.setText(R.string.effort_field_name); holder.rowFieldText.setText(courseDetail.effort); } // About this Course courseAbout = (FrameLayout) view.findViewById(R.id.course_detail_course_about); courseAboutWebView = (EdxWebView) courseAbout.findViewById(R.id.course_detail_course_about_webview); } @Override public void onActivityCreated(Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); setCourseImage(); setCourseVideoButton(); setCourseCardText(); mShortDescription.setText(courseDetail.short_description); populateAboutThisCourse(); } private void setCourseCardText() { String formattedDate = CourseCardUtils.getFormattedDate( getActivity(), courseDetail.start, courseDetail.end, courseDetail.start_type, courseDetail.start_display); mCourseTextDetails.setText(CourseCardUtils.getDescription(courseDetail.org, courseDetail.number, formattedDate)); mCourseTextName.setText(courseDetail.name); } private void setCourseImage() { final String headerImageUrl = courseDetail.media.course_image.getUri(environment.getConfig().getApiHostURL()); Glide.with(CourseDetailFragment.this) .load(headerImageUrl) .placeholder(R.drawable.placeholder_course_card_image) .transform(new TopAnchorFillWidthTransformation(getActivity())) .into(mHeaderImageView); } /** * Shows and enables the play button if the video url was provided. */ private void setCourseVideoButton() { if (courseDetail.media.course_video.uri == null || courseDetail.media.course_video.uri.isEmpty()) { mHeaderPlayIcon.setEnabled(false); } else { mHeaderPlayIcon.setVisibility(mHeaderPlayIcon.VISIBLE); } } /** * Makes a call the the course details api and sets the overview if given. If there is no * overview, remove the courseAbout view. */ private void populateAboutThisCourse() { getCourseDetailCall = courseApi.getCourseDetail(courseDetail.course_id); getCourseDetailCall.enqueue(new ErrorHandlingCallback<CourseDetail>( getActivity(), CallTrigger.LOADING_UNCACHED) { @Override protected void onResponse(@NonNull final CourseDetail courseDetail) { if (courseDetail.overview != null && !courseDetail.overview.isEmpty()) { populateAboutThisCourse(courseDetail.overview); } else { courseAbout.setVisibility(View.GONE); } } }); } /** * Takes a string which can include html and then renders it into the courseAbout webview. * * @param overview A string that can contain html tags */ private void populateAboutThisCourse(String overview) { courseAbout.setVisibility(View.VISIBLE); URLInterceptorWebViewClient client = new URLInterceptorWebViewClient( getActivity(), courseAboutWebView); client.setAllLinksAsExternal(true); StringBuilder buff = WebViewUtil.getIntialWebviewBuffer(getActivity(), logger); buff.append("<body>"); buff.append("<div class=\"header\">"); buff.append(overview); buff.append("</div>"); buff.append("</body>"); courseAboutWebView.clearCache(true); courseAboutWebView.loadDataWithBaseURL(environment.getConfig().getApiHostURL(), buff.toString(), "text/html", HTTP.UTF_8, null); } /** * Creates a ViewHolder for a course detail field such as "effort" or "duration" and then adds * it to the top of the list. */ private ViewHolder createCourseDetailFieldViewHolder(LayoutInflater inflater, LinearLayout parent) { ViewHolder holder = new ViewHolder(); holder.rowView = inflater.inflate(R.layout.course_detail_field, parent, false); holder.rowIcon = (IconImageView) holder.rowView.findViewById(R.id.course_detail_field_icon); holder.rowFieldName = (TextView) holder.rowView.findViewById(R.id.course_detail_field_name); holder.rowFieldText = (TextView) holder.rowView.findViewById(R.id.course_detail_field_text); courseDetailFieldLayout.addView(holder.rowView, 0); return holder; } private class ViewHolder { View rowView; IconImageView rowIcon; TextView rowFieldName; TextView rowFieldText; } /** * Sets the onClickListener and the text for the enrollment button. * * If the current course is found in the list of cached course enrollment list, the button will * be for viewing a course, otherwise, it will be used to enroll in a course. One clicked, user * is then taken to the dashboard for target course. */ private void configureEnrollButton() { ServiceManager api = environment.getServiceManager(); // This call should already be cached, if not, set button as if not enrolled. try { List<EnrolledCoursesResponse> enrolledCoursesResponse = api.getEnrolledCourses(true); for (EnrolledCoursesResponse course : enrolledCoursesResponse) { if (course.getCourse().getId().equals(courseDetail.course_id)) { mEnrolled = true; } } } catch (Exception ex) { logger.debug("Unable to get cached enrollments list"); } if (mEnrolled) { mEnrollButton.setText(R.string.view_course_button_text); } else { mEnrollButton.setText(R.string.enroll_now_button_text); } mEnrollButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { if (!mEnrolled) { enrollInCourse(); } else { openCourseDashboard(); } } }); } @Override public void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); if (requestCode == LOG_IN_REQUEST_CODE && resultCode == Activity.RESULT_OK) { enrollInCourse(); } } /** * Enroll in a course, Then open the course dashboard of the enrolled course. */ public void enrollInCourse() { if (null == environment.getLoginPrefs().getUsername()) { startActivityForResult(environment.getRouter().getRegisterIntent(), LOG_IN_REQUEST_CODE); return; } environment.getSegment().trackEnrollClicked(courseDetail.course_id, emailOptIn); EnrollForCourseTask enrollForCourseTask = new EnrollForCourseTask(getActivity(), courseDetail.course_id, emailOptIn) { @Override public void onSuccess(Void result) { mEnrolled = true; logger.debug("Enrollment successful: " + courseDetail.course_id); mEnrollButton.setText(R.string.view_course_button_text); Toast.makeText(getContext(), R.string.you_are_now_enrolled, Toast.LENGTH_SHORT).show(); new Handler().post(new Runnable() { @Override public void run() { GetEnrolledCourseTask getEnrolledCourseTask = new GetEnrolledCourseTask(getActivity(), courseDetail.course_id) { @Override public void onSuccess(EnrolledCoursesResponse course) { environment.getRouter().showMyCourses(getActivity()); environment.getRouter().showCourseDashboardTabs(getActivity(), environment.getConfig(), course, false); } }; getEnrolledCourseTask.execute(); } }); } @Override public void onException(Exception ex) { super.onException(ex); Toast.makeText(getContext(), R.string.enrollment_failure, Toast.LENGTH_SHORT).show(); } }; enrollForCourseTask.execute(); } /** * Open course dashboard for given course from the enrollments list cache. */ private void openCourseDashboard() { ServiceManager api = environment.getServiceManager(); try { List<EnrolledCoursesResponse> enrolledCoursesResponse = api.getEnrolledCourses(true); for (EnrolledCoursesResponse course : enrolledCoursesResponse) { if (course.getCourse().getId().equals(courseDetail.course_id)) { environment.getRouter().showMyCourses(getActivity()); environment.getRouter().showCourseDashboardTabs(getActivity(), environment.getConfig(), course, false); } } } catch (Exception exception) { logger.debug(exception.toString()); Toast.makeText(getContext(), R.string.cannot_show_dashboard, Toast.LENGTH_SHORT).show(); } } }