public class NavDrawerAdapter extends BaseAdapter {
    ArrayList<NavDrawerItem> navDrawerItemList;
    Context context;

    public NavDrawerAdapter(ArrayList<NavDrawerItem> items, Context context) {
        navDrawerItemList = items;
        this.context = context;
    }

    public int getCount() {
        return navDrawerItemList.size();
    }

    public Object getItem(int i) {
        return navDrawerItemList.get(i);
    }

    public long getItemId(int position) {
        return ((NavDrawerItem)getItem(position)).getId();
    }

    public int getPositionFromId(int id) {
        for (int i = 0; i < navDrawerItemList.size(); i++) {
            if (navDrawerItemList.get(i).getId() == id) {
                return i;
            }
        }
        return -1;
    }

    public View getView(int position, View convertView, ViewGroup parent) {
        // verify that convertView is not null
        if (convertView == null) {
            // inflate a new view
            LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
            convertView = inflater.inflate(R.layout.nav_drawer_item, parent, false);
        }

        TextView label;
        ImageView icon;

        label = (TextView) convertView.findViewById(R.id.drawer_item_name);
        icon = (ImageView) convertView.findViewById(R.id.drawer_item_icon);

        label.setText(navDrawerItemList.get(position).getName());

        icon.setImageResource(navDrawerItemList.get(position).getIconResID());

        return convertView;
    }


} public class Constants {
    public static final String FLOW_DOMAIN = "uwflow.com";
    public static final String SESSION_COOKIE_DOMAIN = FLOW_DOMAIN;
    public static final String BASE_URL = "https://uwflow.com/";
    public static final String FBID = "fbid";
    public static final String FACEBOOK_ACCESS_TOKEN = "fb_access_token";

    public static final String URL_PROFILE_EXT = "profile/";
    public static final String URL_COURSE_EXT = "course/";

    public static final String API_LOGIN = "api/v1/login/facebook";
    public static final String API_COURSE = "api/v1/courses/%s";
    public static final String API_COURSE_PROFESSORS = "api/v1/courses/%s/professors";
    public static final String API_COURSE_EXAMS = "api/v1/courses/%s/exams";
    public static final String API_COURSE_SECTIONS = "api/v1/courses/%s/sections";
    public static final String API_COURSE_USERS = "api/v1/courses/%s/users";
    public static final String API_USER = "api/v1/user";
    public static final String API_USERS = "api/v1/users/%s";
    public static final String API_USER_SCHEDULE = "api/v1/user/schedule";
    public static final String API_USERS_SCHEDULE = "api/v1/users/%s/schedule";
    public static final String API_USER_EXAMS = "api/v1/user/exams";
    public static final String API_USERS_EXAMS = "api/v1/users/%s/exams";
    public static final String API_USER_COURSES = "api/v1/user/courses";
    public static final String API_USERS_COURSES = "api/v1/users/%s/courses";
    public static final String API_USER_FRIENDS = "api/v1/user/friends";
    public static final String API_USERS_FRIENDS = "api/v1/users/%s/friends";
    public static final String API_USER_SHORTLIST_COURSE = "api/v1/user/shortlist/%s";
    public static final String API_SEARCH_COURSES = "api/v1/search/courses?%s";

    public static class DatabaseColumnName{
        public static final String USER_ID = "user_id";

    }

    public static class LoaderManagerId {
        public static final int PROFILE_FRIENDS_LOADER_ID = 1;
        public static final int PROFILE_SCHEDULE_LOADER_ID = 2;
        public static final int PROFILE_EXAMS_LOADER_ID = 3;
        public static final int PROFILE_COURSES_LOADER_ID = 4;
        public static final int PROFILE_LOADER_ID = 5;
        public static final int COURSE_USER_COURSES_LOADER_ID = 6;
    }

    public static class API_REQUEST_CALL_ID {
        public static final int API_USER_COURSE = 0;
        public static final int API_USER_FRIENDS = 1;
        public static final int API_USER_EXAMS = 2;
        public static final int API_USER_SCHEDULE = 3;
        public static final int API_USER = 4;
        public static final int API_COURSE_SECTIONS = 5;
        public static final int API_COURSE_EXAMS = 6;
        public static final int API_COURSE_PROFESSORS = 7;
        public static final int API_COURSE_USERS = 8;
        public static final int API_COURSE = 9;
        public static final int API_SEARCH_COURSES = 10;
    }

    public static class BroadcastActionId {
        public static final String UPDATE_PROFILE_USER = "UpdateProfileUser";
        public static final String UPDATE_PROFILE_USER_FRIENDS = "UpdateProfileUserFriends";
        public static final String UPDATE_PROFILE_USER_SCHEDULE = "UpdateProfileUserSchedule";
        public static final String UPDATE_PROFILE_USER_EXAMS = "UpdateProfileUserExams";
        public static final String UPDATE_PROFILE_USER_COURSES = "UpdateProfileUserCourses";
        public static final String UPDATE_CURRENT_FRAGMENT = "UpdateCurrentFragment";
        public static final String UPDATE_PROFILE_LOADER = "UpdateProfileLoader";

        public static final String PROFILE_DATABASE_USER_LOADED = "profileDatabaseLoaded";
        public static final String PROFILE_DATABASE_USER_FRIEND_LOADED = "profileFriendDatabaseLoaded";
        public static final String PROFILE_DATABASE_USER_COURSE_LOADED = "profileCourseDatabaseLoaded";
        public static final String PROFILE_DATABASE_USER_EXAM_LOADED = "profileExamDatabaseLoaded";
        public static final String PROFILE_DATABASE_USER_SCHEDULE_LOADED = "profileScheduleDatabaseLoaded";
    }

    public static final String UW_FLOW = "uwflow";

    // Keys used in bundles
    public static final String PROFILE_ID_KEY = "profileId";
    public static final String COURSE_ID_KEY = "courseId";
    public static final String TAB_ID_KEY = "tabId";

    public static final int COURSE_SCHEDULE_PAGE_INDEX = 0;
    public static final int COURSE_ABOUT_PAGE_INDEX = 1;
    public static final int COURSE_REVIEWS_PAGE_INDEX = 2;

    public static final int PROFILE_SCHEDULE_PAGE_INDEX = 0;
    public static final int PROFILE_COURSES_PAGE_INDEX = 1;
    public static final int PROFILE_EXAMS_PAGE_INDEX = 2;
    public static final int PROFILE_FRIENDS_PAGE_INDEX = 3;

    public static final int NAV_DRAWER_PROFILE_INDEX = 0;
    public static final int NAV_DRAWER_EXPLORE_INDEX = 1;
    public static final int NAV_DRAWER_SHORTLIST_INDEX = 2;
    public static final int NAV_DRAWER_ABOUT_INDEX = 3;

    public static final String FBID_DAVID = "541400376";
    public static final String FBID_JASPER = "505131734";
    public static final String FBID_WENTAO = "100000580776460";
    public static final String FBID_CHINMAY = "100002131130870";

    public static final String FB_PROFILE_PIC_LARGE_URL_FORMAT = "https://graph.facebook.com/%s/picture?type=large";

    public static final String SHORTLIST_TERM_ID = "9999_99";

} public class FlowDatabaseHelper extends OrmLiteSqliteOpenHelper {
    private static final String TAG = "FlowDatabaseHelper";

    // name of the database file for your application -- change to something appropriate for your app
    private static final String DATABASE_NAME = "flowDatabaseAndroid.db";

    // any time you make changes to your database objects, you may have to increase the database version
    private static final int DATABASE_VERSION = 1;

    /**
     * NOTE IF YOU CHANGE ANYTHING IN ANY OF THE DATABASE FILES OR ADD NEW DATABASE FILES
     * YOU MUST RE-RUN THE MAIN METHOD IN DatabaseConfigUtil class in the util folder
     * Use Run as a different java application! Not the same CONTEXT as flow.
     */

    private Dao<User, String> userDao;
    private Dao<Course, String> userCourseDao;
    private Dao<ScheduleCourse, String> userScheduleCourseDao;
    private Dao<Exam, Integer> userExamDao;
    private Dao<UserCourse, String> userCourseExtraDao;
    private Dao<ScheduleImage, String> userSchduleImageDao;

    public FlowDatabaseHelper(Context context) {
        super(context, DATABASE_NAME, null, DATABASE_VERSION, R.raw.ormlite_config);
    }

    @Override
    public void onCreate(SQLiteDatabase sqLiteDatabase, ConnectionSource connectionSource) {
        try {
            TableUtils.createTable(connectionSource, User.class);
            TableUtils.createTable(connectionSource, Course.class);
            TableUtils.createTable(connectionSource, ScheduleCourse.class);
            TableUtils.createTable(connectionSource, Exam.class);
            TableUtils.createTable(connectionSource, UserCourse.class);
            TableUtils.createTable(connectionSource, ScheduleImage.class);
        } catch (SQLException e) {
            Log.e(TAG, "Unable to create databases", e);
            Crashlytics.logException(e);
        }
    }

    @Override
    public void onUpgrade(SQLiteDatabase sqLiteDatabase, ConnectionSource connectionSource, int oldVer, int newVer) {
        try {
            TableUtils.dropTable(connectionSource, User.class, true);
            TableUtils.dropTable(connectionSource, Course.class, true);
            TableUtils.dropTable(connectionSource, ScheduleCourse.class, true);
            TableUtils.dropTable(connectionSource, Exam.class, true);
            TableUtils.dropTable(connectionSource, UserCourse.class, true);
            TableUtils.dropTable(connectionSource, ScheduleImage.class, true);
            onCreate(sqLiteDatabase, connectionSource);
        } catch (SQLException e) {
            Log.e(TAG, "Unable to upgrade database from version " + oldVer + " to new "
                    + newVer, e);
            Crashlytics.logException(e);
        }
    }

    /**
     * Call this method to delete all entries in the database
     *
     * @param connectionSource
     */
    public static void clearDatabase(ConnectionSource connectionSource){
        try {
            TableUtils.clearTable(connectionSource, User.class);
            TableUtils.clearTable(connectionSource, Course.class);
            TableUtils.clearTable(connectionSource, ScheduleCourse.class);
            TableUtils.clearTable(connectionSource, Exam.class);
            TableUtils.clearTable(connectionSource, UserCourse.class);
            TableUtils.clearTable(connectionSource, ScheduleImage.class);
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

    public Dao<User, String> getUserDao() throws SQLException {
        if (userDao == null) {
            userDao = getDao(User.class);
        }
        return userDao;
    }

    public Dao<Course, String> getUserCourseDao() throws SQLException {
        if (userCourseDao == null){
            userCourseDao = getDao(Course.class);
        }
        return userCourseDao;
    }

    public Dao<ScheduleCourse, String> getUserScheduleCourseDao() throws SQLException {
        if (userScheduleCourseDao == null){
            userScheduleCourseDao = getDao(ScheduleCourse.class);
        }
        return userScheduleCourseDao;
    }

    public Dao<Exam, Integer> getUserExamDao() throws SQLException {
        if (userExamDao == null){
            userExamDao = getDao(Exam.class);
        }
        return userExamDao;
    }

    public Dao<UserCourse, String> getUserCourseExtraDao() throws SQLException {
        if (userCourseExtraDao == null){
            userCourseExtraDao = getDao(UserCourse.class);
        }
        return userCourseExtraDao;
    }

    public Dao<ScheduleImage, String> getUserSchduleImageDao() throws SQLException {
        if (userSchduleImageDao == null){
            userSchduleImageDao = getDao(ScheduleImage.class);
        }
        return userSchduleImageDao;
    }
} @DatabaseTable(tableName = "user")
public class User implements Serializable {
    private static final long serialVersionUID = 1L;
    public static final String IS_ME = "is_me";

    @DatabaseField(id = true)
    private String id;

    @SerializedName("first_name")
    @DatabaseField(canBeNull = false)
    private String firstName;

    @SerializedName("num_points")
    @DatabaseField(canBeNull = false)
    private int point;

    @SerializedName("profile_pic_urls")
    @DatabaseField(dataType = DataType.SERIALIZABLE)
    private ProfilePicUrls profilePicUrls;

    @SerializedName("program_name")
    @DatabaseField(canBeNull = false)
    private String programName;

    @DatabaseField(canBeNull = false)
    private String name;

    @SerializedName("last_name")
    @DatabaseField(canBeNull = false)
    private String lastName;

    @SerializedName("num_invites")
    @DatabaseField
    private int numInvites;

    @DatabaseField
    private String fbid;

    @DatabaseField(canBeNull = false, columnName = "is_me")
    private boolean isMe;

    public User() {
    }

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public String getFirstName() {
        return firstName;
    }

    public void setFirstName(String firstName) {
        this.firstName = firstName;
    }

    public int getPoint() {
        return point;
    }

    public void setPoint(int point) {
        this.point = point;
    }

    public ProfilePicUrls getProfilePicUrls() {
        return profilePicUrls;
    }

    public void setProfilePicUrls(ProfilePicUrls profilePicUrls) {
        this.profilePicUrls = profilePicUrls;
    }

    public String getProgramName() {
        return programName;
    }

    public void setProgramName(String programName) {
        this.programName = programName;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getLastName() {
        return lastName;
    }

    public void setLastName(String lastName) {
        this.lastName = lastName;
    }

    public int getNumInvites() {
        return numInvites;
    }

    public void setNumInvites(int numInvites) {
        this.numInvites = numInvites;
    }

    public String getFbid() {
        return fbid;
    }

    public void setFbid(String fbid) {
        this.fbid = fbid;
    }

    public boolean isMe() {
        return isMe;
    }

    public void setMe(boolean isMe) {
        this.isMe = isMe;
    }

    public static class ProfilePicUrls implements Serializable {
        @SerializedName("default")
        protected String defaultPic;
        protected String large;
        protected String square;

        public ProfilePicUrls() {
        }

        public String getDefaultPic() {
            return defaultPic;
        }

        public void setDefaultPic(String defaultPic) {
            this.defaultPic = defaultPic;
        }

        public String getLarge() {
            return large;
        }

        public void setLarge(String large) {
            this.large = large;
        }

        public String getSquare() {
            return square;
        }

        public void setSquare(String square) {
            this.square = square;
        }

    }
} public class NavDrawerItem {
    private int id;
    private String name;
    private int iconResID;

    public NavDrawerItem(int id, String name, int iconResID) {
        this.id = id;
        this.name = name;
        this.iconResID = iconResID;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getIconResID() {
        return iconResID;
    }

    public void setIconResID(int iconResID) {
        this.iconResID = iconResID;
    }
} public class AboutFragment extends TrackedFragment implements View.OnClickListener {
    private FlowImageLoader flowImageLoader;

    private ImageView profilePic1;
    private ImageView profilePic2;
    private ImageView profilePic3;
    private ImageView profilePic4;
    private Button UWFlowButton;
    private TextView versionNameTextView;

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        // Inflate the layout for this fragment

        View rootView = inflater.inflate(R.layout.about_layout, container, false);

        profilePic1 = (ImageView)rootView.findViewById(R.id.user_image_1);
        profilePic2 = (ImageView)rootView.findViewById(R.id.user_image_2);
        profilePic3 = (ImageView)rootView.findViewById(R.id.user_image_3);
        profilePic4 = (ImageView)rootView.findViewById(R.id.user_image_4);
        UWFlowButton = (Button)rootView.findViewById(R.id.uwflow_button);
        versionNameTextView = (TextView)rootView.findViewById(R.id.version_name);

        flowImageLoader = new FlowImageLoader(getActivity().getApplicationContext());

        return rootView;
    }

    @Override
    public void onActivityCreated(Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
        flowImageLoader.loadImageInto(String.format(Constants.FB_PROFILE_PIC_LARGE_URL_FORMAT, Constants.FBID_DAVID), profilePic1);
        flowImageLoader.loadImageInto(String.format(Constants.FB_PROFILE_PIC_LARGE_URL_FORMAT, Constants.FBID_JASPER), profilePic2);
        flowImageLoader.loadImageInto(String.format(Constants.FB_PROFILE_PIC_LARGE_URL_FORMAT, Constants.FBID_WENTAO), profilePic3);
        flowImageLoader.loadImageInto(String.format(Constants.FB_PROFILE_PIC_LARGE_URL_FORMAT, Constants.FBID_CHINMAY), profilePic4);

        // Make profile pictures clickable
        profilePic1.setOnClickListener(this);
        profilePic2.setOnClickListener(this);
        profilePic3.setOnClickListener(this);
        profilePic4.setOnClickListener(this);

        // Make uwflow.com button clickable
        UWFlowButton.setOnClickListener(this);

        // Fetch and load current version number into TextView
        try {
            versionNameTextView.setText(String.format("Version: %s",
                    getActivity().getPackageManager().getPackageInfo(getActivity().getPackageName(), 0).versionName));
        } catch (PackageManager.NameNotFoundException e) {
            Log.e(Constants.UW_FLOW, "Couldn't resolve version name: " + e);
            Crashlytics.logException(e);
        }
    }

    @Override
    public void onClick(View v) {
        String fbid = null;
        switch(v.getId()) {
            case R.id.user_image_1:
                fbid = Constants.FBID_DAVID;
                break;
            case R.id.user_image_2:
                fbid = Constants.FBID_JASPER;
                break;
            case R.id.user_image_3:
                fbid = Constants.FBID_WENTAO;
                break;
            case R.id.user_image_4:
                fbid = Constants.FBID_CHINMAY;
                break;
            case R.id.uwflow_button:
                startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse("https://uwflow.com")));
                return;
        }
        FacebookUtilities.viewUserOnFacebook(getActivity(), fbid);
    }
} public class CourseFragment extends TrackedFragment implements SharableURL {
    private static final String TAG = "CourseFragment";

    private String mCourseID;
    private CoursePagerAdapter mCoursePagerAdapter;

    private View rootView;
    private ViewPager mViewPager;
    private PagerSlidingTabStrip mTabs;
    private TextView mCourseCodeTextView;
    private TextView mCourseNameTextView;
    private Button mShortlistButton;

    private CourseScheduleFragment mCourseScheduleFragment;
    private CourseAboutFragment mCourseAboutFragment;
    private CourseReviewsFragment mCourseReviewsFragment;

    private CourseDetail mCourseDetail;
    private UserCourseDetail userCourseDetail;
    private CourseUpdateReceiver courseUpdateReceiver;

    /**
     * Static method to instantiate this class with arguments passed as a bundle.
     *
     * @param courseId The ID of the course to show.
     * @return A new instance.
     */
    public static CourseFragment newInstance(String courseId) {
        CourseFragment courseFragment = new CourseFragment();

        Bundle args = new Bundle();
        args.putString(Constants.COURSE_ID_KEY, courseId);
        courseFragment.setArguments(args);

        return courseFragment;
    }

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        if (mCourseScheduleFragment == null) {
            mCourseScheduleFragment = new CourseScheduleFragment();
            mCourseScheduleFragment.setArguments(getArguments());
        }
        if (mCourseAboutFragment == null) {
            mCourseAboutFragment = new CourseAboutFragment();
            mCourseAboutFragment.setArguments(getArguments());
        }
        if (mCourseReviewsFragment == null) {
            mCourseReviewsFragment = new CourseReviewsFragment();
            mCourseReviewsFragment.setArguments(getArguments());
        }
        courseUpdateReceiver = new CourseUpdateReceiver();
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        rootView = inflater.inflate(R.layout.course_layout, container, false);
        mCourseCodeTextView = (TextView) rootView.findViewById(R.id.course_code);
        mCourseNameTextView = (TextView) rootView.findViewById(R.id.course_name);
        mTabs = (PagerSlidingTabStrip) rootView.findViewById(R.id.pager_tabs);

        mCoursePagerAdapter = new CoursePagerAdapter(getChildFragmentManager(),
                mCourseScheduleFragment,
                mCourseAboutFragment,
                mCourseReviewsFragment);

        // TODO(david): Should show a spinner here while course info is loading
        Bundle args = getArguments();
        if (args != null) {
            mCourseID = getArguments().getString(Constants.COURSE_ID_KEY);
            fetchCourseInfo(mCourseID);
        } else {
            String errorLog = "CourseFragment created without bundle: cannot fetch course details.";
            Log.e(TAG, errorLog);
            Crashlytics.log(Log.ERROR, TAG, errorLog);
        }

        mViewPager = (ViewPager) rootView.findViewById(R.id.pager);
        // Note: this is sorta cheating. We might need to decrease this number so that we don't run into memory issues.
        mViewPager.setOffscreenPageLimit(3);
        mViewPager.setAdapter(mCoursePagerAdapter);
        mTabs.setViewPager(mViewPager);

        // Set default tab to About
        mViewPager.setCurrentItem(Constants.COURSE_ABOUT_PAGE_INDEX);

        mShortlistButton = (Button) rootView.findViewById(R.id.shortlist_btn);
        final Button shareButton = (Button) rootView.findViewById(R.id.share_btn);

        if (!((FlowApplication)getActivity().getApplication()).isUserLoggedIn()) {
            mShortlistButton.setVisibility(View.GONE);

        }

        if (mCourseID != null) {
            final String humanizedCourseId = CourseUtil.humanizeCourseId(mCourseID);
            mShortlistButton.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    FlowApiRequests.addCourseToShortlist(mCourseID, new FlowApiRequestCallback() {
                        @Override
                        public void onSuccess(JSONObject response) {
                            String message = String.format("%s added to shortlist.", humanizedCourseId);
                            Toast.makeText(getActivity().getApplicationContext(), message, Toast.LENGTH_SHORT).show();
                            disableShortlistButton("Shortlisted");

                            if (mCourseDetail == null) {
                                return;
                            }

                            // Add the shortlisted course to the DB to update course list on profile page.
                            try {
                                JSONObject userCourseJson = response.getJSONObject("user_course");
                                UserCourse userCourse = JsonToDbUtil.getUserCourse(userCourseJson);
                                FlowDatabaseHelper helper = ((MainFlowActivity) getActivity()).getHelper();

                                final Dao<UserCourse, String> userCourseDao = helper.getUserCourseExtraDao();
                                userCourseDao.createOrUpdate(userCourse);

                                final Dao<Course, String> courseDao = helper.getUserCourseDao();
                                Course course = getCourseFromCourseDetail(mCourseDetail, userCourse.getId());
                                courseDao.createOrUpdate(course);
                            } catch (JSONException e) {
                                e.printStackTrace();
                                Crashlytics.logException(e);
                            } catch (SQLException e) {
                                e.printStackTrace();
                                Crashlytics.logException(e);
                            }
                        }

                        @Override
                        public void onFailure(String error) {
                            String message = String.format("Failed to add %s to shortlist. :(", humanizedCourseId);
                            Toast.makeText(getActivity().getApplicationContext(), message, Toast.LENGTH_SHORT).show();
                            Crashlytics.log(Log.ERROR, TAG, String.format("Failed to add %s to shortlist. Error: %s",
                                    mCourseID, error));
                        }
                    });

                    JSONObject properties = new JSONObject();
                    try {
                        properties.put("course_id", mCourseID);
                    } catch (JSONException e) {
                        e.printStackTrace();
                    }
                    FlowApplication app = ((FlowApplication) getActivity().getApplication());
                    app.track("Add to shortlist", properties);
                    app.getMixpanel().getPeople().increment("Add to shortlist", 1);
                }
            });

            shareButton.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    ((FlowApplication) getActivity().getApplication()).track("Share course");

                    Intent shareIntent = new Intent(Intent.ACTION_SEND);
                    shareIntent.setType("text/plain");
                    shareIntent.putExtra(Intent.EXTRA_TEXT,
                            String.format("https://www.uwflow.com/course/%s", mCourseID));
                    shareIntent.putExtra(Intent.EXTRA_SUBJECT, "Check out this course!");
                    startActivity(Intent.createChooser(shareIntent, "Share course"));
                }
            });
        } else {
            shareButton.setEnabled(false);
            mShortlistButton.setEnabled(false);
        }

        // Load user course info asynchronously
        final UserCoursesLoaderCallback userCoursesLoaderCallback = new UserCoursesLoaderCallback(
                getActivity().getApplicationContext(), this, ((MainFlowActivity) getActivity()).getHelper());
        getLoaderManager().initLoader(Constants.LoaderManagerId.COURSE_USER_COURSES_LOADER_ID, null,
                userCoursesLoaderCallback);

        return rootView;
    }

    @Override
    public void onViewCreated(View view, Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);
    }


    @Override
    public void onActivityCreated(Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
    }


    @Override
    public void onResume() {
        LocalBroadcastManager.getInstance(this.getActivity().getApplicationContext()).registerReceiver(courseUpdateReceiver,
                new IntentFilter(Constants.BroadcastActionId.UPDATE_CURRENT_FRAGMENT));
        super.onResume();
    }

    @Override
    public void onPause() {
        LocalBroadcastManager.getInstance(this.getActivity().getApplicationContext()).unregisterReceiver(courseUpdateReceiver);
        super.onPause();
    }

    @Override
    public String getUrl() {
        if (StringUtils.isEmpty(mCourseID)) {
            return null;
        }
        return Constants.BASE_URL + Constants.URL_COURSE_EXT + mCourseID;
    }

    private void fetchCourseInfo(String course) {

        FlowApiRequests.getCourse(
                course,
                new FlowApiRequestCallbackAdapter() {
                    @Override
                    public void getCourseCallback(CourseDetail courseDetail) {
                        mCourseDetail = courseDetail;

                        mCourseCodeTextView.setText(courseDetail.getCode());
                        mCourseNameTextView.setText(courseDetail.getName());

                        mCourseAboutFragment.loadCourseInfo(courseDetail);
                        mCourseReviewsFragment.loadCourseInfo(courseDetail);

                    }
                });
    }

    public void setUserCourseDetail(UserCourseDetail userCourseDetail) {
        this.userCourseDetail = userCourseDetail;

        // Change the shortlist button if the user has already taken/will take this course.
        for (UserCourse userCourse : userCourseDetail.getUserCourses()) {
            if (userCourse.getCourseId().equals(mCourseID)) {
                if (userCourse.getTermId().equals(Constants.SHORTLIST_TERM_ID)) {
                    disableShortlistButton("Shortlisted");
                } else {
                    disableShortlistButton(userCourse.getTermName());
                }
                break;
            }
        }
    }

    /**
     * A hacky method to convert a CourseDetail (returned from /api/v1/courses/:id) to a Course (returned from
     * /api/v1/user/courses).
     *
     * TODO(david): This really shouldn't be here -- the CourseDetail class should be removed completely and
     *     replaced with just Course.
     */
    private Course getCourseFromCourseDetail(CourseDetail courseDetail, String userCourseId) {
        Gson gson = new Gson();
        String json = gson.toJson(courseDetail);
        Course course = JsonToDbUtil.getCourse(json);
        course.setUserCourseId(userCourseId);
        return course;
    }

    private void disableShortlistButton(String buttonText) {
        mShortlistButton.setText(buttonText);

        // Change to checked icon
        int iconId = R.drawable.ic_course_taken;
        Drawable icon = getActivity().getApplicationContext().getResources().getDrawable(iconId);
        icon.setBounds(0, 0, 28, 28);  // TODO(david): Figure out a way to not hardcode this number
        mShortlistButton.setCompoundDrawables(icon, null, null, null);

        mShortlistButton.setEnabled(false);
    }

    private static class CoursePagerAdapter extends FragmentStatePagerAdapter {
        Fragment fragment1, fragment2, fragment3;
        private static final String[] TITLES = new String[]{
                "Schedule",
                "About",
                "Reviews"
        };

        public CoursePagerAdapter(FragmentManager fm, Fragment fragment1, Fragment fragment2, Fragment fragment3) {
            super(fm);
            this.fragment1 = fragment1;
            this.fragment2 = fragment2;
            this.fragment3 = fragment3;
        }

        @Override
        public Fragment getItem(int position) {
            switch (position) {
                case Constants.COURSE_SCHEDULE_PAGE_INDEX:
                    return fragment1;
                case Constants.COURSE_ABOUT_PAGE_INDEX:
                    return fragment2;
                case Constants.COURSE_REVIEWS_PAGE_INDEX:
                    return fragment3;
                default:
                    return fragment2;
            }
        }

        @Override
        public int getCount() {
            return TITLES.length;
        }

        @Override
        public CharSequence getPageTitle(int position) {
            return TITLES[position];
        }
    }

    protected class CourseUpdateReceiver extends BroadcastReceiver {

        @Override
        public void onReceive(Context context, Intent intent) {
            if (mCourseID != null)
                fetchCourseInfo(mCourseID);
        }
    }
} public class ExploreFragment extends TrackedFragment implements AdapterView.OnItemClickListener {
    private static final String TAG = "ExploreFragment";
    private static final int ITEMS_PER_PAGE = 20;

    // TODO(david): Convert all the search stuff to use a SearchView, which gets us things like suggestions
    //     See http://developer.android.com/guide/topics/search/search-dialog.html
    private EditText mSearchBox;
    private Spinner mSortSpinner;
    private CheckBox mExcludeTakenCheckBox;
    private ListView mResultsListView;
    private View mFooterView;

    private boolean mSortSpinnerFired = false;
    private int mSortSpinnerPosition = -1;
    private boolean mExcludeTakenCheckBoxChecked = false;

    private List<Course> mSearchResultList = new ArrayList<Course>();

    private SearchResultAdapter mSearchResultAdapter;

    private static final Map<String, String> mSortModesMap;
    static {
        Map<String, String> map = new LinkedHashMap<String, String>();
        map.put("Popular", "popular");
        map.put("Friends taken", "friends_taken");
        map.put("Interesting", "interesting");
        map.put("Easy", "easy");
        map.put("Hard", "hard");
        map.put("Course code", "course code");
        mSortModesMap = Collections.unmodifiableMap(map);
    }

    private ArrayAdapter<CharSequence> mSortModesAdapter;

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        // Inflate the layout for this fragment
        View rootView = inflater.inflate(R.layout.explore_layout, container, false);
        mSearchBox = (EditText)rootView.findViewById(R.id.search_box);
        mSortSpinner = (Spinner)rootView.findViewById(R.id.sort_spinner);
        mExcludeTakenCheckBox = (CheckBox)rootView.findViewById(R.id.checkbox_exclude_taken);
        mResultsListView = (ListView)rootView.findViewById(R.id.results_list);

        mFooterView = inflater.inflate(R.layout.search_results_footer, null, false);
        mResultsListView.addFooterView(mFooterView);
        mSearchResultAdapter = new SearchResultAdapter(mSearchResultList, getActivity());
        mResultsListView.setAdapter(mSearchResultAdapter);
        mResultsListView.setOnItemClickListener(this);

        Set<String> sortModes = new LinkedHashSet<String>(mSortModesMap.keySet());

        // Hide various controls for logged out users
        boolean isUserLoggedIn = ((FlowApplication)getActivity().getApplication()).isUserLoggedIn();
        if (!isUserLoggedIn) {
            sortModes.remove("Friends taken");
            mExcludeTakenCheckBox.setVisibility(View.GONE);
        }

        // Populate the sort spinner
        mSortModesAdapter = new ArrayAdapter <CharSequence>(getActivity(), android.R.layout.simple_spinner_item,
                sortModes.toArray(new CharSequence[0]));
        mSortModesAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
        mSortSpinner.setAdapter(mSortModesAdapter);

        setUpListeners();

        return rootView;
    }

    private void setUpListeners() {
        mSortSpinnerFired = false;

        // Do search on sort mode change
        mSortSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
            @Override
            public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
                ((TextView) parent.getChildAt(0)).setTextColor(getResources().getColor(R.color.white));

                // Android stupidly fires this listener on create of the spinner. This is used to ignore that.
                if (!mSortSpinnerFired) {
                    mSortSpinnerFired = true;
                    return;
                }

                // Item selection didn't actually change. This could happen when back button is pressed to return to
                // this fragment.
                if (mSortSpinnerPosition == position) {
                    return;
                }
                mSortSpinnerPosition = position;

                doSearch(0);
            }

            @Override
            public void onNothingSelected(AdapterView<?> parent) {
                Log.e(TAG, "No sort mode selected. This should never happen.");
            }
        });

        // Do search on checkbox include/exclude courses taken change
        mExcludeTakenCheckBox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
            @Override
            public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
                // Checkbox selection didn't actually change. This may happen when back button pressed to return to
                // this fragment.
                if (isChecked == mExcludeTakenCheckBoxChecked) {
                    return;
                }
                mExcludeTakenCheckBoxChecked = isChecked;

                doSearch(0);
            }
        });

        // Do search on edit text submit
        mSearchBox.setOnEditorActionListener(new TextView.OnEditorActionListener() {
            @Override
            public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
                if ( (FlowApplication.isBlackberry==false && actionId == EditorInfo.IME_ACTION_SEARCH) ||
                        (FlowApplication.isBlackberry==true && actionId == EditorInfo.IME_ACTION_UNSPECIFIED )) {
                    doSearch(0);

                    // Hide the keyboard. See http://stackoverflow.com/questions/1109022
                    v.clearFocus();
                    InputMethodManager imm = (InputMethodManager)getActivity().getSystemService(
                            Context.INPUT_METHOD_SERVICE);
                    imm.hideSoftInputFromWindow(v.getWindowToken(), 0);

                    return true;
                }
                return false;
            }
        });

        // Infinite scroll: load next page of results when scrolled towards the end of the list.
        mResultsListView.setOnScrollListener(new InfiniteScrollListener() {
            @Override
            public void onLoadMore(int page) {
                doSearch(page);
            }
        });
    }

    @Override
    public void onActivityCreated(Bundle savedInstanceState){
        super.onActivityCreated(savedInstanceState);

        if (mSearchResultList.isEmpty()) {
            doSearch(0);
        }
    }

    @Override
    public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
        Object item = parent.getItemAtPosition(position);
        if (item == null) {
            return;
        }

        Course course = (Course) item;
        getFragmentManager().beginTransaction()
                .replace(R.id.content_frame, CourseFragment.newInstance(course.getId()))
                .addToBackStack(null)
                .commit();
    }

    /**
     * Performs a search with parameters set on the UI.
     * @param page The page to load. Pass 0 if loading search results for a different query/filters.
     */
    private void doSearch(final int page) {
        mFooterView.setVisibility(View.VISIBLE);

        if (page == 0) {
            mResultsListView.setAlpha((float)0.4);
        }

        // Get keywords
        Editable searchBoxText = mSearchBox.getText();
        String keywords = "";
        if (searchBoxText != null) {
            keywords = searchBoxText.toString();
        }

        // Get sort mode
        Object sortItem = mSortSpinner.getSelectedItem();
        String sortMode = sortItem == null ? "" : mSortModesMap.get(sortItem.toString());

        // Get whether we should exclude taken courses
        boolean excludeTakenCourses = mExcludeTakenCheckBox.isChecked();

        int count = ITEMS_PER_PAGE;
        int offset = page * count;

        FlowApiRequests.searchCourses(keywords, sortMode, excludeTakenCourses, count, offset,
                new FlowApiRequestCallbackAdapter() {
            @Override
            public void searchCoursesCallback(SearchResults searchResults) {
                if (page == 0) {
                    mSearchResultList.clear();
                }
                mSearchResultList.addAll(searchResults.getCourses());
                mSearchResultAdapter.notifyDataSetChanged();

                if (page == 0) {
                    mResultsListView.setSelectionAfterHeaderView();
                }

                mResultsListView.setAlpha((float)1.0);
                mFooterView.setVisibility(View.GONE);
            }
        });

        // Track for analytics
        JSONObject properties = new JSONObject();
        try {
            properties
                    .put("keywords", keywords)
                    .put("sort_mode", sortMode)
                    .put("exclude_taken_course", excludeTakenCourses)
                    .put("count", count)
                    .put("offset", offset);
        } catch (JSONException e) {
            e.printStackTrace();
        }

        ((FlowApplication) getActivity().getApplication()).track("Course search", properties);
    }

    // Adapted from http://guides.thecodepath.com/android/Endless-Scrolling-with-AdapterViews
    private abstract class InfiniteScrollListener implements AbsListView.OnScrollListener {
        private final int remainingThreshold = 5;
        private int currentPage = 0;
        private int currentTotalItems = 0;
        private boolean loading = true;

        @Override
        public void onScrollStateChanged(AbsListView view, int scrollState) {
        }

        @Override
        public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
            int totalItemsLessFooter = totalItemCount - 1;

            // If total items has somehow gone down, then search results were probably refreshed.
            if (totalItemsLessFooter< currentTotalItems) {
                currentPage = 0;
                currentTotalItems = totalItemsLessFooter;
                if (totalItemsLessFooter == 0) {
                    loading = true;
                }
            }

            // See if loading has just completed.
            if (loading && totalItemsLessFooter > currentTotalItems) {
                currentTotalItems = totalItemsLessFooter;
                currentPage++;
                loading = false;
            }

            // If we're not already loading, see if the remaining items below the fold is about to exceed the threshold.
            if (!loading && totalItemsLessFooter - (firstVisibleItem + visibleItemCount) <= remainingThreshold) {
                onLoadMore(currentPage + 1);
                loading = true;
            }
        }

        public abstract void onLoadMore(int page);
    }
} public class ProfileFragment extends TrackedFragment implements SharableURL {
    private String mProfileID;
    private ProfilePagerAdapter profilePagerAdapter;
    private MenuItem mFbMenuItem;

    protected ImageView userPhotoImageView;
    protected ImageView coverPhotoImageView;
    protected TextView userName;
    protected TextView userProgram;
    protected ViewPager viewPager;
    protected PagerSlidingTabStrip tabs;
    protected User user;
    protected UserFriends userFriends;
    protected Exams userExams;
    protected UserCourseDetail userCourses;
    protected ScheduleCourses userSchedule;
    protected ProfileReceiver profileReceiver;
    protected ProfileRefreshReceiver profileRefreshReceiver;
    protected Bitmap userCover;
    protected Bitmap userProfileImage;
    protected FlowImageLoader mFlowImageLoader;
    protected FlowDatabaseLoader mFlowDatabaseLoader;
    // for having hard reference to callbacks so they dont get garbage recycled
    protected FlowImageLoaderCallback userCoverCallback;
    protected FlowImageLoaderCallback userProfileCallback;
    protected FlowResultCollector collector;

    /**
     * Static method to instantiate this class with arguments passed as a bundle.
     *
     * @param userID The ID of the user to show.
     * @param tabID  The ID of the initial tab to show
     * @return A new instance.
     */
    public static ProfileFragment newInstance(String userID, Integer tabID) {
        ProfileFragment profileFragment = new ProfileFragment();

        Bundle bundle = new Bundle();
        if (tabID != null) {
            bundle.putInt(Constants.TAB_ID_KEY, tabID);
        }
        if (userID != null) {
            bundle.putString(Constants.PROFILE_ID_KEY, userID);
        }
        profileFragment.setArguments(bundle);

        return profileFragment;
    }

    public static ProfileFragment newInstance(String userId) {
        return newInstance(userId, null);
    }

    public static ProfileFragment newInstance(User user) {
        return newInstance(user.getId());
    }

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setHasOptionsMenu(true);
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        // Inflate the layout for this fragment
        View rootView = inflater.inflate(R.layout.profile_layout, container, false);

        userPhotoImageView = (ImageView) rootView.findViewById(R.id.user_image);
        userName = (TextView) rootView.findViewById(R.id.user_name);
        userProgram = (TextView) rootView.findViewById(R.id.user_program);
        coverPhotoImageView = (ImageView) rootView.findViewById(R.id.cover_photo);
        viewPager = (ViewPager) rootView.findViewById(R.id.pager);
        // Note: this is sorta cheating. We might need to decrease this number so that we don't run into memory issues.
        viewPager.setOffscreenPageLimit(3);

        Integer tabID = getArguments() != null ? getArguments().getInt(Constants.TAB_ID_KEY) : null;
        if (tabID == null) {
            // Set default tab to Schedule
            viewPager.setCurrentItem(Constants.PROFILE_SCHEDULE_PAGE_INDEX);
        } else {
            viewPager.setCurrentItem(tabID);
        }

        init();

        if (getArguments() != null) {
            mProfileID = getArguments().getString(Constants.PROFILE_ID_KEY);
        }

        profilePagerAdapter = new ProfilePagerAdapter(getChildFragmentManager(), getArguments(), mProfileID == null);

        viewPager.setAdapter(profilePagerAdapter);
        tabs = (PagerSlidingTabStrip) rootView.findViewById(R.id.pager_tabs);
        tabs.setViewPager(viewPager);

        initLoaders();
        populateData();
        return rootView;
    }

    @Override
    public void onActivityCreated(Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
    }

    @Override
    public void onDestroyView() {
        // Unregister since the activity is not visible
        LocalBroadcastManager.getInstance(this.getActivity().getApplicationContext()).unregisterReceiver(profileReceiver);
        super.onDestroyView();
    }

    @Override
    public void onResume() {
        LocalBroadcastManager.getInstance(this.getActivity().getApplicationContext()).registerReceiver(profileRefreshReceiver,
                new IntentFilter(Constants.BroadcastActionId.UPDATE_CURRENT_FRAGMENT));
        super.onResume();
    }

    @Override
    public void onPause() {
        LocalBroadcastManager.getInstance(this.getActivity().getApplicationContext()).unregisterReceiver(profileRefreshReceiver);
        super.onPause();
    }

    @Override
    public void onPrepareOptionsMenu(Menu menu) {
        super.onPrepareOptionsMenu(menu);

        mFbMenuItem = menu.findItem(R.id.action_view_on_fb);
        mFbMenuItem.setVisible(true);
    }

    @Override
    public String getUrl() {
        if (user == null || StringUtils.isEmpty(user.getId())) {
            return null;
        }
        return Constants.BASE_URL + Constants.URL_PROFILE_EXT + user.getId();
    }

    protected void initLoaders() {
        final UserLoaderCallback userLoaderCallback = new UserLoaderCallback(getActivity().getApplicationContext(), this, ((MainFlowActivity) getActivity()).getHelper());
        final UserFriendsLoaderCallback userFriendsLoaderCallback = new UserFriendsLoaderCallback(getActivity().getApplicationContext(), this, ((MainFlowActivity) getActivity()).getHelper());
        final UserScheduleLoaderCallback userScheduleLoaderCallback = new UserScheduleLoaderCallback(getActivity().getApplicationContext(), this, ((MainFlowActivity) getActivity()).getHelper());
        final UserExamsLoaderCallback userExamsLoaderCallback = new UserExamsLoaderCallback(getActivity().getApplicationContext(), this, ((MainFlowActivity) getActivity()).getHelper());
        final UserCoursesLoaderCallback userCoursesLoaderCallback = new UserCoursesLoaderCallback(getActivity().getApplicationContext(), this, ((MainFlowActivity) getActivity()).getHelper());
        getLoaderManager().initLoader(Constants.LoaderManagerId.PROFILE_LOADER_ID, null, userLoaderCallback);
        getLoaderManager().initLoader(Constants.LoaderManagerId.PROFILE_FRIENDS_LOADER_ID, null, userFriendsLoaderCallback);
        getLoaderManager().initLoader(Constants.LoaderManagerId.PROFILE_SCHEDULE_LOADER_ID, null, userScheduleLoaderCallback);
        getLoaderManager().initLoader(Constants.LoaderManagerId.PROFILE_EXAMS_LOADER_ID, null, userExamsLoaderCallback);
        getLoaderManager().initLoader(Constants.LoaderManagerId.PROFILE_COURSES_LOADER_ID, null, userCoursesLoaderCallback);
    }

    protected void init() {
        mFlowImageLoader = new FlowImageLoader(getActivity().getApplicationContext());
        mFlowDatabaseLoader = new FlowDatabaseLoader(getActivity().getApplicationContext(), ((MainFlowActivity) getActivity()).getHelper());
        profileReceiver = new ProfileReceiver();
        profileRefreshReceiver = new ProfileRefreshReceiver();
        userCoverCallback = new FlowImageLoaderCallback() {
            @Override
            public void onImageLoaded(Bitmap bitmap) {
                userCover = bitmap;
            }
        };
        userProfileCallback = new FlowImageLoaderCallback() {
            @Override
            public void onImageLoaded(Bitmap bitmap) {
                userProfileImage = bitmap;
            }
        };
        LocalBroadcastManager.getInstance(this.getActivity().getApplicationContext()).registerReceiver(profileReceiver,
                new IntentFilter(Constants.BroadcastActionId.UPDATE_PROFILE_USER));
    }

    protected void populateData() {
        if (userProfileImage != null) {
            userPhotoImageView.setImageBitmap(userProfileImage);
        }

        if (userCover != null) {
            coverPhotoImageView.setImageBitmap(userCover);
        }

        if (user == null)
            return;

        if (userProfileImage == null && user.getProfilePicUrls() != null) {
            mFlowImageLoader.loadImage(user.getProfilePicUrls().getLarge(), userPhotoImageView, userProfileCallback);

        }

        userName.setText(user.getName());
        userProgram.setText(user.getProgramName());

        if (user.isMe()) {
            // Associate the current user for all future tracking calls to Mixpanel
            // TODO(david): Ideally this should be called as soon as the user logs in. Will need to change server API to
            //     return user info on POST /api/v1/login/facebook.
            FlowApplication app = (FlowApplication) getActivity().getApplication();
            app.getMixpanel().identify(user.getId());

            // Send Crashlytics info about current user (may be helpful in communicating/asking for more info)
            Crashlytics.setUserIdentifier(user.getId());
            Crashlytics.setUserName(user.getName());
        }
    }

    // Getters and Setters for child fragment to pull data
    public User getUser() {
        return user;
    }

    public void setUser(User user) {
        if (user == null) {
            if (mFbMenuItem != null) {
                mFbMenuItem.setEnabled(false);
            }
            return;
        }
        this.user = user;
        if (user.getFbid() != null) {
            if (mFbMenuItem != null) {
                mFbMenuItem.setEnabled(true);
            }

            if (userCover == null) {
                FlowApiRequests.getUserCoverImage(this.getActivity().getApplicationContext(), user.getFbid(), coverPhotoImageView,
                        userCoverCallback);
            }
        }
        BroadcastFactory.fireProfileMeFragmentDataLoaded(this.getActivity().getApplicationContext());
    }

    public UserFriends getUserFriends() {
        return userFriends;
    }

    public void setUserFriends(UserFriends userFriends) {
        this.userFriends = userFriends;
        BroadcastFactory.fireProfileFriendFragmentDataLoaded(this.getActivity().getApplicationContext());
    }

    public Exams getUserExams() {
        return userExams;
    }

    public void setUserExams(Exams userExams) {
        this.userExams = userExams;
        BroadcastFactory.fireProfileExamFragmentDataLoaded(this.getActivity().getApplicationContext());
    }

    public UserCourseDetail getUserCourses() {
        return userCourses;
    }

    public void setUserCourses(UserCourseDetail userCourses) {
        this.userCourses = userCourses;
        BroadcastFactory.fireProfileCourseFragmentDataLoaded(this.getActivity().getApplicationContext());

    }

    public ScheduleCourses getUserSchedule() {
        return userSchedule;
    }

    public void setUserSchedule(ScheduleCourses userSchedule) {
        this.userSchedule = userSchedule;
        BroadcastFactory.fireProfileScheduleFragmentDataLoaded(this.getActivity().getApplicationContext());
    }

    public String getProfileID() {
        return mProfileID;
    }

    public void setProfileID(String profileId) {
        this.mProfileID = profileId;
    }

    /**
     * This class is to listen for the event when the current user is loaded (from database or network)
     * and populate data.
     */
    protected class ProfileReceiver extends BroadcastReceiver {
        @Override
        public void onReceive(Context context, Intent intent) {
            populateData();
        }
    }

    /**
     * This class is to listen for the refresh button and reloads all the data
     */
    protected class ProfileRefreshReceiver extends BroadcastReceiver {
        @Override
        public void onReceive(Context context, Intent intent) {
            userProfileImage = null;
            userCover = null;
            // TODO maybe insted of clearing the whole cache, we can manually clear part of the cache we want to reload
            mFlowImageLoader.clearImageCache();
            // if not logged in user then just fire async call without loading into database
            if (mProfileID != null) {
                Intent reloadLoadersIntent = new Intent(Constants.BroadcastActionId.UPDATE_PROFILE_LOADER);
                LocalBroadcastManager.getInstance(ProfileFragment.this.getActivity().getApplicationContext()).sendBroadcast(reloadLoadersIntent);
            } else {
                // fire async call and load into database
                mFlowDatabaseLoader.loadOrReloadProfileForLogin(null);
            }
        }
    }
} public class FlowAsyncClient {
    private static final String TAG = "FlowAsyncClient";
    private static final String CSRF_PREFERENCE_KEY = "csrf_token";

    protected static AsyncHttpClient client = new AsyncHttpClient();
    private static PersistentCookieStore cookieStore;
    private static SharedPreferences preferences;
    private static String csrfToken;

    public static void init(Context c) {
        cookieStore = new PersistentCookieStore(c);
        client.setCookieStore(cookieStore);

        preferences = PreferenceManager.getDefaultSharedPreferences(c);
        csrfToken = preferences.getString(CSRF_PREFERENCE_KEY, null);
        useCsrfToken();
    }

    private static void useCsrfToken() {
        if (StringUtils.isNotEmpty(csrfToken)) {
            client.addHeader("X-CSRF-Token", csrfToken);
        }
    }

    public static void get(String url, RequestParams params, AsyncHttpResponseHandler responseHandler) {
        client.get(getAbsoluteUrl(url), params, responseHandler);
    }

    public static void post(String url, RequestParams params, AsyncHttpResponseHandler responseHandler) {
        client.post(getAbsoluteUrl(url), params, responseHandler);
    }

    public static void put(String url, RequestParams params, AsyncHttpResponseHandler responseHandler) {
        client.put(getAbsoluteUrl(url), params, responseHandler);
    }

    public static void get(HashMap<String, String> headers, String url, RequestParams params, AsyncHttpResponseHandler responseHandler) {
        final AsyncHttpClient specialClient = new AsyncHttpClient();

        for (String key : headers.keySet()) {
            specialClient.addHeader(key, headers.get(key));
        }
        specialClient.get(getAbsoluteUrl(url), params, responseHandler);
    }

    private static String getAbsoluteUrl(String relativeUrl) {
        return Constants.BASE_URL + relativeUrl;
    }

    public static Cookie getSessionCookie() {
        if (cookieStore == null) {
            return null;
        }

        List<Cookie> cookies = cookieStore.getCookies();
        for (Cookie cookie: cookies) {
            if (cookie.getDomain().equals(Constants.SESSION_COOKIE_DOMAIN) && cookie.getName().equals("session")) {
                return cookie;
            }
        }

        return null;
    }

    public static void clearCookie() {
        if (cookieStore != null) {
            cookieStore.clear();
        }
    }

    public static void setCsrfToken(String xsrfToken) {
        csrfToken = xsrfToken;
        useCsrfToken();

        if (preferences != null) {
            preferences.edit()
                    .putString(CSRF_PREFERENCE_KEY, csrfToken)
                    .commit();
        }
    }

    public static String getCsrfToken() {
        return csrfToken;
    }
} public interface SharableURL {
    /*
     * Get URL associated with the implementing class
     */
    public String getUrl();
} public class FacebookUtilities {

    /**
     * Creates an intent to view a user on Facebook. Attempts to use the Facebook app if installed, falling back to web.
     * @param context
     * @param fbid The user's Facebook ID.
     * @return
     */
    public static Intent getOpenFBProfileIntent(Context context, String fbid) {
        try {
            context.getPackageManager()
                    .getPackageInfo("com.facebook.katana", 0);
            return new Intent(Intent.ACTION_VIEW,
                    Uri.parse(String.format("fb://profile/%s", fbid)));
        } catch (Exception e) {
            return new Intent(Intent.ACTION_VIEW,
                    Uri.parse(String.format("https://www.facebook.com/profile.php?id=%s", fbid)));
        }
    }

    public static Bitmap getFacebookProfilePicture(String url){
        if (url != null) {
            try {
                URL imageURL = new URL(url);
                Bitmap bitmap = BitmapFactory.decodeStream(imageURL.openConnection().getInputStream());
                return bitmap;
            } catch (MalformedURLException e) {
                Log.e(Constants.UW_FLOW, "Error creating image URL: " + e);
                Crashlytics.logException(e);
            } catch (IOException e) {
                Log.e(Constants.UW_FLOW, "Error opening connection during image fetch: " + e);
                Crashlytics.logException(e);
            }
        }
        return null;
    }

    public static void viewUserOnFacebook(Context context, String facebookId) {
        Intent profileIntent = FacebookUtilities.getOpenFBProfileIntent(context, facebookId);
        context.startActivity(profileIntent);

    }

    public static String getFirstWord(String string) {
        String firstWord = null;
        if(string.contains(" ")){
            firstWord= string.substring(0, string.indexOf(" "));
        }
        return firstWord;
    }
}
import android.app.Activity;
import android.app.PendingIntent;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.res.Configuration;
import android.nfc.NdefMessage;
import android.nfc.NdefRecord;
import android.nfc.NfcAdapter;
import android.nfc.NfcEvent;
import android.os.Bundle;
import android.os.PatternMatcher;
import android.support.v4.app.ActionBarDrawerToggle;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentTransaction;
import android.support.v4.content.LocalBroadcastManager;
import android.support.v4.widget.DrawerLayout;
import android.util.Log;
import android.view.MenuItem;
import android.view.View;
import android.widget.*;
import com.facebook.Session;
import com.uwflow.flow_android.adapters.NavDrawerAdapter;
import com.uwflow.flow_android.constant.Constants;
import com.uwflow.flow_android.dao.FlowDatabaseHelper;
import com.uwflow.flow_android.db_object.User;
import com.uwflow.flow_android.entities.NavDrawerItem;
import com.uwflow.flow_android.fragment.AboutFragment;
import com.uwflow.flow_android.fragment.CourseFragment;
import com.uwflow.flow_android.fragment.ExploreFragment;
import com.uwflow.flow_android.fragment.ProfileFragment;
import com.uwflow.flow_android.network.FlowAsyncClient;
import com.uwflow.flow_android.nfc.SharableURL;
import com.uwflow.flow_android.util.FacebookUtilities;
import org.json.JSONException;
import org.json.JSONObject;
import java.util.ArrayList;
package com.uwflow.flow_android;



public class MainFlowActivity extends FlowActivity {
    private static final String TAG = "MainFlowActivity";

    private ListView mDrawerList;
    private DrawerLayout mDrawerLayout;
    private LinearLayout mDrawerContainer;
    private ActionBarDrawerToggle mDrawerToggle;
    private LinearLayout mBottomItemLayout;
    private NavDrawerAdapter mNavDrawerAdapter;
    private ArrayList<NavDrawerItem> mDrawerItems;

    private NfcAdapter mNfcAdapter;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.flow_main);

        mDrawerContainer = (LinearLayout) findViewById(R.id.drawer);
        mDrawerList = (ListView) findViewById(R.id.drawer_list);
        mDrawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout);
        mBottomItemLayout = (LinearLayout) findViewById(R.id.drawer_bottom_item);

        boolean isUserLoggedIn = ((FlowApplication)getApplication()).isUserLoggedIn();

        mDrawerItems = new ArrayList<NavDrawerItem>();

        if (isUserLoggedIn) {
            mDrawerItems.add(new NavDrawerItem(
                    Constants.NAV_DRAWER_PROFILE_INDEX,
                    getString(R.string.drawer_item_profile),
                    R.drawable.drawer_profile_icon));
        }

        mDrawerItems.add(new NavDrawerItem(
                Constants.NAV_DRAWER_EXPLORE_INDEX,
                getString(R.string.drawer_item_explore),
                R.drawable.drawer_explore_icon));
        mDrawerItems.add(new NavDrawerItem(
                Constants.NAV_DRAWER_ABOUT_INDEX,
                getString(R.string.drawer_item_about),
                R.drawable.drawer_about_icon));

        // Configure bottom menu item
        TextView label = (TextView) mBottomItemLayout.findViewById(R.id.drawer_item_name);
        ImageView icon = (ImageView) mBottomItemLayout.findViewById(R.id.drawer_item_icon);

        if (isUserLoggedIn) {
            label.setText("Log out");
            icon.setImageResource(R.drawable.drawer_log_out_icon);
            mBottomItemLayout.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    if (Session.getActiveSession() != null) {
                        Session.getActiveSession().closeAndClearTokenInformation();
                    }
                    Session.setActiveSession(null);

                    // Remove flow cookies
                    FlowAsyncClient.clearCookie();
                    // Remove database entries from all tables on logout
                    FlowDatabaseHelper.clearDatabase(getHelper().getConnectionSource());
                    FlowApplication app = (FlowApplication) getApplication();
                    app.setUserLoggedIn(false);
                    app.track("Logout");

                    Intent intent = new Intent(MainFlowActivity.this, LoginActivity.class);
                    intent.putExtra("finish", true);
                    intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP |
                            Intent.FLAG_ACTIVITY_CLEAR_TASK |
                            Intent.FLAG_ACTIVITY_NEW_TASK);
                    startActivity(intent);
                    finish();
                    return;
                }
            });
        } else {
            label.setText("Log in");
            icon.setImageResource(R.drawable.drawer_log_in_icon);
            mBottomItemLayout.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    ((FlowApplication) getApplication()).track("Login intent from logged out user");

                    Intent intent = new Intent(MainFlowActivity.this, LoginActivity.class);
                    startActivity(intent);
                }
            });
        }

        mNavDrawerAdapter = new NavDrawerAdapter(mDrawerItems, this);
        mDrawerList.setAdapter(mNavDrawerAdapter);

        mDrawerToggle = new ActionBarDrawerToggle(
                this,
                mDrawerLayout,
                R.drawable.ic_drawer_am,
                R.string.drawer_open,
                R.string.drawer_close
        ) {
            public void onDrawerClosed(View view) {
                super.onDrawerClosed(view);

            }
            public void onDrawerOpened(View view) {
                super.onDrawerOpened(view);
            }
        };

        // Set the drawer toggle as the DrawerListener
        mDrawerLayout.setDrawerListener(mDrawerToggle);

        mDrawerList.setOnItemClickListener(new DrawerItemClickListener());

        if (savedInstanceState == null) {
            Fragment initialFragment;
            if (isUserLoggedIn) {
                selectItem(Constants.NAV_DRAWER_PROFILE_INDEX, false);
            } else {
                selectItem(Constants.NAV_DRAWER_EXPLORE_INDEX, false);
            }
        }

        // Check for available NFC Adapter
        mNfcAdapter = NfcAdapter.getDefaultAdapter(this);

        if (mNfcAdapter == null) {
            // TODO: Log that NFC is unavailable on this device?
        } else {
            // Register NFC push message callback
            mNfcAdapter.setNdefPushMessageCallback(new NfcAdapter.CreateNdefMessageCallback() {
                @Override
                public NdefMessage createNdefMessage(NfcEvent event) {
                    Fragment fragment = getSupportFragmentManager().findFragmentById(R.id.content_frame);
                    if (fragment != null && fragment instanceof SharableURL) {