/* * 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.HomeActivity; import com.google.android.apps.iosched.ui.SessionLivestreamActivity; 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 com.google.api.client.googleapis.extensions.android2.auth.GoogleAccountManager; import android.accounts.Account; import android.annotation.TargetApi; import android.app.PendingIntent; import android.appwidget.AppWidgetManager; import android.appwidget.AppWidgetProvider; import android.content.ComponentName; import android.content.ContentResolver; import android.content.Context; import android.content.Intent; import android.database.ContentObserver; import android.net.Uri; import android.os.Build; import android.os.Bundle; import android.os.Handler; import android.os.HandlerThread; import android.widget.RemoteViews; import static com.google.android.apps.iosched.util.LogUtils.LOGV; import static com.google.android.apps.iosched.util.LogUtils.makeLogTag; /** * The app widget's AppWidgetProvider. */ @TargetApi(Build.VERSION_CODES.HONEYCOMB) public class MyScheduleWidgetProvider extends AppWidgetProvider { private static final String TAG = makeLogTag(MyScheduleWidgetProvider.class); public static final String EXTRA_BLOCK_ID = "block_id"; public static final String EXTRA_STARRED_SESSION_ID = "star_session_id"; public static final String EXTRA_BLOCK_TYPE = "block_type"; public static final String EXTRA_STARRED_SESSION = "starred_session"; public static final String EXTRA_NUM_STARRED_SESSIONS = "num_starred_sessions"; public static final String EXTRA_BUTTON = "extra_button"; public static final String CLICK_ACTION = "com.google.android.apps.iosched.appwidget.action.CLICK"; public static final String REFRESH_ACTION = "com.google.android.apps.iosched.appwidget.action.REFRESH"; public static final String EXTRA_PERFORM_SYNC = "com.google.android.apps.iosched.appwidget.extra.PERFORM_SYNC"; private static Handler sWorkerQueue; // TODO: this will get destroyed when the app process is killed. need better observer strategy. private static SessionDataProviderObserver sDataObserver; public MyScheduleWidgetProvider() { // Start the worker thread HandlerThread sWorkerThread = new HandlerThread("MyScheduleWidgetProvider-worker"); sWorkerThread.start(); sWorkerQueue = new Handler(sWorkerThread.getLooper()); } @Override public void onEnabled(Context context) { final ContentResolver r = context.getContentResolver(); if (sDataObserver == null) { final AppWidgetManager mgr = AppWidgetManager.getInstance(context); final ComponentName cn = new ComponentName(context, MyScheduleWidgetProvider.class); sDataObserver = new SessionDataProviderObserver(mgr, cn, sWorkerQueue); r.registerContentObserver(ScheduleContract.Sessions.CONTENT_URI, true, sDataObserver); } } @Override public void onReceive(final Context context, Intent widgetIntent) { final String action = widgetIntent.getAction(); if (REFRESH_ACTION.equals(action)) { final boolean shouldSync = widgetIntent.getBooleanExtra(EXTRA_PERFORM_SYNC, false); sWorkerQueue.removeMessages(0); sWorkerQueue.post(new Runnable() { @Override public void run() { // Trigger sync String chosenAccountName = AccountUtils.getChosenAccountName(context); if (shouldSync && chosenAccountName != null) { Bundle extras = new Bundle(); extras.putBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, true); ContentResolver.requestSync( new Account(chosenAccountName, GoogleAccountManager.ACCOUNT_TYPE), ScheduleContract.CONTENT_AUTHORITY, extras); } // Update widget final AppWidgetManager mgr = AppWidgetManager.getInstance(context); final ComponentName cn = new ComponentName(context, MyScheduleWidgetProvider.class); mgr.notifyAppWidgetViewDataChanged(mgr.getAppWidgetIds(cn), R.id.widget_schedule_list); } }); } else if (CLICK_ACTION.equals(action)) { String blockId = widgetIntent.getStringExtra(EXTRA_BLOCK_ID); int numStarredSessions = widgetIntent.getIntExtra(EXTRA_NUM_STARRED_SESSIONS, 0); String starredSessionId = widgetIntent.getStringExtra(EXTRA_STARRED_SESSION_ID); String blockType = widgetIntent.getStringExtra(EXTRA_BLOCK_TYPE); boolean starredSession = widgetIntent.getBooleanExtra(EXTRA_STARRED_SESSION, false); boolean extraButton = widgetIntent.getBooleanExtra(EXTRA_BUTTON, false); LOGV(TAG, "blockId:" + blockId); LOGV(TAG, "starredSession:" + starredSession); LOGV(TAG, "blockType:" + blockType); LOGV(TAG, "numStarredSessions:" + numStarredSessions); LOGV(TAG, "extraButton:" + extraButton); if (ParserUtils.BLOCK_TYPE_SESSION.equals(blockType) || ParserUtils.BLOCK_TYPE_CODE_LAB.equals(blockType)) { Uri sessionsUri; if (numStarredSessions == 0) { sessionsUri = ScheduleContract.Blocks.buildSessionsUri( blockId); LOGV(TAG, "sessionsUri:" + sessionsUri); Intent intent = new Intent(Intent.ACTION_VIEW, sessionsUri); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); context.startActivity(intent); } else if (numStarredSessions == 1) { if (extraButton) { sessionsUri = ScheduleContract.Blocks.buildSessionsUri(blockId); LOGV(TAG, "sessionsUri extraButton:" + sessionsUri); Intent intent = new Intent(Intent.ACTION_VIEW, sessionsUri); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); context.startActivity(intent); } else { sessionsUri = ScheduleContract.Sessions.buildSessionUri(starredSessionId); LOGV(TAG, "sessionsUri:" + sessionsUri); Intent intent = new Intent(Intent.ACTION_VIEW, sessionsUri); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); context.startActivity(intent); } } else { if (extraButton) { sessionsUri = ScheduleContract.Blocks.buildSessionsUri(blockId); LOGV(TAG, "sessionsUri extraButton:" + sessionsUri); Intent intent = new Intent(Intent.ACTION_VIEW, sessionsUri); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); context.startActivity(intent); } else { sessionsUri = ScheduleContract.Blocks.buildStarredSessionsUri(blockId); LOGV(TAG, "sessionsUri:" + sessionsUri); Intent intent = new Intent(Intent.ACTION_VIEW, sessionsUri); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); context.startActivity(intent); } } } else if (ParserUtils.BLOCK_TYPE_KEYNOTE.equals(blockType)) { Uri sessionUri = ScheduleContract.Sessions.buildSessionUri(starredSessionId); LOGV(TAG, "sessionUri:" + sessionUri); Intent intent = new Intent(Intent.ACTION_VIEW, sessionUri); intent.setClass(context, SessionLivestreamActivity.class); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); context.startActivity(intent); } } super.onReceive(context, widgetIntent); } @Override public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) { final boolean isAuthenticated = AccountUtils.isAuthenticated(context); for (int appWidgetId : appWidgetIds) { // Specify the service to provide data for the collection widget. Note that we need to // embed the appWidgetId via the data otherwise it will be ignored. final Intent intent = new Intent(context, MyScheduleWidgetService.class); intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId); intent.setData(Uri.parse(intent.toUri(Intent.URI_INTENT_SCHEME))); final RemoteViews rv = new RemoteViews(context.getPackageName(), R.layout.widget_layout); compatSetRemoteAdapter(rv, appWidgetId, intent); // Set the empty view to be displayed if the collection is empty. It must be a sibling // view of the collection view. rv.setEmptyView(R.id.widget_schedule_list, android.R.id.empty); rv.setTextViewText(android.R.id.empty, context.getResources().getString(isAuthenticated ? R.string.empty_widget_text : R.string.empty_widget_text_signed_out)); final Intent onClickIntent = new Intent(context, MyScheduleWidgetProvider.class); onClickIntent.setAction(MyScheduleWidgetProvider.CLICK_ACTION); onClickIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId); onClickIntent.setData(Uri.parse(onClickIntent.toUri(Intent.URI_INTENT_SCHEME))); final PendingIntent onClickPendingIntent = PendingIntent.getBroadcast(context, 0, onClickIntent, PendingIntent.FLAG_UPDATE_CURRENT); rv.setPendingIntentTemplate(R.id.widget_schedule_list, onClickPendingIntent); final Intent refreshIntent = new Intent(context, MyScheduleWidgetProvider.class); refreshIntent.setAction(MyScheduleWidgetProvider.REFRESH_ACTION); refreshIntent.putExtra(MyScheduleWidgetProvider.EXTRA_PERFORM_SYNC, true); final PendingIntent refreshPendingIntent = PendingIntent.getBroadcast(context, 0, refreshIntent, PendingIntent.FLAG_UPDATE_CURRENT); rv.setOnClickPendingIntent(R.id.widget_refresh_button, refreshPendingIntent); final Intent openAppIntent = new Intent(context, HomeActivity.class); final PendingIntent openAppPendingIntent = PendingIntent.getActivity(context, 0, openAppIntent, PendingIntent.FLAG_UPDATE_CURRENT); rv.setOnClickPendingIntent(R.id.widget_logo, openAppPendingIntent); appWidgetManager.updateAppWidget(appWidgetId, rv); } super.onUpdate(context, appWidgetManager, appWidgetIds); } @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH) private void compatSetRemoteAdapter(RemoteViews rv, int appWidgetId, Intent intent) { if (UIUtils.hasICS()) { rv.setRemoteAdapter(R.id.widget_schedule_list, intent); } else { //noinspection deprecation rv.setRemoteAdapter(appWidgetId, R.id.widget_schedule_list, intent); } } private static class SessionDataProviderObserver extends ContentObserver { private AppWidgetManager mAppWidgetManager; private ComponentName mComponentName; SessionDataProviderObserver(AppWidgetManager appWidgetManager, ComponentName componentName, Handler handler) { super(handler); mAppWidgetManager = appWidgetManager; mComponentName = componentName; } @Override public void onChange(boolean selfChange) { // The data has changed, so notify the widget that the collection view needs to be updated. // In response, the factory's onDataSetChanged() will be called which will requery the // cursor for the new data. mAppWidgetManager.notifyAppWidgetViewDataChanged( mAppWidgetManager.getAppWidgetIds(mComponentName), R.id.widget_schedule_list); } } }