Home | History | Annotate | Download | only in socialwidget
      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
     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
     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 
     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 
    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 
    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      */
    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