/* * 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.appwidget; import com.google.android.apps.iosched.R; import com.google.android.apps.iosched.provider.ScheduleContract; import com.google.android.apps.iosched.ui.widget.SimpleSectionedListAdapter; import com.google.android.apps.iosched.ui.widget.SimpleSectionedListAdapter.Section; import com.google.android.apps.iosched.util.AccountUtils; import com.google.android.apps.iosched.util.ParserUtils; import com.google.android.apps.iosched.util.UIUtils; import android.annotation.TargetApi; import android.content.Context; import android.content.Intent; import android.content.res.Resources; import android.database.Cursor; import android.os.Build; import android.os.Bundle; import android.provider.BaseColumns; import android.text.TextUtils; import android.text.format.DateUtils; import android.util.SparseBooleanArray; import android.util.SparseIntArray; import android.view.View; import android.widget.RemoteViews; import android.widget.RemoteViewsService; import java.util.ArrayList; import java.util.List; import static com.google.android.apps.iosched.util.LogUtils.LOGV; import static com.google.android.apps.iosched.util.LogUtils.makeLogTag; /** * This is the service that provides the factory to be bound to the collection service. */ @TargetApi(Build.VERSION_CODES.HONEYCOMB) public class MyScheduleWidgetService extends RemoteViewsService { @Override public RemoteViewsFactory onGetViewFactory(Intent intent) { return new WidgetRemoveViewsFactory(this.getApplicationContext()); } /** * This is the factory that will provide data to the collection widget. */ private static class WidgetRemoveViewsFactory implements RemoteViewsService.RemoteViewsFactory { private static final String TAG = makeLogTag(WidgetRemoveViewsFactory.class); private Context mContext; private Cursor mCursor; private SparseIntArray mPMap; private List<SimpleSectionedListAdapter.Section> mSections; private SparseBooleanArray mHeaderPositionMap; public WidgetRemoveViewsFactory(Context context) { mContext = context; } public void onCreate() { // Since we reload the cursor in onDataSetChanged() which gets called immediately after // onCreate(), we do nothing here. } public void onDestroy() { if (mCursor != null) { mCursor.close(); } } public int getCount() { if (mCursor == null || !AccountUtils.isAuthenticated(mContext)) { return 0; } int size = mCursor.getCount() + mSections.size(); if (size < 10) { init(); size = mCursor.getCount() + mSections.size(); } LOGV(TAG, "size returned:" + size); return size; } public RemoteViews getViewAt(int position) { RemoteViews rv; boolean isSectionHeader = mHeaderPositionMap.get(position); int offset = mPMap.get(position); if (isSectionHeader) { rv = new RemoteViews(mContext.getPackageName(), R.layout.list_item_schedule_header); Section section = mSections.get(offset - 1); rv.setTextViewText(R.id.list_item_schedule_header_textview, section.getTitle()); } else { int cursorPosition = position - offset; mCursor.moveToPosition(cursorPosition); rv = new RemoteViews(mContext.getPackageName(), R.layout.list_item_schedule_block_widget); final String type = mCursor.getString(BlocksQuery.BLOCK_TYPE); final String blockId = mCursor.getString(BlocksQuery.BLOCK_ID); final String blockTitle = mCursor.getString(BlocksQuery.BLOCK_TITLE); final String blockType = mCursor.getString(BlocksQuery.BLOCK_TYPE); final String blockMeta = mCursor.getString(BlocksQuery.BLOCK_META); final long blockStart = mCursor.getLong(BlocksQuery.BLOCK_START); final Resources res = mContext.getResources(); if (ParserUtils.BLOCK_TYPE_SESSION.equals(type) || ParserUtils.BLOCK_TYPE_CODE_LAB.equals(type)) { final int numStarredSessions = mCursor.getInt(BlocksQuery.NUM_STARRED_SESSIONS); final String starredSessionId = mCursor .getString(BlocksQuery.STARRED_SESSION_ID); if (numStarredSessions == 0) { // No sessions starred rv.setTextViewText(R.id.block_title, mContext.getString( R.string.schedule_empty_slot_title_template, TextUtils.isEmpty(blockTitle) ? "" : (" " + blockTitle.toLowerCase()))); rv.setTextColor(R.id.block_title, res.getColor(R.color.body_text_1_positive)); rv.setTextViewText(R.id.block_subtitle, mContext.getString( R.string.schedule_empty_slot_subtitle)); rv.setViewVisibility(R.id.extra_button, View.GONE); // TODO: use TaskStackBuilder final Intent fillInIntent = new Intent(); final Bundle extras = new Bundle(); extras.putString(MyScheduleWidgetProvider.EXTRA_BLOCK_ID, blockId); extras.putString(MyScheduleWidgetProvider.EXTRA_BLOCK_TYPE, blockType); extras.putInt(MyScheduleWidgetProvider.EXTRA_NUM_STARRED_SESSIONS, numStarredSessions); extras.putBoolean(MyScheduleWidgetProvider.EXTRA_STARRED_SESSION, false); fillInIntent.putExtras(extras); rv.setOnClickFillInIntent(R.id.list_item_middle_container, fillInIntent); } else if (numStarredSessions == 1) { // exactly 1 session starred final String starredSessionTitle = mCursor.getString(BlocksQuery.STARRED_SESSION_TITLE); String starredSessionRoomName = mCursor.getString(BlocksQuery.STARRED_SESSION_ROOM_NAME); if (starredSessionRoomName == null) { // TODO: remove this WAR for API not returning rooms for code labs starredSessionRoomName = mContext.getString( starredSessionTitle.contains("Code Lab") ? R.string.codelab_room : R.string.unknown_room); } rv.setTextViewText(R.id.block_title, starredSessionTitle); rv.setTextColor(R.id.block_title, res.getColor(R.color.body_text_1)); rv.setTextViewText(R.id.block_subtitle, starredSessionRoomName); rv.setViewVisibility(R.id.extra_button, View.VISIBLE); // TODO: use TaskStackBuilder final Intent fillInIntent = new Intent(); final Bundle extras = new Bundle(); extras.putString(MyScheduleWidgetProvider.EXTRA_STARRED_SESSION_ID, starredSessionId); extras.putString(MyScheduleWidgetProvider.EXTRA_BLOCK_TYPE, blockType); extras.putInt(MyScheduleWidgetProvider.EXTRA_NUM_STARRED_SESSIONS, numStarredSessions); extras.putBoolean(MyScheduleWidgetProvider.EXTRA_STARRED_SESSION, true); fillInIntent.putExtras(extras); rv.setOnClickFillInIntent(R.id.list_item_middle_container, fillInIntent); final Intent fillInIntent2 = new Intent(); final Bundle extras2 = new Bundle(); extras2.putString(MyScheduleWidgetProvider.EXTRA_BLOCK_ID, blockId); extras2.putString(MyScheduleWidgetProvider.EXTRA_BLOCK_TYPE, blockType); extras2.putBoolean(MyScheduleWidgetProvider.EXTRA_STARRED_SESSION, true); extras2.putBoolean(MyScheduleWidgetProvider.EXTRA_BUTTON, true); extras2.putInt(MyScheduleWidgetProvider.EXTRA_NUM_STARRED_SESSIONS, numStarredSessions); fillInIntent2.putExtras(extras2); rv.setOnClickFillInIntent(R.id.extra_button, fillInIntent2); } else { // 2 or more sessions starred rv.setTextViewText(R.id.block_title, mContext.getString(R.string.schedule_conflict_title, numStarredSessions)); rv.setTextColor(R.id.block_title, res.getColor(R.color.body_text_1)); rv.setTextViewText(R.id.block_subtitle, mContext.getString(R.string.schedule_conflict_subtitle)); rv.setViewVisibility(R.id.extra_button, View.VISIBLE); // TODO: use TaskStackBuilder final Intent fillInIntent = new Intent(); final Bundle extras = new Bundle(); extras.putString(MyScheduleWidgetProvider.EXTRA_BLOCK_ID, blockId); extras.putString(MyScheduleWidgetProvider.EXTRA_BLOCK_TYPE, blockType); extras.putBoolean(MyScheduleWidgetProvider.EXTRA_STARRED_SESSION, true); extras.putInt(MyScheduleWidgetProvider.EXTRA_NUM_STARRED_SESSIONS, numStarredSessions); fillInIntent.putExtras(extras); rv.setOnClickFillInIntent(R.id.list_item_middle_container, fillInIntent); final Intent fillInIntent2 = new Intent(); final Bundle extras2 = new Bundle(); extras2.putString(MyScheduleWidgetProvider.EXTRA_BLOCK_ID, blockId); extras2.putString(MyScheduleWidgetProvider.EXTRA_BLOCK_TYPE, blockType); extras2.putBoolean(MyScheduleWidgetProvider.EXTRA_STARRED_SESSION, true); extras2.putBoolean(MyScheduleWidgetProvider.EXTRA_BUTTON, true); extras2.putInt(MyScheduleWidgetProvider.EXTRA_NUM_STARRED_SESSIONS, numStarredSessions); fillInIntent2.putExtras(extras2); rv.setOnClickFillInIntent(R.id.extra_button, fillInIntent2); } rv.setTextColor(R.id.block_subtitle, res.getColor(R.color.body_text_2)); } else if (ParserUtils.BLOCK_TYPE_KEYNOTE.equals(type)) { final String starredSessionId = mCursor .getString(BlocksQuery.STARRED_SESSION_ID); final String starredSessionTitle = mCursor.getString(BlocksQuery.STARRED_SESSION_TITLE); rv.setTextViewText(R.id.block_title, starredSessionTitle); rv.setTextColor(R.id.block_title, res.getColor(R.color.body_text_1)); rv.setTextViewText(R.id.block_subtitle, res.getString(R.string.keynote_room)); rv.setViewVisibility(R.id.extra_button, View.GONE); // TODO: use TaskStackBuilder final Intent fillInIntent = new Intent(); final Bundle extras = new Bundle(); extras.putString(MyScheduleWidgetProvider.EXTRA_STARRED_SESSION_ID, starredSessionId); extras.putString(MyScheduleWidgetProvider.EXTRA_BLOCK_TYPE, blockType); fillInIntent.putExtras(extras); rv.setOnClickFillInIntent(R.id.list_item_middle_container, fillInIntent); } else { rv.setTextViewText(R.id.block_title, blockTitle); rv.setTextColor(R.id.block_title, res.getColor(R.color.body_text_disabled)); rv.setTextViewText(R.id.block_subtitle, blockMeta); rv.setTextColor(R.id.block_subtitle, res.getColor(R.color.body_text_disabled)); rv.setViewVisibility(R.id.extra_button, View.GONE); // TODO: use TaskStackBuilder final Intent fillInIntent = new Intent(); final Bundle extras = new Bundle(); extras.putString(MyScheduleWidgetProvider.EXTRA_BLOCK_ID, blockId); extras.putString(MyScheduleWidgetProvider.EXTRA_BLOCK_TYPE, blockType); extras.putInt(MyScheduleWidgetProvider.EXTRA_NUM_STARRED_SESSIONS, -1); fillInIntent.putExtras(extras); rv.setOnClickFillInIntent(R.id.list_item_middle_container, fillInIntent); } rv.setTextViewText(R.id.block_time, DateUtils.formatDateTime(mContext, blockStart, DateUtils.FORMAT_SHOW_TIME | DateUtils.FORMAT_12HOUR)); } return rv; } public RemoteViews getLoadingView() { return null; } public int getViewTypeCount() { return 2; } public long getItemId(int position) { return position; } public boolean hasStableIds() { return true; } public void onDataSetChanged() { init(); } private void init() { if (mCursor != null) { mCursor.close(); } mCursor = mContext.getContentResolver().query(ScheduleContract.Blocks.CONTENT_URI, BlocksQuery.PROJECTION, ScheduleContract.Blocks.BLOCK_END + " >= ?", new String[]{ Long.toString(UIUtils.getCurrentTime(mContext)) }, ScheduleContract.Blocks.DEFAULT_SORT); mSections = new ArrayList<SimpleSectionedListAdapter.Section>(); mCursor.moveToFirst(); long previousTime = -1; long time; mPMap = new SparseIntArray(); mHeaderPositionMap = new SparseBooleanArray(); int offset = 0; int globalPosition = 0; while (!mCursor.isAfterLast()) { time = mCursor.getLong(BlocksQuery.BLOCK_START); if (!UIUtils.isSameDay(previousTime, time)) { mSections.add(new SimpleSectionedListAdapter.Section(mCursor.getPosition(), DateUtils.formatDateTime(mContext, time, DateUtils.FORMAT_ABBREV_MONTH | DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_SHOW_WEEKDAY))); ++offset; mHeaderPositionMap.put(globalPosition, true); mPMap.put(globalPosition, offset); ++globalPosition; } mHeaderPositionMap.put(globalPosition, false); mPMap.put(globalPosition, offset); ++globalPosition; previousTime = time; mCursor.moveToNext(); } LOGV(TAG, "Leaving init()"); } } public interface BlocksQuery { String[] PROJECTION = { BaseColumns._ID, ScheduleContract.Blocks.BLOCK_ID, ScheduleContract.Blocks.BLOCK_TITLE, ScheduleContract.Blocks.BLOCK_START, ScheduleContract.Blocks.BLOCK_END, ScheduleContract.Blocks.BLOCK_TYPE, ScheduleContract.Blocks.BLOCK_META, ScheduleContract.Blocks.NUM_STARRED_SESSIONS, ScheduleContract.Blocks.STARRED_SESSION_ID, ScheduleContract.Blocks.STARRED_SESSION_TITLE, ScheduleContract.Blocks.STARRED_SESSION_ROOM_NAME, }; int _ID = 0; int BLOCK_ID = 1; int BLOCK_TITLE = 2; int BLOCK_START = 3; int BLOCK_END = 4; int BLOCK_TYPE = 5; int BLOCK_META = 6; int NUM_STARRED_SESSIONS = 7; int STARRED_SESSION_ID = 8; int STARRED_SESSION_TITLE = 9; int STARRED_SESSION_ROOM_NAME = 10; } }