Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 04c28ba4 authored by Flavio Fiszman's avatar Flavio Fiszman Committed by Android (Google) Code Review
Browse files

Merge "Handle onAppWidgetOptionsChanged widget add" into sc-dev

parents 9a49890e af8cc8d0
Loading
Loading
Loading
Loading
+4 −1
Original line number Diff line number Diff line
@@ -35,6 +35,7 @@ import android.view.ViewGroup;

import com.android.systemui.R;
import com.android.systemui.people.widget.PeopleSpaceWidgetManager;
import com.android.systemui.people.widget.PeopleTileKey;
import com.android.systemui.statusbar.notification.NotificationEntryManager;

import java.util.List;
@@ -138,7 +139,9 @@ public class PeopleSpaceActivity extends Activity {
                        + mAppWidgetId);
            }
        }
        mPeopleSpaceWidgetManager.addNewWidget(tile, mAppWidgetId);
        PeopleTileKey key = new PeopleTileKey(
                tile.getId(), tile.getUserHandle().getIdentifier(), tile.getPackageName());
        mPeopleSpaceWidgetManager.addNewWidget(mAppWidgetId, key);
        finishActivity();
    }

+89 −158
Original line number Diff line number Diff line
@@ -52,9 +52,7 @@ import android.icu.text.MeasureFormat;
import android.icu.util.Measure;
import android.icu.util.MeasureUnit;
import android.net.Uri;
import android.os.Bundle;
import android.os.Parcelable;
import android.os.ServiceManager;
import android.provider.ContactsContract;
import android.provider.Settings;
import android.service.notification.ConversationChannelWrapper;
@@ -71,10 +69,11 @@ import com.android.internal.logging.UiEvent;
import com.android.internal.logging.UiEventLogger;
import com.android.internal.util.ArrayUtils;
import com.android.settingslib.utils.ThreadUtils;
import com.android.systemui.Dependency;
import com.android.systemui.R;
import com.android.systemui.people.widget.AppWidgetOptionsHelper;
import com.android.systemui.people.widget.LaunchConversationActivity;
import com.android.systemui.people.widget.PeopleSpaceWidgetProvider;
import com.android.systemui.people.widget.PeopleTileKey;
import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;

@@ -106,7 +105,6 @@ public class PeopleSpaceUtils {
    private static final int DAYS_IN_A_WEEK = 7;
    private static final int MIN_HOUR = 1;
    private static final int ONE_DAY = 1;
    public static final String OPTIONS_PEOPLE_SPACE_TILE = "options_people_space_tile";
    public static final String PACKAGE_NAME = "package_name";
    public static final String USER_ID = "user_id";
    public static final String SHORTCUT_ID = "shortcut_id";
@@ -115,6 +113,9 @@ public class PeopleSpaceUtils {
    public static final int INVALID_WIDGET_ID = -1;
    public static final int INVALID_USER_ID = -1;

    public static final PeopleTileKey EMPTY_KEY =
            new PeopleTileKey(EMPTY_STRING, INVALID_USER_ID, EMPTY_STRING);

    private static final Pattern DOUBLE_EXCLAMATION_PATTERN = Pattern.compile("[!][!]+");
    private static final Pattern DOUBLE_QUESTION_PATTERN = Pattern.compile("[?][?]+");
    private static final Pattern ANY_DOUBLE_MARK_PATTERN = Pattern.compile("[!?][!?]+");
@@ -200,67 +201,76 @@ public class PeopleSpaceUtils {
            AppWidgetManager appWidgetManager, IPeopleManager peopleManager) {
        Map<Integer, PeopleSpaceTile> widgetIdToTile = new HashMap<>();
        for (int appWidgetId : appWidgetIds) {
            PeopleSpaceTile tile = getPeopleSpaceTile(peopleManager, appWidgetManager, context,
                    appWidgetId);
            PeopleSpaceTile tile = getPeopleSpaceTile(
                    context, appWidgetId, appWidgetManager, peopleManager);
            if (tile == null) {
                if (DEBUG) Log.d(TAG, "Matching conversation not found for shortcut ID");
                //TODO: Delete app widget id when crash is fixed (b/172932636)
                continue;
            }

            if (DEBUG) Log.d(TAG, "Widget: " + appWidgetId + ", " + tile.getUserName());
            RemoteViews views = createRemoteViews(context, tile, appWidgetId);

            // Tell the AppWidgetManager to perform an update on the current app widget.
            appWidgetManager.updateAppWidget(appWidgetId, views);

            updateAppWidgetOptionsAndView(appWidgetManager, context, appWidgetId, tile);
            widgetIdToTile.put(appWidgetId, tile);
        }
        getBirthdaysOnBackgroundThread(context, appWidgetManager, widgetIdToTile, appWidgetIds);
    }

    /**
     * Returns a {@link PeopleSpaceTile} based on the {@code appWidgetId}. If the PeopleSpaceTile
     * isn't cached, store it in AppWidgetOptions.
     */
    @Nullable
    public static PeopleSpaceTile getPeopleSpaceTile(IPeopleManager peopleManager,
            AppWidgetManager appWidgetManager,
            Context context, int appWidgetId) {
        try {
            // Migrate storage for existing users.
            SharedPreferences widgetSp = context.getSharedPreferences(String.valueOf(appWidgetId),
                    Context.MODE_PRIVATE);
            String pkg = widgetSp.getString(PACKAGE_NAME, EMPTY_STRING);
            int userId = widgetSp.getInt(USER_ID, INVALID_USER_ID);
            String shortcutId = widgetSp.getString(SHORTCUT_ID, EMPTY_STRING);
            if (!validKey(shortcutId, pkg, userId)) {
                SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(context);
                shortcutId = sp.getString(String.valueOf(appWidgetId), null);
                if (shortcutId == null) {
                    Log.e(TAG, "Cannot restore widget");
                    return null;
                }
                migrateExistingUsersToNewStorage(context, shortcutId, appWidgetId);
                pkg = widgetSp.getString(PACKAGE_NAME, EMPTY_STRING);
                userId = widgetSp.getInt(USER_ID, INVALID_USER_ID);
            }

            // Check if tile is cached.
            Bundle options = appWidgetManager.getAppWidgetOptions(appWidgetId);
            PeopleSpaceTile tile = options.getParcelable(OPTIONS_PEOPLE_SPACE_TILE);
    public static PeopleSpaceTile getPeopleSpaceTile(Context context, int appWidgetId,
            AppWidgetManager appWidgetManager, IPeopleManager peopleManager) {
        // First, check if tile is cached in AppWidgetOptions.
        PeopleSpaceTile tile = AppWidgetOptionsHelper.getPeopleTile(appWidgetManager, appWidgetId);
        if (tile != null) {
            if (DEBUG) Log.d(TAG, "People Tile is cached for widget: " + appWidgetId);
            return tile;
        }

        // If not, we get the PeopleTileKey from SharedPreferences, retrieve the Conversation from
        // persisted storage, and cache it in AppWidgetOptions.
        SharedPreferences widgetSp = context.getSharedPreferences(
                String.valueOf(appWidgetId),
                Context.MODE_PRIVATE);
        PeopleTileKey sharedPreferencesKey = new PeopleTileKey(
                widgetSp.getString(SHORTCUT_ID, EMPTY_STRING),
                widgetSp.getInt(USER_ID, INVALID_USER_ID),
                widgetSp.getString(PACKAGE_NAME, EMPTY_STRING));

        if (!sharedPreferencesKey.isValid()) {
            Log.e(TAG, "Cannot find shortcut info for widgetId: " + appWidgetId);
            return null;
        }

        if (DEBUG) Log.d(TAG, "PeopleTile key is present in sharedPreferences: " + appWidgetId);
        // If tile is null, we need to retrieve from persisted storage.
            if (DEBUG) {
                Log.d(TAG,
                        "Retrieving from storage after reboots: " + shortcutId + " user: " + userId
                                + " pkg: " + pkg);
        return getPeopleTileFromPersistentStorage(context, sharedPreferencesKey, peopleManager);
    }

    /**
     * Returns a {@link PeopleSpaceTile} based on {@link ConversationChannel} returned by
     * {@link IPeopleManager}.
     */
    public static PeopleSpaceTile getPeopleTileFromPersistentStorage(Context context,
            PeopleTileKey peopleTileKey, IPeopleManager peopleManager) {
        try {
            if (DEBUG) Log.d(TAG, "Retrieving Tile from storage: " + peopleTileKey.toString());
            LauncherApps launcherApps = context.getSystemService(LauncherApps.class);
            ConversationChannel channel = peopleManager.getConversation(pkg, userId, shortcutId);
            if (launcherApps == null) {
                Log.d(TAG, "LauncherApps is null");
                return null;
            }

            ConversationChannel channel = peopleManager.getConversation(
                    peopleTileKey.getPackageName(),
                    peopleTileKey.getUserId(),
                    peopleTileKey.getShortcutId());
            if (channel == null) {
                Log.d(TAG, "Could not retrieve conversation from storage");
                return null;
            }

            return new PeopleSpaceTile.Builder(channel, launcherApps).build();
        } catch (Exception e) {
            Log.e(TAG, "Failed to retrieve conversation for tile: " + e);
@@ -269,79 +279,46 @@ public class PeopleSpaceUtils {
    }

    /** Returns stored widgets for the conversation specified. */
    public static Set<String> getStoredWidgetIds(SharedPreferences sp, String shortcutId,
            String packageName, int userId) {
        if (shortcutId == null || packageName == null) {
    public static Set<String> getStoredWidgetIds(SharedPreferences sp, PeopleTileKey key) {
        if (!key.isValid()) {
            return new HashSet<>();
        }
        String key = PeopleSpaceUtils.getKey(shortcutId, packageName, userId);
        return new HashSet<>(sp.getStringSet(key, new HashSet<>()));
    }


    /** Best-effort attempts to migrate existing users to the new storage format. */
    // TODO: Remove after sufficient time. Temporary migration storage for existing users.
    private static void migrateExistingUsersToNewStorage(Context context, String shortcutId,
            int appWidgetId) {
        try {
            List<PeopleSpaceTile> tiles =
                    PeopleSpaceUtils.getTiles(context, INotificationManager.Stub.asInterface(
                            ServiceManager.getService(Context.NOTIFICATION_SERVICE)),
                            IPeopleManager.Stub.asInterface(
                                    ServiceManager.getService(Context.PEOPLE_SERVICE)),
                            context.getSystemService(LauncherApps.class),
                            Dependency.get(NotificationEntryManager.class));
            Optional<PeopleSpaceTile> entry = tiles.stream().filter(
                    e -> e.getId().equals(shortcutId)).findFirst();
            if (entry.isPresent()) {
                if (DEBUG) Log.d(TAG, "Migrate storage for " + entry.get().getUserName());
                setStorageForTile(context, entry.get(), appWidgetId);
            } else {
                Log.e(TAG, "Could not migrate user. Delete old storage");
                SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(context);
                SharedPreferences.Editor editor = sp.edit();
                editor.remove(String.valueOf(appWidgetId));
                editor.apply();
            }
        } catch (Exception e) {
            Log.e(TAG, "Could not query conversations");
        }
        return new HashSet<>(sp.getStringSet(key.toString(), new HashSet<>()));
    }

    /** Sets all relevant storage for {@code appWidgetId} association to {@code tile}. */
    public static void setStorageForTile(Context context, PeopleSpaceTile tile, int appWidgetId) {
    public static void setSharedPreferencesStorageForTile(Context context, PeopleTileKey key,
            int appWidgetId) {
        // Write relevant persisted storage.
        SharedPreferences widgetSp = context.getSharedPreferences(String.valueOf(appWidgetId),
                Context.MODE_PRIVATE);
        SharedPreferences.Editor widgetEditor = widgetSp.edit();
        widgetEditor.putString(PeopleSpaceUtils.PACKAGE_NAME, tile.getPackageName());
        widgetEditor.putString(PeopleSpaceUtils.SHORTCUT_ID, tile.getId());
        int userId = getUserId(tile);
        widgetEditor.putInt(PeopleSpaceUtils.USER_ID, userId);
        widgetEditor.putString(PeopleSpaceUtils.PACKAGE_NAME, key.getPackageName());
        widgetEditor.putString(PeopleSpaceUtils.SHORTCUT_ID, key.getShortcutId());
        widgetEditor.putInt(PeopleSpaceUtils.USER_ID, key.getUserId());
        widgetEditor.apply();
        SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(context);
        SharedPreferences.Editor editor = sp.edit();
        editor.putString(String.valueOf(appWidgetId), tile.getId());
        String key = PeopleSpaceUtils.getKey(tile.getId(), tile.getPackageName(), userId);
        editor.putString(String.valueOf(appWidgetId), key.getShortcutId());

        // Don't overwrite existing widgets with the same key.
        Set<String> storedWidgetIds = new HashSet<>(sp.getStringSet(key, new HashSet<>()));
        Set<String> storedWidgetIds = new HashSet<>(
                sp.getStringSet(key.toString(), new HashSet<>()));
        storedWidgetIds.add(String.valueOf(appWidgetId));
        editor.putStringSet(key, storedWidgetIds);
        editor.putStringSet(key.toString(), storedWidgetIds);
        editor.apply();

        // Write cached storage.
        updateAppWidgetOptionsAndView(AppWidgetManager.getInstance(context), context, appWidgetId,
                tile);
    }

    /** Removes stored data when tile is deleted. */
    public static void removeStorageForTile(Context context, String key, int widgetId) {
    public static void removeSharedPreferencesStorageForTile(Context context, PeopleTileKey key,
            int widgetId) {
        // Delete widgetId mapping to key.
        SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(context);
        SharedPreferences.Editor editor = sp.edit();
        Set<String> storedWidgetIds = new HashSet<>(sp.getStringSet(key, new HashSet<>()));
        Set<String> storedWidgetIds = new HashSet<>(
                sp.getStringSet(key.toString(), new HashSet<>()));
        storedWidgetIds.remove(String.valueOf(widgetId));
        editor.putStringSet(key, storedWidgetIds);
        editor.putStringSet(key.toString(), storedWidgetIds);
        editor.remove(String.valueOf(widgetId));
        editor.apply();

@@ -361,12 +338,12 @@ public class PeopleSpaceUtils {
            Log.w(TAG, "NotificationEntryManager is null");
            return tiles;
        }
        Map<String, NotificationEntry> visibleNotifications = notificationEntryManager
        Map<PeopleTileKey, NotificationEntry> visibleNotifications = notificationEntryManager
                .getVisibleNotifications()
                .stream()
                .filter(entry -> entry.getRanking() != null
                        && entry.getRanking().getConversationShortcutInfo() != null)
                .collect(Collectors.toMap(PeopleSpaceUtils::getKey, e -> e));
                .collect(Collectors.toMap(PeopleTileKey::new, e -> e));
        if (DEBUG) {
            Log.d(TAG, "Number of visible notifications:" + visibleNotifications.size());
        }
@@ -378,16 +355,15 @@ public class PeopleSpaceUtils {
    }

    static PeopleSpaceTile augmentTileFromVisibleNotifications(Context context,
            PeopleSpaceTile tile, Map<String, NotificationEntry> visibleNotifications) {
        String shortcutId = tile.getId();
        String packageName = tile.getPackageName();
        int userId = getUserId(tile);
        String key = getKey(shortcutId, packageName, userId);
            PeopleSpaceTile tile, Map<PeopleTileKey, NotificationEntry> visibleNotifications) {
        PeopleTileKey key = new PeopleTileKey(
                tile.getId(), getUserId(tile), tile.getPackageName());

        if (!visibleNotifications.containsKey(key)) {
            if (DEBUG) Log.d(TAG, "No existing notifications for key:" + key);
            if (DEBUG) Log.d(TAG, "No existing notifications for key:" + key.toString());
            return tile;
        }
        if (DEBUG) Log.d(TAG, "Augmenting tile from visible notifications, key:" + key);
        if (DEBUG) Log.d(TAG, "Augmenting tile from visible notifications, key:" + key.toString());
        return augmentTileFromNotification(context, tile, visibleNotifications.get(key).getSbn());
    }

@@ -423,17 +399,6 @@ public class PeopleSpaceUtils {
                .build();
    }

    private static void updateAppWidgetOptions(AppWidgetManager appWidgetManager, int appWidgetId,
            PeopleSpaceTile tile) {
        if (tile == null) {
            if (DEBUG) Log.d(TAG, "Requested to store null tile");
            return;
        }
        Bundle newOptions = new Bundle();
        newOptions.putParcelable(OPTIONS_PEOPLE_SPACE_TILE, tile);
        appWidgetManager.updateAppWidgetOptions(appWidgetId, newOptions);
    }

    /** Creates a {@link RemoteViews} for {@code tile}. */
    public static RemoteViews createRemoteViews(Context context,
            PeopleSpaceTile tile, int appWidgetId) {
@@ -935,11 +900,15 @@ public class PeopleSpaceUtils {
        removeBirthdayStatusIfPresent(appWidgetManager, context, storedTile, appWidgetId);
    }

    /** Update app widget options and the current view. */
    /** Updates tile in app widget options and the current view. */
    public static void updateAppWidgetOptionsAndView(AppWidgetManager appWidgetManager,
            Context context, int appWidgetId, PeopleSpaceTile tile) {
        updateAppWidgetOptions(appWidgetManager, appWidgetId, tile);
        AppWidgetOptionsHelper.setPeopleTile(appWidgetManager, appWidgetId, tile);

        if (DEBUG) Log.d(TAG, "Widget: " + appWidgetId + ", " + tile.getUserName());
        RemoteViews views = createRemoteViews(context, tile, appWidgetId);

        // Tell the AppWidgetManager to perform an update on the current app widget.
        appWidgetManager.updateAppWidget(appWidgetId, views);
    }

@@ -984,44 +953,6 @@ public class PeopleSpaceUtils {
        return lookupKeysWithBirthdaysToday;
    }

    static String getKey(NotificationEntry entry) {
        if (entry.getRanking() == null || entry.getRanking().getConversationShortcutInfo() == null
                || entry.getSbn() == null || entry.getSbn().getUser() == null) {
            return null;
        }
        return getKey(entry.getRanking().getConversationShortcutInfo().getId(),
                entry.getSbn().getPackageName(),
                entry.getSbn().getUser().getIdentifier());
    }

    /**
     * Returns the uniquely identifying key for the conversation.
     *
     * <p>{@code userId} will always be a number, so we put user ID as the
     * delimiter between the app-provided strings of shortcut ID and package name.
     *
     * <p>There aren't restrictions on shortcut ID characters, but there are restrictions requiring
     * a {@code packageName} to always start with a letter. This restriction means we are
     * guaranteed to avoid cases like "a/b/0/0/package.name" having two potential keys, as the first
     * case is impossible given the package name restrictions:
     * <ul>
     *     <li>"a/b" + "/" + 0 + "/" + "0/packageName"</li>
     *     <li>"a/b/0" + "/" + 0 + "/" + "packageName"</li>
     * </ul>
     */
    @Nullable
    public static String getKey(String shortcutId, String packageName, int userId) {
        if (!validKey(shortcutId, packageName, userId)) {
            return null;
        }
        return shortcutId + "/" + userId + "/" + packageName;
    }

    /** Returns whether the key is valid. */
    public static boolean validKey(String shortcutId, String packageName, int userId) {
        return !TextUtils.isEmpty(shortcutId) && !TextUtils.isEmpty(packageName) && userId >= 0;
    }

    /** Returns the userId associated with a {@link PeopleSpaceTile} */
    public static int getUserId(PeopleSpaceTile tile) {
        return tile.getUserHandle().getIdentifier();
+91 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2021 The Android Open Source Project
 *
 * 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.android.systemui.people.widget;

import static com.android.systemui.people.PeopleSpaceUtils.DEBUG;
import static com.android.systemui.people.PeopleSpaceUtils.EMPTY_KEY;
import static com.android.systemui.people.PeopleSpaceUtils.EMPTY_STRING;
import static com.android.systemui.people.PeopleSpaceUtils.INVALID_USER_ID;
import static com.android.systemui.people.PeopleSpaceUtils.PACKAGE_NAME;
import static com.android.systemui.people.PeopleSpaceUtils.SHORTCUT_ID;
import static com.android.systemui.people.PeopleSpaceUtils.USER_ID;

import android.app.people.PeopleSpaceTile;
import android.appwidget.AppWidgetManager;
import android.os.Bundle;
import android.util.Log;

/** Helper class encapsulating AppWidgetOptions for People Tile. */
public class AppWidgetOptionsHelper {
    private static final String TAG = "AppWidgetOptionsHelper";

    /** Key to store {@link PeopleSpaceTile} in AppWidgetOptions Bundle. */
    public static final String OPTIONS_PEOPLE_TILE = "options_people_tile";

    /** Sets {@link PeopleSpaceTile} in AppWidgetOptions. */
    public static void setPeopleTile(AppWidgetManager appWidgetManager, int appWidgetId,
            PeopleSpaceTile tile) {
        if (tile == null) {
            if (DEBUG) Log.d(TAG, "Requested to store null tile");
            return;
        }
        Bundle options = appWidgetManager.getAppWidgetOptions(appWidgetId);
        options.putParcelable(OPTIONS_PEOPLE_TILE, tile);
        appWidgetManager.updateAppWidgetOptions(appWidgetId, options);
    }

    /** Gets {@link PeopleSpaceTile} from AppWidgetOptions. */
    public static PeopleSpaceTile getPeopleTile(AppWidgetManager appWidgetManager,
            int appWidgetId) {
        Bundle options = appWidgetManager.getAppWidgetOptions(appWidgetId);
        return options != null ? options.getParcelable(OPTIONS_PEOPLE_TILE) : null;
    }

    /** Sets {@link PeopleTileKey} in AppWidgetOptions. */
    public static void setPeopleTileKey(AppWidgetManager appWidgetManager, int appWidgetId,
            PeopleTileKey key) {
        Bundle options = appWidgetManager.getAppWidgetOptions(appWidgetId);
        options.putString(SHORTCUT_ID, key.getShortcutId());
        options.putInt(USER_ID, key.getUserId());
        options.putString(PACKAGE_NAME, key.getPackageName());
        appWidgetManager.updateAppWidgetOptions(appWidgetId, options);
    }

    /** Gets {@link PeopleTileKey} from AppWidgetOptions. */
    public static PeopleTileKey getPeopleTileKey(AppWidgetManager appWidgetManager,
            int appWidgetId) {
        Bundle options = appWidgetManager.getAppWidgetOptions(appWidgetId);
        if (options == null) {
            return EMPTY_KEY;
        }
        return getPeopleTileKeyFromBundle(options);
    }

    /** Gets {@link PeopleTileKey} from Bundle {@code options}. */
    public static PeopleTileKey getPeopleTileKeyFromBundle(Bundle options) {
        String pkg = options.getString(PACKAGE_NAME, EMPTY_STRING);
        int userId = options.getInt(USER_ID, INVALID_USER_ID);
        String shortcutId = options.getString(SHORTCUT_ID, EMPTY_STRING);
        return new PeopleTileKey(shortcutId, userId, pkg);
    }

    /** Removes {@link PeopleTileKey} from AppWidgetOptions. */
    public static void removePeopleTileKey(AppWidgetManager appWidgetManager,
            int appWidgetId) {
        setPeopleTileKey(appWidgetManager, appWidgetId, EMPTY_KEY);
    }
}
+72 −55

File changed.

Preview size limit exceeded, changes collapsed.

+15 −5
Original line number Diff line number Diff line
@@ -22,6 +22,7 @@ import android.app.people.PeopleManager;
import android.appwidget.AppWidgetManager;
import android.appwidget.AppWidgetProvider;
import android.content.Context;
import android.os.Bundle;
import android.util.Log;

import com.android.internal.annotations.VisibleForTesting;
@@ -67,18 +68,21 @@ public class PeopleSpaceWidgetProvider extends AppWidgetProvider {
        ensurePeopleSpaceWidgetManagerInitialized(context);
        peopleSpaceWidgetManager.updateWidgets(appWidgetIds);
        for (int appWidgetId : appWidgetIds) {
            if (DEBUG) Log.d(TAG, "Ensure listener is registered for widget: " + appWidgetId);
            PeopleSpaceWidgetProvider.TileConversationListener
                    newListener = new PeopleSpaceWidgetProvider.TileConversationListener();
            peopleSpaceWidgetManager.registerConversationListenerIfNeeded(appWidgetId,
                    newListener);
        }
        return;
    }

    private void ensurePeopleSpaceWidgetManagerInitialized(Context context) {
        if (peopleSpaceWidgetManager == null) {
            peopleSpaceWidgetManager = new PeopleSpaceWidgetManager(context);
        }
    /** Called when widget updates. */
    @Override
    public void onAppWidgetOptionsChanged(Context context, AppWidgetManager appWidgetManager,
            int appWidgetId, Bundle newOptions) {
        super.onAppWidgetOptionsChanged(context, appWidgetManager, appWidgetId, newOptions);
        ensurePeopleSpaceWidgetManagerInitialized(context);
        peopleSpaceWidgetManager.onAppWidgetOptionsChanged(appWidgetId, newOptions);
    }

    @Override
@@ -88,6 +92,12 @@ public class PeopleSpaceWidgetProvider extends AppWidgetProvider {
        peopleSpaceWidgetManager.deleteWidgets(appWidgetIds);
    }

    private void ensurePeopleSpaceWidgetManagerInitialized(Context context) {
        if (peopleSpaceWidgetManager == null) {
            peopleSpaceWidgetManager = new PeopleSpaceWidgetManager(context);
        }
    }

    @VisibleForTesting
    public void setPeopleSpaceWidgetManager(PeopleSpaceWidgetManager manager) {
        peopleSpaceWidgetManager = manager;
Loading