• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2010 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.contacts.socialwidget;
18 
19 import android.app.PendingIntent;
20 import android.appwidget.AppWidgetManager;
21 import android.appwidget.AppWidgetProvider;
22 import android.content.ContentUris;
23 import android.content.Context;
24 import android.content.Intent;
25 import android.content.Loader;
26 import android.graphics.Bitmap;
27 import android.graphics.BitmapFactory;
28 import android.graphics.Typeface;
29 import android.net.Uri;
30 import android.provider.ContactsContract.StreamItems;
31 import android.text.SpannableStringBuilder;
32 import android.text.TextUtils;
33 import android.text.style.AbsoluteSizeSpan;
34 import android.text.style.StyleSpan;
35 import android.util.Log;
36 import android.util.SparseArray;
37 import android.view.View;
38 import android.widget.RemoteViews;
39 
40 import com.android.contacts.R;
41 import com.android.contacts.common.model.AccountTypeManager;
42 import com.android.contacts.model.Contact;
43 import com.android.contacts.model.ContactLoader;
44 import com.android.contacts.common.model.account.AccountType;
45 import com.android.contacts.quickcontact.QuickContactBroadcastReceiver;
46 import com.android.contacts.util.ContactBadgeUtil;
47 import com.android.contacts.util.HtmlUtils;
48 import com.android.contacts.util.StreamItemEntry;
49 
50 import java.util.List;
51 
52 public class SocialWidgetProvider extends AppWidgetProvider {
53     private static final String TAG = "SocialWidgetProvider";
54 
55     /**
56      * Max length of a snippet that is considered "short" and displayed in
57      * a separate line.
58      */
59     private static final int SHORT_SNIPPET_LENGTH = 48;
60 
61     private static SparseArray<ContactLoader> sLoaders = new SparseArray<ContactLoader>();
62 
63     @Override
onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds)64     public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
65         for (int appWidgetId : appWidgetIds) {
66             Log.d(TAG, "onUpdate called for " + appWidgetId);
67         }
68 
69         for (int appWidgetId : appWidgetIds) {
70             loadWidgetData(context, appWidgetManager, appWidgetId, false);
71         }
72     }
73 
74     @Override
onDeleted(Context context, int[] appWidgetIds)75     public void onDeleted(Context context, int[] appWidgetIds) {
76         for (int appWidgetId : appWidgetIds) {
77             ContactLoader loader = sLoaders.get(appWidgetId);
78             if (loader != null) {
79                 Log.d(TAG, "Stopping loader for widget with id=" + appWidgetId);
80                 loader.reset();
81                 sLoaders.delete(appWidgetId);
82             }
83         }
84         SocialWidgetSettings.getInstance().remove(context, appWidgetIds);
85     }
86 
loadWidgetData(final Context context, final AppWidgetManager appWidgetManager, final int widgetId, boolean forceLoad)87     public static void loadWidgetData(final Context context,
88             final AppWidgetManager appWidgetManager, final int widgetId, boolean forceLoad) {
89         ContactLoader previousLoader = sLoaders.get(widgetId);
90 
91         if (previousLoader != null && !forceLoad) {
92             previousLoader.startLoading();
93             return;
94         }
95 
96         if (previousLoader != null) {
97             previousLoader.reset();
98         }
99 
100         // Show that we are loading
101         final RemoteViews loadingViews =
102                 new RemoteViews(context.getPackageName(), R.layout.social_widget);
103         loadingViews.setTextViewText(R.id.name,
104                 context.getString(R.string.social_widget_loading));
105         loadingViews.setViewVisibility(R.id.name, View.VISIBLE);
106         loadingViews.setViewVisibility(R.id.name_and_snippet, View.GONE);
107         appWidgetManager.updateAppWidget(widgetId, loadingViews);
108 
109         // Load
110         final Uri contactUri =
111                 SocialWidgetSettings.getInstance().getContactUri(context, widgetId);
112         if (contactUri == null) {
113             // Not yet set-up (this can happen while the Configuration activity is visible)
114             return;
115         }
116         final ContactLoader contactLoader = new ContactLoader(context, contactUri, false, true,
117                 false, true, false);
118         contactLoader.registerListener(0,
119                 new ContactLoader.OnLoadCompleteListener<Contact>() {
120                     @Override
121                     public void onLoadComplete(Loader<Contact> loader,
122                             Contact contactData) {
123                         bindRemoteViews(context, widgetId, appWidgetManager, contactData);
124                     }
125                 });
126         contactLoader.startLoading();
127         sLoaders.append(widgetId, contactLoader);
128     }
129 
bindRemoteViews(final Context context, final int widgetId, final AppWidgetManager widgetManager, Contact contactData)130     private static void bindRemoteViews(final Context context, final int widgetId,
131             final AppWidgetManager widgetManager, Contact contactData) {
132         Log.d(TAG, "Loaded " + contactData.getLookupKey()
133                 + " for widget with id=" + widgetId);
134         final RemoteViews views = new RemoteViews(context.getPackageName(),
135                 R.layout.social_widget);
136 
137         if (!contactData.isLoaded()) {
138             setDisplayNameAndSnippet(context, views,
139                     context.getString(R.string.invalidContactMessage), null, null, null);
140             setPhoto(views, ContactBadgeUtil.loadDefaultAvatarPhoto(context, false, false));
141         } else {
142             byte[] photo = contactData.getPhotoBinaryData();
143             setPhoto(views, photo != null
144                     ? BitmapFactory.decodeByteArray(photo, 0, photo.length)
145                             : ContactBadgeUtil.loadDefaultAvatarPhoto(context, false, false));
146 
147             // TODO: Rotate between all the stream items?
148 
149             final Intent intent = new Intent(context, QuickContactBroadcastReceiver.class);
150             intent.setData(contactData.getLookupUri());
151             final PendingIntent pendingIntent = PendingIntent.getBroadcast(
152                     context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
153             views.setOnClickPendingIntent(R.id.border, pendingIntent);
154 
155             setDisplayNameAndSnippet(context, views, contactData.getDisplayName(),
156                     contactData.getPhoneticName(), contactData.getStreamItems(), pendingIntent);
157         }
158 
159         // Configure UI
160         widgetManager.updateAppWidget(widgetId, views);
161     }
162 
163 
setPhoto(RemoteViews views, Bitmap photo)164     private static void setPhoto(RemoteViews views, Bitmap photo) {
165         views.setImageViewBitmap(R.id.image, photo);
166     }
167 
168     /**
169      * Set the display name, phonetic name and the social snippet.
170      */
setDisplayNameAndSnippet(Context context, RemoteViews views, CharSequence displayName, CharSequence phoneticName, List<StreamItemEntry> streamItems, PendingIntent defaultIntent)171     private static void setDisplayNameAndSnippet(Context context, RemoteViews views,
172             CharSequence displayName, CharSequence phoneticName,
173             List<StreamItemEntry> streamItems, PendingIntent defaultIntent) {
174         SpannableStringBuilder sb = new SpannableStringBuilder();
175 
176         CharSequence name = displayName;
177         // If there is no display name, use the default missing name string
178         if (TextUtils.isEmpty(name)) {
179             name = context.getString(R.string.missing_name);
180         }
181         if (!TextUtils.isEmpty(phoneticName)) {
182             name = context.getString(R.string.widget_name_and_phonetic,
183                     name, phoneticName);
184         }
185         sb.append(name);
186 
187         AbsoluteSizeSpan sizeSpan = new AbsoluteSizeSpan(
188                 context.getResources().getDimensionPixelSize(R.dimen.widget_text_size_name));
189         StyleSpan styleSpan = new StyleSpan(Typeface.BOLD);
190         sb.setSpan(sizeSpan, 0, name.length(), 0);
191         sb.setSpan(styleSpan, 0, name.length(), 0);
192 
193         if (streamItems == null || streamItems.isEmpty()) {
194             views.setTextViewText(R.id.name, sb);
195             views.setViewVisibility(R.id.name, View.VISIBLE);
196             views.setViewVisibility(R.id.name_and_snippet, View.GONE);
197             // Don't set a pending intent if the intent is null, otherwise the system will try
198             // to write the null intent to a Parcel.
199             if (defaultIntent != null) {
200                 views.setOnClickPendingIntent(R.id.widget_container, defaultIntent);
201             }
202         } else {
203             // TODO: Rotate between all the stream items?
204             StreamItemEntry streamItem = streamItems.get(0);
205             CharSequence status = HtmlUtils.fromHtml(context, streamItem.getText());
206             if (status == null) {
207               status = "";
208             }
209             if (status.length() <= SHORT_SNIPPET_LENGTH) {
210                 sb.append("\n");
211             } else {
212                 sb.append("  ");
213             }
214             sb.append(status);
215             views.setTextViewText(R.id.name_and_snippet, sb);
216             views.setViewVisibility(R.id.name, View.GONE);
217             views.setViewVisibility(R.id.name_and_snippet, View.VISIBLE);
218             final AccountTypeManager manager = AccountTypeManager.getInstance(context);
219             final AccountType accountType =
220                     manager.getAccountType(streamItem.getAccountType(), streamItem.getDataSet());
221             if (accountType.getViewStreamItemActivity() != null) {
222                 final Uri uri = ContentUris.withAppendedId(StreamItems.CONTENT_URI,
223                         streamItem.getId());
224                 final Intent intent = new Intent(Intent.ACTION_VIEW, uri);
225                 intent.setClassName(accountType.syncAdapterPackageName,
226                         accountType.getViewStreamItemActivity());
227                 views.setOnClickPendingIntent(R.id.name_and_snippet_container,
228                         PendingIntent.getActivity(context, 0, intent, 0));
229             }
230         }
231     }
232 }
233