1c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor/*
2c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor * Copyright (C) 2012 The Android Open Source Project
3c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor *
4c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor * Licensed under the Apache License, Version 2.0 (the "License");
5c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor * you may not use this file except in compliance with the License.
6c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor * You may obtain a copy of the License at
7c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor *
8c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor *      http://www.apache.org/licenses/LICENSE-2.0
9c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor *
10c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor * Unless required by applicable law or agreed to in writing, software
11c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor * distributed under the License is distributed on an "AS IS" BASIS,
12c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor * See the License for the specific language governing permissions and
14c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor * limitations under the License.
15c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor */
16c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor
17c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylorpackage com.android.mms.widget;
18c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor
19c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylorimport android.appwidget.AppWidgetManager;
20c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylorimport android.content.Context;
21c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylorimport android.content.Intent;
22c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylorimport android.content.res.Resources;
23c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylorimport android.database.Cursor;
24c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylorimport android.provider.Telephony.Threads;
25c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylorimport android.text.Spannable;
26c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylorimport android.text.SpannableStringBuilder;
27c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylorimport android.text.style.ForegroundColorSpan;
28ee2749f9d077a6dfddd536b66694b4428c5df92cTom Taylorimport android.text.style.TextAppearanceSpan;
29c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylorimport android.util.Log;
30c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylorimport android.view.View;
31c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylorimport android.widget.RemoteViews;
32c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylorimport android.widget.RemoteViewsService;
33c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor
34c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylorimport com.android.mms.LogTag;
35c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylorimport com.android.mms.R;
36c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylorimport com.android.mms.data.Contact;
37c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylorimport com.android.mms.data.Conversation;
38c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylorimport com.android.mms.ui.ConversationList;
39c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylorimport com.android.mms.ui.ConversationListItem;
40c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylorimport com.android.mms.ui.MessageUtils;
41c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylorimport com.android.mms.util.SmileyParser;
42c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor
43c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylorpublic class MmsWidgetService extends RemoteViewsService {
44c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor    private static final String TAG = "MmsWidgetService";
45c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor
46c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor    /**
47c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor     * Lock to avoid race condition between widgets.
48c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor     */
49c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor    private static final Object sWidgetLock = new Object();
50c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor
51c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor    @Override
52c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor    public RemoteViewsFactory onGetViewFactory(Intent intent) {
53c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor        if (Log.isLoggable(LogTag.WIDGET, Log.VERBOSE)) {
54c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor            Log.v(TAG, "onGetViewFactory intent: " + intent);
55c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor        }
56c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor        return new MmsFactory(getApplicationContext(), intent);
57c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor    }
58c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor
59c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor    /**
60c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor     * Remote Views Factory for Mms Widget.
61c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor     */
62c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor    private static class MmsFactory
63c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor            implements RemoteViewsService.RemoteViewsFactory, Contact.UpdateListener {
64c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor        private static final int MAX_CONVERSATIONS_COUNT = 25;
65c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor        private final Context mContext;
66c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor        private final int mAppWidgetId;
67c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor        private boolean mShouldShowViewMore;
68c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor        private Cursor mConversationCursor;
69c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor        private int mUnreadConvCount;
70c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor        private final AppWidgetManager mAppWidgetManager;
71c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor
72c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor        // Static colors
73c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor        private static int SUBJECT_TEXT_COLOR_READ;
74c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor        private static int SUBJECT_TEXT_COLOR_UNREAD;
75c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor        private static int SENDERS_TEXT_COLOR_READ;
76c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor        private static int SENDERS_TEXT_COLOR_UNREAD;
77c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor
78c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor        public MmsFactory(Context context, Intent intent) {
79c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor            mContext = context;
80c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor            mAppWidgetId = intent.getIntExtra(
81c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor                    AppWidgetManager.EXTRA_APPWIDGET_ID, AppWidgetManager.INVALID_APPWIDGET_ID);
82c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor            mAppWidgetManager = AppWidgetManager.getInstance(context);
83c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor            if (Log.isLoggable(LogTag.WIDGET, Log.VERBOSE)) {
84c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor                Log.v(TAG, "MmsFactory intent: " + intent + "widget id: " + mAppWidgetId);
85c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor            }
86c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor            // Initialize colors
87c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor            Resources res = context.getResources();
88c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor            SENDERS_TEXT_COLOR_READ = res.getColor(R.color.widget_sender_text_color_read);
89c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor            SENDERS_TEXT_COLOR_UNREAD = res.getColor(R.color.widget_sender_text_color_unread);
90c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor            SUBJECT_TEXT_COLOR_READ = res.getColor(R.color.widget_subject_text_color_read);
91c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor            SUBJECT_TEXT_COLOR_UNREAD = res.getColor(R.color.widget_subject_text_color_unread);
92c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor        }
93c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor
94c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor        @Override
95c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor        public void onCreate() {
96c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor            if (Log.isLoggable(LogTag.WIDGET, Log.VERBOSE)) {
97c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor                Log.v(TAG, "onCreate");
98c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor            }
99c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor            Contact.addListener(this);
100c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor        }
101c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor
102c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor        @Override
103c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor        public void onDestroy() {
104c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor            if (Log.isLoggable(LogTag.WIDGET, Log.VERBOSE)) {
105c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor                Log.v(TAG, "onDestroy");
106c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor            }
107c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor            synchronized (sWidgetLock) {
108c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor                if (mConversationCursor != null && !mConversationCursor.isClosed()) {
109c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor                    mConversationCursor.close();
110c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor                    mConversationCursor = null;
111c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor                }
112c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor                Contact.removeListener(this);
113c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor            }
114c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor        }
115c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor
116c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor        @Override
117c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor        public void onDataSetChanged() {
118c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor            if (Log.isLoggable(LogTag.WIDGET, Log.VERBOSE)) {
119c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor                Log.v(TAG, "onDataSetChanged");
120c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor            }
121c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor            synchronized (sWidgetLock) {
122c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor                if (mConversationCursor != null) {
123c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor                    mConversationCursor.close();
124c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor                    mConversationCursor = null;
125c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor                }
126c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor                mConversationCursor = queryAllConversations();
127c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor                mUnreadConvCount = queryUnreadCount();
128c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor                onLoadComplete();
129c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor            }
130c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor        }
131c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor
132c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor        private Cursor queryAllConversations() {
133c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor            return mContext.getContentResolver().query(
134c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor                    Conversation.sAllThreadsUri, Conversation.ALL_THREADS_PROJECTION,
135c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor                    null, null, null);
136c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor        }
137c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor
138c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor        private int queryUnreadCount() {
139c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor            Cursor cursor = null;
140c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor            int unreadCount = 0;
141c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor            try {
142c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor                cursor = mContext.getContentResolver().query(
143c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor                    Conversation.sAllThreadsUri, Conversation.ALL_THREADS_PROJECTION,
144c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor                    Threads.READ + "=0", null, null);
145c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor                if (cursor != null) {
146c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor                    unreadCount = cursor.getCount();
147c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor                }
148c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor            } finally {
149c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor                if (cursor != null) {
150c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor                    cursor.close();
151c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor                }
152c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor            }
153c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor            return unreadCount;
154c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor        }
155c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor
156c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor        /**
157c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor         * Returns the number of items should be shown in the widget list.  This method also updates
158c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor         * the boolean that indicates whether the "show more" item should be shown.
159c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor         * @return the number of items to be displayed in the list.
160c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor         */
161c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor        @Override
162c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor        public int getCount() {
163c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor            if (Log.isLoggable(LogTag.WIDGET, Log.VERBOSE)) {
164c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor                Log.v(TAG, "getCount");
165c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor            }
166c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor            synchronized (sWidgetLock) {
167c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor                if (mConversationCursor == null) {
168c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor                    return 0;
169c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor                }
170c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor                final int count = getConversationCount();
171c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor                mShouldShowViewMore = count < mConversationCursor.getCount();
172c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor                return count + (mShouldShowViewMore ? 1 : 0);
173c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor            }
174c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor        }
175c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor
176c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor        /**
177c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor         * Returns the number of conversations that should be shown in the widget.  This method
178c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor         * doesn't update the boolean that indicates that the "show more" item should be included
179c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor         * in the list.
180c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor         * @return
181c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor         */
182c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor        private int getConversationCount() {
183c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor            if (Log.isLoggable(LogTag.WIDGET, Log.VERBOSE)) {
184c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor                Log.v(TAG, "getConversationCount");
185c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor            }
186c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor            synchronized (sWidgetLock) {
187c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor                return Math.min(mConversationCursor.getCount(), MAX_CONVERSATIONS_COUNT);
188c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor            }
189c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor        }
190c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor
191c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor        /*
192c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor         * Add color to a given text
193c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor         */
194c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor        private SpannableStringBuilder addColor(CharSequence text, int color) {
195c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor            SpannableStringBuilder builder = new SpannableStringBuilder(text);
196c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor            if (color != 0) {
197c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor                builder.setSpan(new ForegroundColorSpan(color), 0, text.length(),
198c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor                        Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
199c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor            }
200c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor            return builder;
201c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor        }
202c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor
203c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor        /**
204c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor         * @return the {@link RemoteViews} for a specific position in the list.
205c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor         */
206c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor        @Override
207c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor        public RemoteViews getViewAt(int position) {
208c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor            if (Log.isLoggable(LogTag.WIDGET, Log.VERBOSE)) {
209c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor                Log.v(TAG, "getViewAt position: " + position);
210c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor            }
211c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor            synchronized (sWidgetLock) {
212c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor                // "View more conversations" view.
213c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor                if (mConversationCursor == null
214c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor                        || (mShouldShowViewMore && position >= getConversationCount())) {
215c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor                    return getViewMoreConversationsView();
216c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor                }
217c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor
218c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor                if (!mConversationCursor.moveToPosition(position)) {
219c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor                    // If we ever fail to move to a position, return the "View More conversations"
220c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor                    // view.
221c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor                    Log.w(TAG, "Failed to move to position: " + position);
222c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor                    return getViewMoreConversationsView();
223c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor                }
224c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor
225c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor                Conversation conv = Conversation.from(mContext, mConversationCursor);
226c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor
227c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor                // Inflate and fill out the remote view
228c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor                RemoteViews remoteViews = new RemoteViews(
229c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor                        mContext.getPackageName(), R.layout.widget_conversation);
230c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor
231c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor                if (conv.hasUnreadMessages()) {
232c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor                    remoteViews.setViewVisibility(R.id.widget_unread_background, View.VISIBLE);
233c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor                    remoteViews.setViewVisibility(R.id.widget_read_background, View.GONE);
234c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor                } else {
235c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor                    remoteViews.setViewVisibility(R.id.widget_unread_background, View.GONE);
236c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor                    remoteViews.setViewVisibility(R.id.widget_read_background, View.VISIBLE);
237c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor                }
238c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor                boolean hasAttachment = conv.hasAttachment();
239c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor                remoteViews.setViewVisibility(R.id.attachment, hasAttachment ? View.VISIBLE :
240c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor                    View.GONE);
241c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor
242c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor                // Date
243c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor                remoteViews.setTextViewText(R.id.date,
244c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor                        addColor(MessageUtils.formatTimeStampString(mContext, conv.getDate()),
245c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor                                conv.hasUnreadMessages() ? SUBJECT_TEXT_COLOR_UNREAD :
246c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor                                    SUBJECT_TEXT_COLOR_READ));
247c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor
248c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor                // From
249ee2749f9d077a6dfddd536b66694b4428c5df92cTom Taylor                int color = conv.hasUnreadMessages() ? SENDERS_TEXT_COLOR_UNREAD :
250ee2749f9d077a6dfddd536b66694b4428c5df92cTom Taylor                        SENDERS_TEXT_COLOR_READ;
251c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor                SpannableStringBuilder from = addColor(conv.getRecipients().formatNames(", "),
252ee2749f9d077a6dfddd536b66694b4428c5df92cTom Taylor                        color);
253ee2749f9d077a6dfddd536b66694b4428c5df92cTom Taylor
254ee2749f9d077a6dfddd536b66694b4428c5df92cTom Taylor                if (conv.hasDraft()) {
255ee2749f9d077a6dfddd536b66694b4428c5df92cTom Taylor                    from.append(mContext.getResources().getString(R.string.draft_separator));
256ee2749f9d077a6dfddd536b66694b4428c5df92cTom Taylor                    int before = from.length();
257ee2749f9d077a6dfddd536b66694b4428c5df92cTom Taylor                    from.append(mContext.getResources().getString(R.string.has_draft));
258ee2749f9d077a6dfddd536b66694b4428c5df92cTom Taylor                    from.setSpan(new TextAppearanceSpan(mContext,
259ee2749f9d077a6dfddd536b66694b4428c5df92cTom Taylor                            android.R.style.TextAppearance_Small, color), before,
260ee2749f9d077a6dfddd536b66694b4428c5df92cTom Taylor                            from.length(), Spannable.SPAN_INCLUSIVE_EXCLUSIVE);
261ee2749f9d077a6dfddd536b66694b4428c5df92cTom Taylor                    from.setSpan(new ForegroundColorSpan(
262ee2749f9d077a6dfddd536b66694b4428c5df92cTom Taylor                            mContext.getResources().getColor(R.drawable.text_color_red)),
263ee2749f9d077a6dfddd536b66694b4428c5df92cTom Taylor                            before, from.length(), Spannable.SPAN_INCLUSIVE_EXCLUSIVE);
264ee2749f9d077a6dfddd536b66694b4428c5df92cTom Taylor                }
265ee2749f9d077a6dfddd536b66694b4428c5df92cTom Taylor
266c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor                // Unread messages are shown in bold
267c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor                if (conv.hasUnreadMessages()) {
268c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor                    from.setSpan(ConversationListItem.STYLE_BOLD, 0, from.length(),
269c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor                            Spannable.SPAN_INCLUSIVE_EXCLUSIVE);
270c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor                }
271c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor                remoteViews.setTextViewText(R.id.from, from);
272c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor
273c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor                // Subject
274c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor                // TODO: the SmileyParser inserts image spans but they don't seem to make it
275c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor                // into the remote view.
276c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor                SmileyParser parser = SmileyParser.getInstance();
277c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor                remoteViews.setTextViewText(R.id.subject,
278c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor                        addColor(parser.addSmileySpans(conv.getSnippet()),
279c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor                                conv.hasUnreadMessages() ? SUBJECT_TEXT_COLOR_UNREAD :
280c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor                                    SUBJECT_TEXT_COLOR_READ));
281c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor
282c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor                // On click intent.
283c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor                Intent clickIntent = new Intent(Intent.ACTION_VIEW);
284c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor                clickIntent.setType("vnd.android-dir/mms-sms");
285c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor                clickIntent.putExtra("thread_id", conv.getThreadId());
286c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor
287c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor                remoteViews.setOnClickFillInIntent(R.id.widget_conversation, clickIntent);
288c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor
289c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor                return remoteViews;
290c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor            }
291c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor        }
292c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor
293c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor        /**
294c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor         * @return the "View more conversations" view. When the user taps this item, they're
295c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor         * taken to the messaging app's conversation list.
296c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor         */
297c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor        private RemoteViews getViewMoreConversationsView() {
298c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor            if (Log.isLoggable(LogTag.WIDGET, Log.VERBOSE)) {
299c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor                Log.v(TAG, "getViewMoreConversationsView");
300c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor            }
301c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor            RemoteViews view = new RemoteViews(mContext.getPackageName(), R.layout.widget_loading);
302c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor            view.setTextViewText(
303c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor                    R.id.loading_text, mContext.getText(R.string.view_more_conversations));
304c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor            view.setOnClickFillInIntent(R.id.widget_loading,
305c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor                   new Intent(mContext, ConversationList.class));
306c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor            return view;
307c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor        }
308c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor
309c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor        @Override
310c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor        public RemoteViews getLoadingView() {
311c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor            RemoteViews view = new RemoteViews(mContext.getPackageName(), R.layout.widget_loading);
312c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor            view.setTextViewText(
313c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor                    R.id.loading_text, mContext.getText(R.string.loading_conversations));
314c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor            return view;
315c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor        }
316c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor
317c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor        @Override
318c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor        public int getViewTypeCount() {
3191e1be6fad69b0941e446576c20a6dfd79e8da629Tom Taylor            return 2;
320c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor        }
321c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor
322c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor        @Override
323c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor        public long getItemId(int position) {
324c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor            return position;
325c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor        }
326c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor
327c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor        @Override
328c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor        public boolean hasStableIds() {
329c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor            return true;
330c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor        }
331c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor
332c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor        private void onLoadComplete() {
333c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor            if (Log.isLoggable(LogTag.WIDGET, Log.VERBOSE)) {
334c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor                Log.v(TAG, "onLoadComplete");
335c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor            }
336c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor            RemoteViews remoteViews = new RemoteViews(mContext.getPackageName(), R.layout.widget);
337c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor
338c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor            remoteViews.setViewVisibility(R.id.widget_unread_count, mUnreadConvCount > 0 ?
339c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor                    View.VISIBLE : View.GONE);
340c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor            if (mUnreadConvCount > 0) {
341c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor                remoteViews.setTextViewText(
342c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor                        R.id.widget_unread_count, Integer.toString(mUnreadConvCount));
343c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor            }
344c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor
345c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor            mAppWidgetManager.partiallyUpdateAppWidget(mAppWidgetId, remoteViews);
346c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor        }
347c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor
348c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor        public void onUpdate(Contact updated) {
349c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor            if (Log.isLoggable(LogTag.WIDGET, Log.VERBOSE)) {
350c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor                Log.v(TAG, "onUpdate from Contact: " + updated);
351c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor            }
352c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor            mAppWidgetManager.notifyAppWidgetViewDataChanged(mAppWidgetId, R.id.conversation_list);
353c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor        }
354c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor
355c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor    }
356c7aa632be8e7d3ebe71f236f534ea2f0af71e04aTom Taylor}
357