/* * Copyright 2013 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.service; import android.content.Intent; import android.database.Cursor; import android.provider.BaseColumns; import android.text.TextUtils; import android.text.format.DateUtils; import com.google.android.apps.dashclock.api.ExtensionData; import com.google.android.apps.iosched.R; import com.google.android.apps.iosched.provider.ScheduleContract; import com.google.android.apps.iosched.ui.HomeActivity; import com.google.android.apps.iosched.util.PrefUtils; import com.google.android.apps.iosched.util.UIUtils; import java.util.ArrayList; import java.util.Formatter; import java.util.List; import java.util.Locale; import static com.google.android.apps.iosched.util.LogUtils.LOGD; import static com.google.android.apps.iosched.util.LogUtils.LOGE; import static com.google.android.apps.iosched.util.LogUtils.makeLogTag; /** * An I/O 2013 extension for DashClock. */ public class DashClockExtension extends com.google.android.apps.dashclock.api.DashClockExtension { private static final String TAG = makeLogTag(DashClockExtension.class); private static final long MINUTE_MILLIS = 60 * 1000; private static final long NOW_BUFFER_TIME_MILLIS = 15 * MINUTE_MILLIS; private static final int MAX_BLOCKS = 5; private static final long CONTENT_CHANGE_DELAY_MILLIS = 5 * 1000; private static long mLastChange = 0; @Override protected void onInitialize(boolean isReconnect) { super.onInitialize(isReconnect); setUpdateWhenScreenOn(true); addWatchContentUris(new String[]{ ScheduleContract.Sessions.CONTENT_URI.toString() }); } @Override protected void onUpdateData(int reason) { if (reason == DashClockExtension.UPDATE_REASON_CONTENT_CHANGED) { long time = System.currentTimeMillis(); if (time < mLastChange + CONTENT_CHANGE_DELAY_MILLIS) { return; } mLastChange = time; } long currentTime = UIUtils.getCurrentTime(this); if (currentTime >= UIUtils.CONFERENCE_END_MILLIS) { publishUpdate(new ExtensionData() .visible(true) .icon(R.drawable.dashclock_extension) .status(getString(R.string.whats_on_thank_you_short)) .expandedTitle(getString(R.string.whats_on_thank_you_title)) .expandedBody(getString(R.string.whats_on_thank_you_subtitle)) .clickIntent(new Intent(this, HomeActivity.class))); return; } Cursor cursor = tryOpenBlocksCursor(); if (cursor == null) { LOGE(TAG, "Null blocks cursor, short-circuiting."); return; } StringBuilder buffer = new StringBuilder(); Formatter formatter = new Formatter(buffer, Locale.getDefault()); String firstBlockStartTime = null; List<String> blocks = new ArrayList<String>(); long lastBlockStart = 0; while (cursor.moveToNext()) { if (blocks.size() >= MAX_BLOCKS) { break; } final String type = cursor.getString(BlocksQuery.BLOCK_TYPE); final String blockTitle = cursor.getString(BlocksQuery.BLOCK_TITLE); final String blockType = cursor.getString(BlocksQuery.BLOCK_TYPE); final long blockStart = cursor.getLong(BlocksQuery.BLOCK_START); buffer.setLength(0); boolean showWeekday = !DateUtils.isToday(blockStart) && !UIUtils.isSameDayDisplay(lastBlockStart, blockStart, this); String blockStartTime = DateUtils.formatDateRange(this, formatter, blockStart, blockStart, DateUtils.FORMAT_SHOW_TIME | (showWeekday ? DateUtils.FORMAT_SHOW_WEEKDAY | DateUtils.FORMAT_ABBREV_WEEKDAY : 0), PrefUtils.getDisplayTimeZone(this).getID()).toString(); lastBlockStart = blockStart; if (firstBlockStartTime == null) { firstBlockStartTime = blockStartTime; } if (ScheduleContract.Blocks.BLOCK_TYPE_SESSION.equals(type) || ScheduleContract.Blocks.BLOCK_TYPE_CODELAB.equals(type) || ScheduleContract.Blocks.BLOCK_TYPE_OFFICE_HOURS.equals(blockType)) { final int numStarredSessions = cursor.getInt(BlocksQuery.NUM_STARRED_SESSIONS); if (numStarredSessions == 1) { // exactly 1 session starred String title = cursor.getString(BlocksQuery.STARRED_SESSION_TITLE); String room = cursor.getString(BlocksQuery.STARRED_SESSION_ROOM_NAME); if (room == null) { room = getString(R.string.unknown_room); } blocks.add(blockStartTime + ", " + room + " — " + title); } else { // 2 or more sessions starred String title = getString(R.string.schedule_conflict_title, numStarredSessions); blocks.add(blockStartTime + " — " + title); } } else if (ScheduleContract.Blocks.BLOCK_TYPE_KEYNOTE.equals(type)) { final String title = cursor.getString(BlocksQuery.STARRED_SESSION_TITLE); blocks.add(blockStartTime + " — " + title); } else { blocks.add(blockStartTime + " — " + blockTitle); } } cursor.close(); LOGD(TAG, blocks.size() + " blocks"); if (blocks.size() > 0) { publishUpdate(new ExtensionData() .visible(true) .icon(R.drawable.dashclock_extension) .status(firstBlockStartTime) .expandedTitle(blocks.get(0)) .expandedBody(TextUtils.join("\n", blocks.subList(1, blocks.size()))) .clickIntent(new Intent(this, HomeActivity.class))); } else { publishUpdate(new ExtensionData().visible(false)); } } private Cursor tryOpenBlocksCursor() { try { String liveStreamedOnlyBlocksSelection = "(" + (UIUtils.shouldShowLiveSessionsOnly(this) ? ScheduleContract.Blocks.BLOCK_TYPE + " NOT IN ('" + ScheduleContract.Blocks.BLOCK_TYPE_SESSION + "','" + ScheduleContract.Blocks.BLOCK_TYPE_CODELAB + "','" + ScheduleContract.Blocks.BLOCK_TYPE_OFFICE_HOURS + "','" + ScheduleContract.Blocks.BLOCK_TYPE_FOOD + "')" + " OR " + ScheduleContract.Blocks.NUM_LIVESTREAMED_SESSIONS + ">1 " : "1==1") + ")"; String onlyStarredSelection = "(" + ScheduleContract.Blocks.BLOCK_TYPE + " NOT IN ('" + ScheduleContract.Blocks.BLOCK_TYPE_SESSION + "','" + ScheduleContract.Blocks.BLOCK_TYPE_CODELAB + "','" + ScheduleContract.Blocks.BLOCK_TYPE_OFFICE_HOURS + "') " + " OR " + ScheduleContract.Blocks.NUM_STARRED_SESSIONS + ">0)"; String excludeSandbox = ScheduleContract.Blocks.BLOCK_TYPE + " != '" + ScheduleContract.Blocks.BLOCK_TYPE_SANDBOX + "'"; return getContentResolver().query(ScheduleContract.Blocks.CONTENT_URI, BlocksQuery.PROJECTION, ScheduleContract.Blocks.BLOCK_START + " >= ? AND " + liveStreamedOnlyBlocksSelection + " AND " + onlyStarredSelection + " AND " + excludeSandbox, new String[]{ Long.toString(UIUtils.getCurrentTime(this) - NOW_BUFFER_TIME_MILLIS) }, ScheduleContract.Blocks.DEFAULT_SORT); } catch (Exception e) { LOGE(TAG, "Error querying I/O 2013 content provider", e); return null; } } public interface BlocksQuery { String[] PROJECTION = { BaseColumns._ID, ScheduleContract.Blocks.BLOCK_TITLE, ScheduleContract.Blocks.BLOCK_START, ScheduleContract.Blocks.BLOCK_TYPE, ScheduleContract.Blocks.NUM_STARRED_SESSIONS, ScheduleContract.Blocks.STARRED_SESSION_TITLE, ScheduleContract.Blocks.STARRED_SESSION_ROOM_NAME, ScheduleContract.Blocks.NUM_LIVESTREAMED_SESSIONS, }; int _ID = 0; int BLOCK_TITLE = 1; int BLOCK_START = 2; int BLOCK_TYPE = 3; int NUM_STARRED_SESSIONS = 4; int STARRED_SESSION_TITLE = 5; int STARRED_SESSION_ROOM_NAME = 6; } }