public class Config {
    // OAuth 2.0 related config
    public static final String APP_NAME = "Your-App-Name";
    public static final String API_KEY = "API_KEY"; // from the APIs console
    public static final String CLIENT_ID = "0000000000000.apps.googleusercontent.com"; // from the APIs console
  public static final String EMS_SLOTS = "http://javazone.no/ems/server/events/3baa25d3-9cca-459a-90d7-9fc349209289/slots";
  public static final String EMS_ROOMS = "http://javazone.no/ems/server/events/3baa25d3-9cca-459a-90d7-9fc349209289/rooms";

  // Conference API-specific config
    // NOTE: the backend used for the Google I/O 2012 Android app is not currently open source, so
    // you should modify these fields to reflect your own backend.

    private static final String BASE_URL = "http://javazone.no/ems/server/events/3baa25d3-9cca-459a-90d7-9fc349209289";

  //TODO slots
    public static final String GET_ALL_BLOCKS =  BASE_URL + "/slots";
    public static final String GET_ALL_SESSIONS_URL      = BASE_URL + "/sessions";

    // Static file host for the sandbox data
    public static final String GET_SANDBOX_URL = "https://javazone.no";

    // YouTube API config
    public static final String YOUTUBE_API_KEY = "API_KEY";
    // YouTube share URL
    public static final String YOUTUBE_SHARE_URL_PREFIX = "http://youtu.be/";

    // Livestream captions config
    public static final String PRIMARY_LIVESTREAM_CAPTIONS_URL = "TODO";
    public static final String SECONDARY_LIVESTREAM_CAPTIONS_URL = "TODO";
    public static final String PRIMARY_LIVESTREAM_TRACK = "android";
    public static final String SECONDARY_LIVESTREAM_TRACK = "chrome";

    // Social network integration
    public static final String HASHTAG = "?q=javazone";
    public static final String TWITTER_SEARCH_URL = "http://search.twitter.com/search.json";

} public class Constants {
    public static final String LIGHTNINGTALK = "lightning-talk";
    public static final String WORKSHOP = "workshop";

    // session feedback key for passing data to intent
    public static final String SESSION_FEEDBACK_TITLE = "SESSION_FEEDBACK_TITLE";
    public static final String SESSION_FEEDBACK_SUBTITLE = "SESSION_FEEDBACK_SUBTITLE";
    public static final String SESSION_ID = "SESSION_ID";

    public static final String SESSION_FEEDBACK_WEB_URI_TEST = "http://test.javazone.no/devnull/server";
    public static final String SESSION_FEEDBACK_WEB_URI = "http://javazone.no/devnull/server";

} public class ScheduleContract {

    /**
     * Special value for {@link SyncColumns#UPDATED} indicating that an entry
     * has never been updated, or doesn't exist yet.
     */
    public static final long UPDATED_NEVER = -2;

    /**
     * Special value for {@link SyncColumns#UPDATED} indicating that the last
     * update time is unknown, usually when inserted from a local file source.
     */
    public static final long UPDATED_UNKNOWN = -1;

    public interface SyncColumns {
        /** Last time this entry was updated or synchronized. */
        String UPDATED = "updated";
    }

    interface BlocksColumns {
        /** Unique string identifying this block of time. */
        String BLOCK_ID = "block_id";
        /** Title describing this block of time. */
        String BLOCK_TITLE = "block_title";
        /** Time when this block starts. */
        String BLOCK_START = "block_start";
        /** Time when this block ends. */
        String BLOCK_END = "block_end";
        /** Type describing this block. */
        String BLOCK_TYPE = "block_type";
        /** Extra string metadata for the block. */
        String BLOCK_META = "block_meta";
    }

    interface TracksColumns {
        /** Unique string identifying this track. */
        String TRACK_ID = "track_id";
        /** Name describing this track. */
        String TRACK_NAME = "track_name";
        /** Color used to identify this track, in {@link Color#argb} format. */
        String TRACK_COLOR = "track_color";
        /** Body of text explaining this track in detail. */
        String TRACK_ABSTRACT = "track_abstract";
    }

    interface RoomsColumns {
        /** Unique string identifying this room. */
        String ROOM_ID = "room_id";
        /** Name describing this room. */
        String ROOM_NAME = "room_name";
        /** Building floor this room exists on. */
        String ROOM_FLOOR = "room_floor";
    }

    interface SessionsColumns {
        /** Unique string identifying this session. */
        String SESSION_ID = "session_id";
        /** The type of session (session, keynote, codelab, etc). */
        String SESSION_TYPE = "session_type";
        /** Difficulty level of the session. */
        String SESSION_LEVEL = "session_level";
        /** Title describing this track. */
        String SESSION_TITLE = "session_title";
        /** Body of text explaining this session in detail. */
        String SESSION_ABSTRACT = "session_abstract";
        /** Requirements that attendees should meet. */
        String SESSION_REQUIREMENTS = "session_requirements";
        /** Keywords/tags for this session. */
        String SESSION_TAGS = "session_keywords";
        /** Hashtag for this session. */
        String SESSION_HASHTAGS = "session_hashtag";
        /** Full URL to session online. */
        String SESSION_URL = "session_url";
        /** Full URL to YouTube. */
        String SESSION_YOUTUBE_URL = "session_youtube_url";
        /** Full URL to PDF. */
        String SESSION_PDF_URL = "session_pdf_url";
        /** Full URL to official session notes. */
        String SESSION_NOTES_URL = "session_notes_url";
        /** User-specific flag indicating starred status. */
        String SESSION_STARRED = "session_starred";
        /** Key for session Calendar event. (Used in ICS or above) */
        String SESSION_CAL_EVENT_ID = "session_cal_event_id";
        /** The YouTube live stream URL. */
        String SESSION_LIVESTREAM_URL = "session_livestream_url";
        String SESSION_START = "session_start";
        String SESSION_END = "session_end";
    }

    interface SpeakersColumns {
        /** Unique string identifying this speaker. */
        String SPEAKER_ID = "speaker_id";
        /** Name of this speaker. */
        String SPEAKER_NAME = "speaker_name";
        /** Profile photo of this speaker. */
        String SPEAKER_IMAGE_URL = "speaker_image_url";
        /** Company this speaker works for. */
        String SPEAKER_COMPANY = "speaker_company";
        /** Body of text describing this speaker in detail. */
        String SPEAKER_ABSTRACT = "speaker_abstract";
        /** Full URL to the speaker's profile. */
        String SPEAKER_URL = "speaker_url";
    }

    interface VendorsColumns {
        /** Unique string identifying this vendor. */
        String VENDOR_ID = "vendor_id";
        /** Name of this vendor. */
        String VENDOR_NAME = "vendor_name";
        /** Location or city this vendor is based in. */
        String VENDOR_LOCATION = "vendor_location";
        /** Body of text describing this vendor. */
        String VENDOR_DESC = "vendor_desc";
        /** Link to vendor online. */
        String VENDOR_URL = "vendor_url";
        /** Body of text describing the product of this vendor. */
        String VENDOR_PRODUCT_DESC = "vendor_product_desc";
        /** Link to vendor logo. */
        String VENDOR_LOGO_URL = "vendor_logo_url";
        /** User-specific flag indicating starred status. */
        String VENDOR_STARRED = "vendor_starred";
    }

    interface AnnouncementsColumns {
        /** Unique string identifying this announcment. */
        String ANNOUNCEMENT_ID = "announcement_id";
        /** Title of the announcement. */
        String ANNOUNCEMENT_TITLE = "announcement_title";
        /** Summary of the announcement. */
        String ANNOUNCEMENT_SUMMARY = "announcement_summary";
        /** Track announcement belongs to. */
        String ANNOUNCEMENT_TRACKS = "announcement_tracks";
        /** Full URL for the announcement. */
        String ANNOUNCEMENT_URL = "announcement_url";
        /** Date of the announcement. */
        String ANNOUNCEMENT_DATE = "announcement_date";
    }

    public static final String CONTENT_AUTHORITY = "no.java.schedule";

    public static final Uri BASE_CONTENT_URI = Uri.parse("content://" + CONTENT_AUTHORITY);

    private static final String PATH_BLOCKS = "blocks";
    private static final String PATH_AT = "at";
    private static final String PATH_BETWEEN = "between";
    private static final String PATH_TRACKS = "tracks";
    private static final String PATH_ROOMS = "rooms";
    private static final String PATH_SESSIONS = "sessions";
    private static final String PATH_WITH_TRACK = "with_track";
    private static final String PATH_STARRED = "starred";
    private static final String PATH_SPEAKERS = "speakers";
    private static final String PATH_VENDORS = "vendors";
    private static final String PATH_ANNOUNCEMENTS = "announcements";
    private static final String PATH_SEARCH = "search";
    private static final String PATH_SEARCH_SUGGEST = "search_suggest_query";

    /**
     * Blocks are generic timeslots that {@link Sessions} and other related
     * events fall into.
     */
    public static class Blocks implements BlocksColumns, BaseColumns {
        public static final Uri CONTENT_URI =
                BASE_CONTENT_URI.buildUpon().appendPath(PATH_BLOCKS).build();

        public static final String CONTENT_TYPE =
                "vnd.android.cursor.dir/vnd.androidito-iosched.block";
        public static final String CONTENT_ITEM_TYPE =
                "vnd.android.cursor.item/vnd.androidito-iosched.block";

        /** Count of {@link Sessions} inside given block. */
        public static final String SESSIONS_COUNT = "sessions_count";

        /**
         * Flag indicating the number of sessions inside this block that have
         * {@link Sessions#SESSION_STARRED} set.
         */
        public static final String NUM_STARRED_SESSIONS = "num_starred_sessions";

        /**
         * The {@link Sessions#SESSION_ID} of the first starred session in this
         * block.
         */
        public static final String STARRED_SESSION_ID = "starred_session_id";

        /**
         * The {@link Sessions#SESSION_TITLE} of the first starred session in
         * this block.
         */
        public static final String STARRED_SESSION_TITLE = "starred_session_title";

        /**
         * The {@link Sessions#SESSION_LIVESTREAM_URL} of the first starred
         * session in this block.
         */
        public static final String STARRED_SESSION_LIVESTREAM_URL =
                "starred_session_livestream_url";

        /**
         * The {@link Rooms#ROOM_NAME} of the first starred session in this
         * block.
         */
        public static final String STARRED_SESSION_ROOM_NAME = "starred_session_room_name";

        /**
         * The {@link Rooms#ROOM_ID} of the first starred session in this block.
         */
        public static final String STARRED_SESSION_ROOM_ID = "starred_session_room_id";

        /**
         * The {@link Sessions#SESSION_HASHTAGS} of the first starred session in
         * this block.
         */
        public static final String STARRED_SESSION_HASHTAGS = "starred_session_hashtags";

        /**
         * The {@link Sessions#SESSION_URL} of the first starred session in this
         * block.
         */
        public static final String STARRED_SESSION_URL = "starred_session_url";

        /** Default "ORDER BY" clause. */
        public static final String DEFAULT_SORT = BlocksColumns.BLOCK_START + " ASC, "
                + BlocksColumns.BLOCK_END + " ASC";

        public static final String EMPTY_SESSIONS_SELECTION = "(" + BLOCK_TYPE
                + " = '" + ParserUtils.BLOCK_TYPE_SESSION + "' OR " + BLOCK_TYPE
                + " = '" + ParserUtils.BLOCK_TYPE_CODE_LAB + "') AND "
                + SESSIONS_COUNT + " = 0";

        /** Build {@link Uri} for requested {@link #BLOCK_ID}. */
        public static Uri buildBlockUri(String blockId) {
            return CONTENT_URI.buildUpon().appendPath(blockId).build();
        }

        /**
         * Build {@link Uri} that references any {@link Sessions} associated
         * with the requested {@link #BLOCK_ID}.
         */
        public static Uri buildSessionsUri(String blockId) {
            return CONTENT_URI.buildUpon().appendPath(blockId).appendPath(PATH_SESSIONS).build();
        }

        /**
         * Build {@link Uri} that references starred {@link Sessions} associated
         * with the requested {@link #BLOCK_ID}.
         */
        public static Uri buildStarredSessionsUri(String blockId) {
            return CONTENT_URI.buildUpon().appendPath(blockId).appendPath(PATH_SESSIONS)
                    .appendPath(PATH_STARRED).build();
        }

        /**
         * Build {@link Uri} that references any {@link Blocks} that occur
         * between the requested time boundaries.
         */
        public static Uri buildBlocksBetweenDirUri(long startTime, long endTime) {
            return CONTENT_URI.buildUpon().appendPath(PATH_BETWEEN).appendPath(
                    String.valueOf(startTime)).appendPath(String.valueOf(endTime)).build();
        }

        /** Read {@link #BLOCK_ID} from {@link Blocks} {@link Uri}. */
        public static String getBlockId(Uri uri) {
            return uri.getPathSegments().get(1);
        }

        /**
         * Generate a {@link #BLOCK_ID} that will always match the requested
         * {@link Blocks} details.
         */
        public static String generateBlockId(long startTime, long endTime) {
            startTime /= DateUtils.SECOND_IN_MILLIS;
            endTime /= DateUtils.SECOND_IN_MILLIS;
            return ParserUtils.sanitizeId(startTime + "-" + endTime);
        }
    }

    /**
     * Tracks are overall categories for {@link Sessions} and {@link Vendors},
     * such as "Android" or "Enterprise."
     */
    public static class Tracks implements TracksColumns, BaseColumns {
        public static final Uri CONTENT_URI =
                BASE_CONTENT_URI.buildUpon().appendPath(PATH_TRACKS).build();

        public static final String CONTENT_TYPE =
                "vnd.android.cursor.dir/vnd.androidito-iosched.track";
        public static final String CONTENT_ITEM_TYPE =
                "vnd.android.cursor.item/vnd.androidito-iosched.track";

        /** "All tracks" ID. */
        public static final String ALL_TRACK_ID = "all";
        public static final String CODELABS_TRACK_ID = generateTrackId("Code Labs");
        public static final String TECH_TALK_TRACK_ID = generateTrackId("Tech Talk");

        /** Count of {@link Sessions} inside given track. */
        public static final String SESSIONS_COUNT = "sessions_count";
        /** Count of {@link Vendors} inside given track. */
        public static final String VENDORS_COUNT = "vendors_count";

        /** Default "ORDER BY" clause. */
        public static final String DEFAULT_SORT = TracksColumns.TRACK_NAME + " ASC";

        /** Build {@link Uri} for requested {@link #TRACK_ID}. */
        public static Uri buildTrackUri(String trackId) {
            return CONTENT_URI.buildUpon().appendPath(trackId).build();
        }

        /**
         * Build {@link Uri} that references any {@link Sessions} associated
         * with the requested {@link #TRACK_ID}.
         */
        public static Uri buildSessionsUri(String trackId) {
            return CONTENT_URI.buildUpon().appendPath(trackId).appendPath(PATH_SESSIONS).build();
        }

        /**
         * Build {@link Uri} that references any {@link Vendors} associated with
         * the requested {@link #TRACK_ID}.
         */
        public static Uri buildVendorsUri(String trackId) {
            return CONTENT_URI.buildUpon().appendPath(trackId).appendPath(PATH_VENDORS).build();
        }

        /** Read {@link #TRACK_ID} from {@link Tracks} {@link Uri}. */
        public static String getTrackId(Uri uri) {
            return uri.getPathSegments().get(1);
        }

        /**
         * Generate a {@link #TRACK_ID} that will always match the requested
         * {@link Tracks} details.
         */
        public static String generateTrackId(String name) {
            return ParserUtils.sanitizeId(name);
        }
    }

    /**
     * Rooms are physical locations at the conference venue.
     */
    public static class Rooms implements RoomsColumns, BaseColumns {
        public static final Uri CONTENT_URI =
                BASE_CONTENT_URI.buildUpon().appendPath(PATH_ROOMS).build();

        public static final String CONTENT_TYPE =
                "vnd.android.cursor.dir/vnd.androidito-iosched.room";
        public static final String CONTENT_ITEM_TYPE =
                "vnd.android.cursor.item/vnd.androidito-iosched.room";

        /** Default "ORDER BY" clause. */
        public static final String DEFAULT_SORT = RoomsColumns.ROOM_FLOOR + " ASC, "
                + RoomsColumns.ROOM_NAME + " COLLATE NOCASE ASC";

        /** Build {@link Uri} for requested {@link #ROOM_ID}. */
        public static Uri buildRoomUri(String roomId) {
            return CONTENT_URI.buildUpon().appendPath(roomId).build();
        }

        /**
         * Build {@link Uri} that references any {@link Sessions} associated
         * with the requested {@link #ROOM_ID}.
         */
        public static Uri buildSessionsDirUri(String roomId) {
            return CONTENT_URI.buildUpon().appendPath(roomId).appendPath(PATH_SESSIONS).build();
        }

        /** Read {@link #ROOM_ID} from {@link Rooms} {@link Uri}. */
        public static String getRoomId(Uri uri) {
            return uri.getPathSegments().get(1);
        }
    }

    /**
     * Each session is a block of time that has a {@link Tracks}, a
     * {@link Rooms}, and zero or more {@link Speakers}.
     */
    public static class Sessions implements SessionsColumns, BlocksColumns, RoomsColumns,
            SyncColumns, BaseColumns {
        public static final Uri CONTENT_URI =
                BASE_CONTENT_URI.buildUpon().appendPath(PATH_SESSIONS).build();
        public static final Uri CONTENT_STARRED_URI =
                CONTENT_URI.buildUpon().appendPath(PATH_STARRED).build();

        public static final String CONTENT_TYPE =
                "vnd.android.cursor.dir/vnd.androidito-iosched.session";
        public static final String CONTENT_ITEM_TYPE =
                "vnd.android.cursor.item/vnd.androidito-iosched.session";

        public static final String BLOCK_ID = "block_id";
        public static final String ROOM_ID = "room_id";
        public static final String START = "session_start";
        public static final String END = "session_end";


        public static final String SEARCH_SNIPPET = "search_snippet";

        // TODO: shortcut primary track to offer sub-sorting here
        /** Default "ORDER BY" clause. */
        public static final String DEFAULT_SORT = BlocksColumns.BLOCK_START + " ASC,"
                + SessionsColumns.SESSION_TITLE + " COLLATE NOCASE ASC";

        public static final String BLOCK_SESSION_SORT =
                              ROOM_NAME + " ASC,"+
                              SessionsColumns.SESSION_START + " ASC";

        public static final String LIVESTREAM_SELECTION =
                SESSION_LIVESTREAM_URL + " is not null AND " + SESSION_LIVESTREAM_URL + "!=''";

        // Used to fetch sessions for a particular time
        public static final String AT_TIME_SELECTION =
                BLOCK_START + " < ? and " + BLOCK_END + " " + "> ?";

        // Builds selectionArgs for {@link AT_TIME_SELECTION}
        public static String[] buildAtTimeSelectionArgs(long time) {
            final String timeString = String.valueOf(time);
            return new String[] { timeString, timeString };
        }

        // Used to fetch upcoming sessions
        public static final String UPCOMING_SELECTION =
                BLOCK_START + " = (select min(" + BLOCK_START + ") from " +
                ScheduleDatabase.Tables.BLOCKS_JOIN_SESSIONS + " where " + LIVESTREAM_SELECTION +
                " and " + BLOCK_START + " >" + " ?)";

        // Builds selectionArgs for {@link UPCOMING_SELECTION}
        public static String[] buildUpcomingSelectionArgs(long minTime) {
            return new String[] { String.valueOf(minTime) };
        }

        /** Build {@link Uri} for requested {@link #SESSION_ID}. */
        public static Uri buildSessionUri(String sessionId) {
            return CONTENT_URI.buildUpon().appendPath(sessionId).build();
        }

        /**
         * Build {@link Uri} that references any {@link Speakers} associated
         * with the requested {@link #SESSION_ID}.
         */
        public static Uri buildSpeakersDirUri(String sessionId) {
            return CONTENT_URI.buildUpon().appendPath(sessionId).appendPath(PATH_SPEAKERS).build();
        }

        /**
         * Build {@link Uri} that includes track detail with list of sessions.
         */
        public static Uri buildWithTracksUri() {
            return CONTENT_URI.buildUpon().appendPath(PATH_WITH_TRACK).build();
        }

        /**
         * Build {@link Uri} that includes track detail for a specific session.
         */
        public static Uri buildWithTracksUri(String sessionId) {
            return CONTENT_URI.buildUpon().appendPath(sessionId)
                    .appendPath(PATH_WITH_TRACK).build();
        }

        /**
         * Build {@link Uri} that references any {@link Tracks} associated with
         * the requested {@link #SESSION_ID}.
         */
        public static Uri buildTracksDirUri(String sessionId) {
            return CONTENT_URI.buildUpon().appendPath(sessionId).appendPath(PATH_TRACKS).build();
        }

        public static Uri buildSessionsAtDirUri(long time) {
            return CONTENT_URI.buildUpon().appendPath(PATH_AT).appendPath(String.valueOf(time))
                    .build();
        }

        public static Uri buildSearchUri(String query) {
            return CONTENT_URI.buildUpon().appendPath(PATH_SEARCH).appendPath(query).build();
        }

        public static boolean isSearchUri(Uri uri) {
            List<String> pathSegments = uri.getPathSegments();
            return pathSegments.size() >= 2 && PATH_SEARCH.equals(pathSegments.get(1));
        }

        /** Read {@link #SESSION_ID} from {@link Sessions} {@link Uri}. */
        public static String getSessionId(Uri uri) {
            return uri.getPathSegments().get(1);
        }

        public static String getSearchQuery(Uri uri) {
            return uri.getPathSegments().get(2);
        }
    }

    /**
     * Speakers are individual people that lead {@link Sessions}.
     */
    public static class Speakers implements SpeakersColumns, SyncColumns, BaseColumns {
        public static final Uri CONTENT_URI =
                BASE_CONTENT_URI.buildUpon().appendPath(PATH_SPEAKERS).build();

        public static final String CONTENT_TYPE =
                "vnd.android.cursor.dir/vnd.androidito-iosched.speaker";
        public static final String CONTENT_ITEM_TYPE =
                "vnd.android.cursor.item/vnd.androidito-iosched.speaker";

        /** Default "ORDER BY" clause. */
        public static final String DEFAULT_SORT = SpeakersColumns.SPEAKER_NAME
                + " COLLATE NOCASE ASC";

        /** Build {@link Uri} for requested {@link #SPEAKER_ID}. */
        public static Uri buildSpeakerUri(String speakerId) {
            return CONTENT_URI.buildUpon().appendPath(speakerId).build();
        }

        /**
         * Build {@link Uri} that references any {@link Sessions} associated
         * with the requested {@link #SPEAKER_ID}.
         */
        public static Uri buildSessionsDirUri(String speakerId) {
            return CONTENT_URI.buildUpon().appendPath(speakerId).appendPath(PATH_SESSIONS).build();
        }

        /** Read {@link #SPEAKER_ID} from {@link Speakers} {@link Uri}. */
        public static String getSpeakerId(Uri uri) {
            return uri.getPathSegments().get(1);
        }
    }

    /**
     * Each vendor is a company appearing at the conference that may be
     * associated with a specific {@link Tracks}.
     */
    public static class Vendors implements VendorsColumns, SyncColumns, BaseColumns {
        public static final Uri CONTENT_URI =
                BASE_CONTENT_URI.buildUpon().appendPath(PATH_VENDORS).build();

        public static final String CONTENT_TYPE =
                "vnd.android.cursor.dir/vnd.androidito-iosched.vendor";
        public static final String CONTENT_ITEM_TYPE =
                "vnd.android.cursor.item/vnd.androidito-iosched.vendor";

        /** {@link Tracks#TRACK_ID} that this vendor belongs to. */
        public static final String TRACK_ID = "track_id";

        public static final String SEARCH_SNIPPET = "search_snippet";

        /** Default "ORDER BY" clause. */
        public static final String DEFAULT_SORT = VendorsColumns.VENDOR_NAME
                + " COLLATE NOCASE ASC";

        /** Build {@link Uri} for requested {@link #VENDOR_ID}. */
        public static Uri buildVendorUri(String vendorId) {
            return CONTENT_URI.buildUpon().appendPath(vendorId).build();
        }

        public static Uri buildSearchUri(String query) {
            return CONTENT_URI.buildUpon().appendPath(PATH_SEARCH).appendPath(query).build();
        }

        public static boolean isSearchUri(Uri uri) {
            List<String> pathSegments = uri.getPathSegments();
            return pathSegments.size() >= 2 && PATH_SEARCH.equals(pathSegments.get(1));
        }

        /** Read {@link #VENDOR_ID} from {@link Vendors} {@link Uri}. */
        public static String getVendorId(Uri uri) {
            return uri.getPathSegments().get(1);
        }

        public static String getSearchQuery(Uri uri) {
            return uri.getPathSegments().get(2);
        }

        /**
         * Generate a {@link #VENDOR_ID} that will always match the requested
         * {@link Vendors} details.
         */
        public static String generateVendorId(String companyName) {
            return ParserUtils.sanitizeId(companyName);
        }
    }

    /**
     * Announcements of breaking news
     */
    public static class Announcements implements AnnouncementsColumns, BaseColumns {
        public static final Uri CONTENT_URI =
                BASE_CONTENT_URI.buildUpon().appendPath(PATH_ANNOUNCEMENTS).build();

        public static final String CONTENT_TYPE =
                "vnd.android.cursor.dir/vnd.androidito-iosched.announcement";
        public static final String CONTENT_ITEM_TYPE =
                "vnd.android.cursor.item/vnd.androidito-iosched.announcement";

        /** Default "ORDER BY" clause. */
        public static final String DEFAULT_SORT = AnnouncementsColumns.ANNOUNCEMENT_DATE
                + " COLLATE NOCASE ASC";

        /** Build {@link Uri} for requested {@link #ANNOUNCEMENT_ID}. */
        public static Uri buildAnnouncementUri(String announcementId) {
            return CONTENT_URI.buildUpon().appendPath(announcementId).build();
        }

        /**
         * Build {@link Uri} that references any {@link Announcements}
         * associated with the requested {@link #ANNOUNCEMENT_ID}.
         */
        public static Uri buildAnnouncementsDirUri(String announcementId) {
            return CONTENT_URI.buildUpon().appendPath(announcementId)
                    .appendPath(PATH_ANNOUNCEMENTS).build();
        }

        /**
         * Read {@link #ANNOUNCEMENT_ID} from {@link Announcements} {@link Uri}.
         */
        public static String getAnnouncementId(Uri uri) {
            return uri.getPathSegments().get(1);
        }
    }

    public static class SearchSuggest {
        public static final Uri CONTENT_URI =
                BASE_CONTENT_URI.buildUpon().appendPath(PATH_SEARCH_SUGGEST).build();

        public static final String DEFAULT_SORT = SearchManager.SUGGEST_COLUMN_TEXT_1
                + " COLLATE NOCASE ASC";
    }

    public static Uri addCallerIsSyncAdapterParameter(Uri uri) {
        return uri.buildUpon().appendQueryParameter(
                ContactsContract.CALLER_IS_SYNCADAPTER, "true").build();
    }

    public static boolean hasCallerIsSyncAdapterParameter(Uri uri) {
        return TextUtils.equals("true",
                uri.getQueryParameter(ContactsContract.CALLER_IS_SYNCADAPTER));
    }

    private ScheduleContract() {
    }
} public static class Sessions implements SessionsColumns, BlocksColumns, RoomsColumns,
        SyncColumns, BaseColumns {
    public static final Uri CONTENT_URI =
            BASE_CONTENT_URI.buildUpon().appendPath(PATH_SESSIONS).build();
    public static final Uri CONTENT_STARRED_URI =
            CONTENT_URI.buildUpon().appendPath(PATH_STARRED).build();

    public static final String CONTENT_TYPE =
            "vnd.android.cursor.dir/vnd.androidito-iosched.session";
    public static final String CONTENT_ITEM_TYPE =
            "vnd.android.cursor.item/vnd.androidito-iosched.session";

    public static final String BLOCK_ID = "block_id";
    public static final String ROOM_ID = "room_id";
    public static final String START = "session_start";
    public static final String END = "session_end";


    public static final String SEARCH_SNIPPET = "search_snippet";

    // TODO: shortcut primary track to offer sub-sorting here
    /** Default "ORDER BY" clause. */
    public static final String DEFAULT_SORT = BlocksColumns.BLOCK_START + " ASC,"
            + SessionsColumns.SESSION_TITLE + " COLLATE NOCASE ASC";

    public static final String BLOCK_SESSION_SORT =
                          ROOM_NAME + " ASC,"+
                          SessionsColumns.SESSION_START + " ASC";

    public static final String LIVESTREAM_SELECTION =
            SESSION_LIVESTREAM_URL + " is not null AND " + SESSION_LIVESTREAM_URL + "!=''";

    // Used to fetch sessions for a particular time
    public static final String AT_TIME_SELECTION =
            BLOCK_START + " < ? and " + BLOCK_END + " " + "> ?";

    // Builds selectionArgs for {@link AT_TIME_SELECTION}
    public static String[] buildAtTimeSelectionArgs(long time) {
        final String timeString = String.valueOf(time);
        return new String[] { timeString, timeString };
    }

    // Used to fetch upcoming sessions
    public static final String UPCOMING_SELECTION =
            BLOCK_START + " = (select min(" + BLOCK_START + ") from " +
            ScheduleDatabase.Tables.BLOCKS_JOIN_SESSIONS + " where " + LIVESTREAM_SELECTION +
            " and " + BLOCK_START + " >" + " ?)";

    // Builds selectionArgs for {@link UPCOMING_SELECTION}
    public static String[] buildUpcomingSelectionArgs(long minTime) {
        return new String[] { String.valueOf(minTime) };
    }

    /** Build {@link Uri} for requested {@link #SESSION_ID}. */
    public static Uri buildSessionUri(String sessionId) {
        return CONTENT_URI.buildUpon().appendPath(sessionId).build();
    }

    /**
     * Build {@link Uri} that references any {@link Speakers} associated
     * with the requested {@link #SESSION_ID}.
     */
    public static Uri buildSpeakersDirUri(String sessionId) {
        return CONTENT_URI.buildUpon().appendPath(sessionId).appendPath(PATH_SPEAKERS).build();
    }

    /**
     * Build {@link Uri} that includes track detail with list of sessions.
     */
    public static Uri buildWithTracksUri() {
        return CONTENT_URI.buildUpon().appendPath(PATH_WITH_TRACK).build();
    }

    /**
     * Build {@link Uri} that includes track detail for a specific session.
     */
    public static Uri buildWithTracksUri(String sessionId) {
        return CONTENT_URI.buildUpon().appendPath(sessionId)
                .appendPath(PATH_WITH_TRACK).build();
    }

    /**
     * Build {@link Uri} that references any {@link Tracks} associated with
     * the requested {@link #SESSION_ID}.
     */
    public static Uri buildTracksDirUri(String sessionId) {
        return CONTENT_URI.buildUpon().appendPath(sessionId).appendPath(PATH_TRACKS).build();
    }

    public static Uri buildSessionsAtDirUri(long time) {
        return CONTENT_URI.buildUpon().appendPath(PATH_AT).appendPath(String.valueOf(time))
                .build();
    }

    public static Uri buildSearchUri(String query) {
        return CONTENT_URI.buildUpon().appendPath(PATH_SEARCH).appendPath(query).build();
    }

    public static boolean isSearchUri(Uri uri) {
        List<String> pathSegments = uri.getPathSegments();
        return pathSegments.size() >= 2 && PATH_SEARCH.equals(pathSegments.get(1));
    }

    /** Read {@link #SESSION_ID} from {@link Sessions} {@link Uri}. */
    public static String getSessionId(Uri uri) {
        return uri.getPathSegments().get(1);
    }

    public static String getSearchQuery(Uri uri) {
        return uri.getPathSegments().get(2);
    }
} public interface SyncColumns {
    /** Last time this entry was updated or synchronized. */
    String UPDATED = "updated";
} public class SyncHelper {

    private static final String TAG = makeLogTag(SyncHelper.class);

    static {
        // Per http://android-developers.blogspot.com/2011/09/androids-http-clients.html
        if (!UIUtils.hasFroyo()) {
            System.setProperty("http.keepAlive", "false");
        }
    }

    public static final int FLAG_SYNC_LOCAL = 0x1;
    public static final int FLAG_SYNC_REMOTE = 0x2;

    private static final int LOCAL_VERSION_CURRENT = 19;

    private Context mContext;
    private String mUserAgent;

    public SyncHelper(Context context) {
        mContext = context;
        mUserAgent = buildUserAgent(context);
    }

    /**
     * Loads conference information (sessions, rooms, tracks, speakers, etc.)
     * from a local static cache data and then syncs down data from the
     * Conference API.
     * 
     * @param syncResult Optional {@link SyncResult} object to populate.
     * @throws IOException
     */
    public void performSync(SyncResult syncResult) throws IOException {


        // Bulk of sync work, performed by executing several fetches from
        // local and online sources.
        final ContentResolver resolver = mContext.getContentResolver();
        ArrayList<ContentProviderOperation> batch = new ArrayList<ContentProviderOperation>();

        LOGI(TAG, "Performing sync");


      try {
        final long startRemote = System.currentTimeMillis();
        LOGI(TAG, "Syncing rooms");
        batch.addAll(fetchResource(Config.EMS_ROOMS, new  RoomsHandler(mContext)));
        LOGI(TAG, "Syncing sessions");
        batch.addAll(fetchResource( Config.GET_ALL_SESSIONS_URL, new SessionsHandler(mContext, false, false)));
        LOGI(TAG, "Syncing tracks");
        batch.addAll(fetchResource( Config.GET_ALL_SESSIONS_URL, new TracksHandler(mContext)));

        //TODO Enable announcements for JavaZone
        //LOGI(TAG, "Remote syncing announcements");
        //batch.addAll(executeGet(Config.GET_ALL_ANNOUNCEMENTS_URL,
        //        new AnnouncementsHandler(mContext, false), auth));

        LOGD(TAG, "Remote sync took " + (System.currentTimeMillis() - startRemote) + "ms");
        if (syncResult != null) {
          ++syncResult.stats.numUpdates;
          ++syncResult.stats.numEntries;
        }

        EasyTracker.getTracker().dispatch();

      } catch (HandlerException.UnauthorizedException e) {
        LOGI(TAG, "Unauthorized; getting a new auth token.", e);
        if (syncResult != null) {
          ++syncResult.stats.numAuthExceptions;
        }

      }
      // all other IOExceptions are thrown


      try {
            // Apply all queued up remaining batch operations (only remote content at this point).
            resolver.applyBatch(ScheduleContract.CONTENT_AUTHORITY, batch);

            // Delete empty blocks
            Cursor emptyBlocksCursor = resolver.query(ScheduleContract.Blocks.CONTENT_URI,
                    new String[]{ScheduleContract.Blocks.BLOCK_ID,ScheduleContract.Blocks.SESSIONS_COUNT},
                    ScheduleContract.Blocks.EMPTY_SESSIONS_SELECTION, null, null);
            batch = new ArrayList<ContentProviderOperation>();
            int numDeletedEmptyBlocks = 0;
            while (emptyBlocksCursor.moveToNext()) {
                batch.add(ContentProviderOperation
                        .newDelete(ScheduleContract.Blocks.buildBlockUri(
                                emptyBlocksCursor.getString(0)))
                        .build());
                ++numDeletedEmptyBlocks;
            }
            emptyBlocksCursor.close();
            resolver.applyBatch(ScheduleContract.CONTENT_AUTHORITY, batch);
            LOGD(TAG, "Deleted " + numDeletedEmptyBlocks + " empty session blocks.");
        } catch (RemoteException e) {
            throw new RuntimeException("Problem applying batch operation", e);
        } catch (OperationApplicationException e) {
            throw new RuntimeException("Problem applying batch operation", e);
        }

        if (UIUtils.hasICS()) {
            LOGD(TAG, "Done with sync'ing conference data. Starting to sync "
                    + "session with Calendar.");
            syncCalendar();
        }
    }

    private void syncCalendar() {
        //Intent intent = new Intent(SessionCalendarService.ACTION_UPDATE_ALL_SESSIONS_CALENDAR);
        //intent.setClass(mContext, SessionCalendarService.class);
        //mContext.startService(intent);
    }

    /**
     * Build and return a user-agent string that can identify this application
     * to remote servers. Contains the package name and version code.
     */
    private static String buildUserAgent(Context context) {
        String versionName = "unknown";
        int versionCode = 0;

        try {
            final PackageInfo info = context.getPackageManager()
                    .getPackageInfo(context.getPackageName(), 0);
            versionName = info.versionName;
            versionCode = info.versionCode;
        } catch (PackageManager.NameNotFoundException ignored) {
        }

        return context.getPackageName() + "/" + versionName + " (" + versionCode + ") (gzip)";
    }

    public void addOrRemoveSessionFromSchedule(Context context, String sessionId,
            boolean inSchedule) throws IOException {
      //TODO sync at EMS server
      /**
        mAuthToken = AccountUtils.getAuthToken(mContext);
        JsonObject starredSession = new JsonObject();
        starredSession.addProperty("sessionid", sessionId);

        byte[] postJsonBytes = new Gson().toJson(starredSession).getBytes();

        URL url = new URL(Config.EDIT_MY_SCHEDULE_URL + (inSchedule ? "add" : "remove"));
        HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();
        urlConnection.setRequestProperty("User-Agent", mUserAgent);
        urlConnection.setRequestProperty("Content-Type", "application/json");
        urlConnection.setRequestProperty("Authorization", "Bearer " + mAuthToken);
        urlConnection.setDoOutput(true);
        urlConnection.setFixedLengthStreamingMode(postJsonBytes.length);

        LOGD(TAG, "Posting to URL: " + url);
        OutputStream out = new BufferedOutputStream(urlConnection.getOutputStream());
        out.write(postJsonBytes);
        out.flush();

        urlConnection.connect();
        throwErrors(urlConnection);
        String json = readInputStream(urlConnection.getInputStream());
        EditMyScheduleResponse response = new Gson().fromJson(json,
                EditMyScheduleResponse.class);
        if (!response.success) {
            String responseMessageLower = (response.message != null)
                    ? response.message.toLowerCase()
                    : "";

            if (responseMessageLower.contains("no profile")) {
                throw new HandlerException.NoDevsiteProfileException();
            }
        }
       **/
    }

    public ArrayList<ContentProviderOperation> fetchResource( String urlString, JSONHandler handler) throws IOException {

      String response = null;

      if (isFirstRun()) {
         response = getLocalResource(mContext, urlString);
      } else if (isOnline(mContext)) {
          response = getHttpResource(urlString);
      }

      if (response!=null && !response.trim().equals("")){
        return handler.parse(response);
      } else {
        return new ArrayList<ContentProviderOperation>();
      }
    }


  private boolean isFirstRun(){
    return isFirstRun(mContext);
  }

  public static boolean isFirstRun(Context context) {
    final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
    return prefs.getBoolean("first_run", true);
  }

  public static String getLocalResource(Context pContext,String pUrlString) {

    pUrlString = pUrlString.replaceFirst("http://", "");

    //fix file/directory clashes....
    if (pUrlString.endsWith("/sessions")) {
      pUrlString=pUrlString+".json";
    }

    LOGD("LocalResourceUrls", pUrlString);

    try {
      InputStream asset = pContext.getAssets().open(pUrlString);


      LOGD("LocalResourceUrls Found",pUrlString);
      return convertStreamToString(asset);

    }
    catch (IOException e) {
      LOGE("LocalResourceUrls NotFound",pUrlString);
      LOGE(makeLogTag(SyncHelper.class),"Exception reading asset",e);
      return null;
    }

  }

  static String convertStreamToString(java.io.InputStream is) {
      Scanner s = new java.util.Scanner(is).useDelimiter("\\A");
      return s.hasNext() ? s.next() : "";
  }

  public static String getHttpResource(final String urlString) throws IOException {
    LOGD(TAG, "Requesting URL: " + urlString);
    URL url = new URL(urlString);
    HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();
    urlConnection.setRequestProperty("User-Agent", "Androidito JZ15");

    urlConnection.setRequestProperty("Accept","application/json");

    urlConnection.connect();
    throwErrors(urlConnection);

    String response = readInputStream(urlConnection.getInputStream());
    LOGV(TAG, "HTTP response: " + response);
    return response;
  }

  private static void throwErrors(HttpURLConnection urlConnection) throws IOException {
        final int status = urlConnection.getResponseCode();
        if (status < 200 || status >= 300) {
            String errorMessage = null;
            try {
                String errorContent = readInputStream(urlConnection.getErrorStream());
                LOGV(TAG, "Error content: " + errorContent);
                ErrorResponse errorResponse = new Gson().fromJson(
                        errorContent, ErrorResponse.class);
                errorMessage = errorResponse.error.message;
            } catch (IOException ignored) {
            } catch (JsonSyntaxException ignored) {
            }

            String exceptionMessage = "Error response "
                    + status + " "
                    + urlConnection.getResponseMessage()
                    + (errorMessage == null ? "" : (": " + errorMessage))
                    + " for " + urlConnection.getURL();

            // TODO: the API should return 401, and we shouldn't have to parse the message
            throw (errorMessage != null && errorMessage.toLowerCase().contains("auth"))
                    ? new HandlerException.UnauthorizedException(exceptionMessage)
                    : new HandlerException(exceptionMessage);
        }
    }

    public static String readInputStream(InputStream inputStream)
            throws IOException {
        BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
        String responseLine;
        StringBuilder responseBuilder = new StringBuilder();
        while ((responseLine = bufferedReader.readLine()) != null) {
            responseBuilder.append(responseLine);
        }
        return responseBuilder.toString();
    }

    public static boolean isOnline(Context mContext) {
        ConnectivityManager cm = (ConnectivityManager) mContext.getSystemService(
                Context.CONNECTIVITY_SERVICE);

        return cm.getActiveNetworkInfo() != null &&
                cm.getActiveNetworkInfo().isConnectedOrConnecting();
    }
} public class ParserUtils {
    public static final String BLOCK_TYPE_SESSION = "session";
    public static final String BLOCK_TYPE_CODE_LAB = "codelab";
    public static final String BLOCK_TYPE_KEYNOTE = "keynote";
    public static final String BLOCK_TYPE_WORKSHOP = "workshop";

    /** Used to sanitize a string to be {@link Uri} safe. */
    private static final Pattern sSanitizePattern = Pattern.compile("[^a-z0-9-_]");
    private static final Time sTime = new Time();

    /**
     * Sanitize the given string to be {@link Uri} safe for building
     * {@link ContentProvider} paths.
     */
    public static String sanitizeId(String input) {
        if (input == null) {
            return null;
        }
        return sSanitizePattern.matcher(input.toLowerCase()).replaceAll("");
    }

    /**
     * Parse the given string as a RFC 3339 timestamp, returning the value as
     * milliseconds since the epoch.
     */
    public static long parseTime(String time) {
        sTime.parse3339(time);
        return sTime.toMillis(false);
    }
} public interface SessionsSpeakers {
    String SESSION_ID = "session_id";
    String SPEAKER_ID = "speaker_id";
} public interface SessionsTracks {
    String SESSION_ID = "session_id";
    String TRACK_ID = "track_id";
} public class LogUtils {
    private static final String LOG_PREFIX = "iosched_";
    private static final int LOG_PREFIX_LENGTH = LOG_PREFIX.length();
    private static final int MAX_LOG_TAG_LENGTH = 23;

    public static String makeLogTag(String str) {
        if (str.length() > MAX_LOG_TAG_LENGTH - LOG_PREFIX_LENGTH) {
            return LOG_PREFIX + str.substring(0, MAX_LOG_TAG_LENGTH - LOG_PREFIX_LENGTH - 1);
        }

        return LOG_PREFIX + str;
    }

    /**
     * WARNING: Don't use this when obfuscating class names with Proguard!
     */
    public static String makeLogTag(Class cls) {
        return makeLogTag(cls.getSimpleName());
    }

    public static void LOGD(final String tag, String message) {
        if (Log.isLoggable(tag, Log.DEBUG)) {
            Log.d(tag, message);
        }
    }

    public static void LOGD(final String tag, String message, Throwable cause) {
        if (Log.isLoggable(tag, Log.DEBUG)) {
            Log.d(tag, message, cause);
        }
    }

    public static void LOGV(final String tag, String message) {
        //noinspection PointlessBooleanExpression,ConstantConditions
        if (BuildConfig.DEBUG && Log.isLoggable(tag, Log.VERBOSE)) {
            Log.v(tag, message);
        }
    }

    public static void LOGV(final String tag, String message, Throwable cause) {
        //noinspection PointlessBooleanExpression,ConstantConditions
        if (BuildConfig.DEBUG && Log.isLoggable(tag, Log.VERBOSE)) {
            Log.v(tag, message, cause);
        }
    }

    public static void LOGI(final String tag, String message) {
        Log.i(tag, message);
    }

    public static void LOGI(final String tag, String message, Throwable cause) {
        Log.i(tag, message, cause);
    }

    public static void LOGW(final String tag, String message) {
        Log.w(tag, message);
    }

    public static void LOGW(final String tag, String message, Throwable cause) {
        Log.w(tag, message, cause);
    }

    public static void LOGE(final String tag, String message) {
        Log.e(tag, message);
    }

    public static void LOGE(final String tag, String message, Throwable cause) {
        Log.e(tag, message, cause);
    }

    private LogUtils() {
    }
} public static String sanitizeId(String input) {
    if (input == null) {
        return null;
    }
    return sSanitizePattern.matcher(input.toLowerCase()).replaceAll("");
}
import android.content.ContentProviderOperation;
import android.content.Context;
import android.database.Cursor;
import android.net.Uri;
import android.text.TextUtils;
import android.text.format.Time;
import com.google.android.apps.iosched.Config;
import com.google.android.apps.iosched.io.model.Constants;
import com.google.android.apps.iosched.provider.ScheduleContract;
import com.google.android.apps.iosched.provider.ScheduleContract.Sessions;
import com.google.android.apps.iosched.provider.ScheduleContract.SyncColumns;
import com.google.android.apps.iosched.sync.SyncHelper;
import com.google.android.apps.iosched.util.Lists;
import com.google.android.apps.iosched.util.ParserUtils;
import com.google.gson.Gson;
import no.java.schedule.R;
import no.java.schedule.io.model.*;
import java.io.IOException;
import java.net.MalformedURLException;
import java.util.*;
import java.util.regex.Pattern;
import static com.google.android.apps.iosched.provider.ScheduleDatabase.SessionsSpeakers;
import static com.google.android.apps.iosched.provider.ScheduleDatabase.SessionsTracks;
import static com.google.android.apps.iosched.util.LogUtils.*;
import static com.google.android.apps.iosched.util.ParserUtils.sanitizeId;
/*
 * Copyright 2012 Google Inc.
 *
 * 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.google.android.apps.iosched.io;




/**
 * Handler that parses session JSON data into a list of content provider operations.
 */
public class SessionsHandler extends JSONHandler {

  private static final String TAG = makeLogTag(SessionsHandler.class);

  private static final String EVENT_TYPE_KEYNOTE = "keynote";
  private static final String EVENT_TYPE_CODELAB = "codelab";

  private static final int PARSE_FLAG_FORCE_SCHEDULE_REMOVE = 1;
  private static final int PARSE_FLAG_FORCE_SCHEDULE_ADD = 2;

  private static final Time sTime = new Time();

  private static final Pattern sRemoveSpeakerIdPrefixPattern = Pattern.compile(".*//");

  private boolean mLocal;
  private boolean mThrowIfNoAuthToken;
  private Map<String,EMSItem> mBlocks;
  private Map<String, EMSItem> mRooms;

  public SessionsHandler(Context context, boolean local, boolean throwIfNoAuthToken) {
    super(context);
    mLocal = local;
    mThrowIfNoAuthToken = throwIfNoAuthToken;
  }

  public ArrayList<ContentProviderOperation> parse(String json)
      throws IOException {
    final ArrayList<ContentProviderOperation> batch = Lists.newArrayList();

    JZSessionsResponse collectionResponse = new Gson().fromJson(json, JZSessionsResponse.class);


    LOGI(TAG, "Updating sessions data");

    // by default retain locally starred if local sync
    boolean retainLocallyStarredSessions = true; //mLocal;

    if (collectionResponse.error != null && collectionResponse.error.isJsonPrimitive()) {
      String errorMessageLower = collectionResponse.error.getAsString().toLowerCase();

      if (!mLocal && (errorMessageLower.contains("no profile")
          || errorMessageLower.contains("no auth token"))) {
        // There was some authentication issue; retain locally starred sessions.
        retainLocallyStarredSessions = true;
        LOGW(TAG, "The user has no developers.google.com profile or this call is "
            + "not authenticated. Retaining locally starred sessions.");
      }

      if (mThrowIfNoAuthToken && errorMessageLower.contains("no auth token")) {
        throw new HandlerException.UnauthorizedException("No auth token but we tried "
            + "authenticating. Need to invalidate the auth token.");
      }
    }

    Set<String> starredSessionIds = new HashSet<String>();
    if (retainLocallyStarredSessions) {
      // Collect the list of current starred sessions
      Cursor starredSessionsCursor = mContext.getContentResolver().query(
          Sessions.CONTENT_STARRED_URI,
          new String[]{Sessions.SESSION_ID},
          null, null, null);
      while (starredSessionsCursor.moveToNext()) {
        starredSessionIds.add(starredSessionsCursor.getString(0));
      }
      starredSessionsCursor.close();
    }

    // Clear out existing sessions
    batch.add(ContentProviderOperation
        .newDelete(ScheduleContract.addCallerIsSyncAdapterParameter(
            Sessions.CONTENT_URI))
        .build());

    // Maintain a list of created block IDs
    Set<String> blockIds = new HashSet<String>();

    List<JZSessionsResult> sessions = toJZSessionResultList(collectionResponse.collection.items);

    for (JZSessionsResult event : sessions) {

      int flags = 0;
      if (retainLocallyStarredSessions) {
        flags = (starredSessionIds.contains(event.id)
            ? PARSE_FLAG_FORCE_SCHEDULE_ADD
            : PARSE_FLAG_FORCE_SCHEDULE_REMOVE);
      }

      String sessionId = event.id;
      if (TextUtils.isEmpty(sessionId)) {
        LOGW(TAG, "Found session with empty ID in API response.");
        continue;
      }


      // Session title  - fix special titles
      String sessionTitle = event.title;

      // Whether or not it's in the schedule
      boolean inSchedule = "Y".equals(event.attending);
      if ((flags & PARSE_FLAG_FORCE_SCHEDULE_ADD) != 0
          || (flags & PARSE_FLAG_FORCE_SCHEDULE_REMOVE) != 0) {
        inSchedule = (flags & PARSE_FLAG_FORCE_SCHEDULE_ADD) != 0;
      }


      // Hashtags
      String hashtags = "";
      //if (event.labels != null) {
      //    StringBuilder hashtagsBuilder = new StringBuilder();
      //    for (JZLabel trackName : event.labels) {
      //
      //        hashtagsBuilder.append(" #");
      //        hashtagsBuilder.append(
      //                ScheduleContract.Tracks.generateTrackId(trackName));
      //    }
      //    hashtags = hashtagsBuilder.toString().trim();
      //}

      // Pre-reqs
      String prereqs = "";
      //if (event.prereq != null && event.prereq.length > 0) {
      //    StringBuilder sb = new StringBuilder();
      //    for (String prereq : event.prereq) {
      //        sb.append(prereq);
      //        sb.append(" ");
      //    }
      //    prereqs = sb.toString();
      //    if (prereqs.startsWith("<br>")) {
      //        prereqs = prereqs.substring(4);
      //    }
      //}

      String youtubeUrl = null;
      //if (event.youtube_url != null && event.youtube_url.length > 0) {
      //    youtubeUrl = event.youtube_url[0];
      //}

      populateStartEndTime(event);
      parseSpeakers(event,batch);
      //populateRoom(event);

      long sessionStartTime=0;
      long sessionEndTime=0;      //TODO handle sessions without timeslot

      long originalSessionEndTime=1;
      long originalSessionStartTime=1;


      if (event.start!=null && event.end!=null){
        originalSessionStartTime = event.start.millis();
        originalSessionEndTime = event.end.millis();

        sessionStartTime = event.start.millis();//parseTime(event.start_date, event.start_time);
        sessionEndTime = event.end.millis();//event.end_date, event.end_time);
      }

      if  (Constants.LIGHTNINGTALK.equals(event.format)){