1 /* 2 * Copyright (C) 2014 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.launcher3.widget; 18 19 import static com.android.launcher3.BuildConfig.WIDGETS_ENABLED; 20 21 import android.annotation.TargetApi; 22 import android.appwidget.AppWidgetManager; 23 import android.appwidget.AppWidgetProviderInfo; 24 import android.content.ComponentName; 25 import android.content.Context; 26 import android.os.Build; 27 import android.os.Bundle; 28 import android.os.UserHandle; 29 import android.util.Log; 30 import android.widget.RemoteViews; 31 32 import androidx.annotation.NonNull; 33 import androidx.annotation.Nullable; 34 import androidx.annotation.RequiresApi; 35 import androidx.annotation.VisibleForTesting; 36 37 import com.android.launcher3.logging.FileLog; 38 import com.android.launcher3.model.data.LauncherAppWidgetInfo; 39 import com.android.launcher3.pm.UserCache; 40 import com.android.launcher3.util.PackageUserKey; 41 import com.android.launcher3.widget.custom.CustomWidgetManager; 42 43 import java.util.Collections; 44 import java.util.List; 45 import java.util.stream.Collectors; 46 import java.util.stream.Stream; 47 48 /** 49 * Utility class to working with {@link AppWidgetManager} 50 */ 51 public class WidgetManagerHelper { 52 53 private static final String TAG = "WidgetManagerHelper"; 54 //TODO: replace this with OPTION_APPWIDGET_RESTORE_COMPLETED b/63667276 55 public static final String WIDGET_OPTION_RESTORE_COMPLETED = "appWidgetRestoreCompleted"; 56 57 final AppWidgetManager mAppWidgetManager; 58 final Context mContext; 59 WidgetManagerHelper(Context context)60 public WidgetManagerHelper(Context context) { 61 this(context, AppWidgetManager.getInstance(context)); 62 } 63 64 @VisibleForTesting WidgetManagerHelper(Context context, AppWidgetManager appWidgetManager)65 public WidgetManagerHelper(Context context, AppWidgetManager appWidgetManager) { 66 mContext = context; 67 mAppWidgetManager = appWidgetManager; 68 } 69 70 /** 71 * @see AppWidgetManager#getAppWidgetInfo(int) 72 */ 73 @Nullable getLauncherAppWidgetInfo( int appWidgetId, ComponentName componentName)74 public LauncherAppWidgetProviderInfo getLauncherAppWidgetInfo( 75 int appWidgetId, ComponentName componentName) { 76 77 // For custom widgets. 78 if (appWidgetId <= LauncherAppWidgetInfo.CUSTOM_WIDGET_ID) { 79 return CustomWidgetManager.INSTANCE.get(mContext).getWidgetProvider(componentName); 80 } 81 82 AppWidgetProviderInfo info = mAppWidgetManager.getAppWidgetInfo(appWidgetId); 83 if (info == null) { 84 FileLog.w(TAG, 85 "getLauncherAppWidgetInfo: AppWidgetManager returned null AppWidgetInfo for" 86 + " appWidgetId=" + appWidgetId 87 + ", componentName=" + componentName); 88 return null; 89 } 90 return LauncherAppWidgetProviderInfo.fromProviderInfo(mContext, info); 91 } 92 93 /** 94 * @see AppWidgetManager#getInstalledProvidersForPackage(String, UserHandle) 95 */ 96 @TargetApi(Build.VERSION_CODES.O) getAllProviders(@ullable PackageUserKey packageUser)97 public List<AppWidgetProviderInfo> getAllProviders(@Nullable PackageUserKey packageUser) { 98 if (!WIDGETS_ENABLED) { 99 return Collections.emptyList(); 100 } 101 102 if (packageUser == null) { 103 return allWidgetsSteam(mContext).collect(Collectors.toList()); 104 } 105 106 try { 107 return mAppWidgetManager.getInstalledProvidersForPackage( 108 packageUser.mPackageName, packageUser.mUser); 109 } catch (IllegalStateException e) { 110 // b/277189566: Launcher will load the widget when it gets the user-unlock event. 111 // If exception is thrown because of device is locked, it means a race condition occurs 112 // that the user got locked again while launcher is processing the event. In this case 113 // we should return empty list. 114 Log.e(TAG, "getAllProviders: Error getting installed providers for" 115 + " package=" + packageUser.mPackageName, e); 116 return Collections.emptyList(); 117 } 118 } 119 120 /** 121 * @see AppWidgetManager#bindAppWidgetIdIfAllowed(int, UserHandle, ComponentName, Bundle) 122 */ bindAppWidgetIdIfAllowed(int appWidgetId, AppWidgetProviderInfo info, Bundle options)123 public boolean bindAppWidgetIdIfAllowed(int appWidgetId, AppWidgetProviderInfo info, 124 Bundle options) { 125 if (!WIDGETS_ENABLED) { 126 return false; 127 } 128 if (appWidgetId <= LauncherAppWidgetInfo.CUSTOM_WIDGET_ID) { 129 return true; 130 } 131 return mAppWidgetManager.bindAppWidgetIdIfAllowed( 132 appWidgetId, info.getProfile(), info.provider, options); 133 } 134 findProvider(ComponentName provider, UserHandle user)135 public LauncherAppWidgetProviderInfo findProvider(ComponentName provider, UserHandle user) { 136 if (!WIDGETS_ENABLED) { 137 return null; 138 } 139 for (AppWidgetProviderInfo info : 140 getAllProviders(new PackageUserKey(provider.getPackageName(), user))) { 141 if (info.provider.equals(provider)) { 142 return LauncherAppWidgetProviderInfo.fromProviderInfo(mContext, info); 143 } 144 } 145 Log.w(TAG, "findProvider: No App Widget Provider found for component=" + provider 146 + " user=" + user); 147 return null; 148 } 149 150 /** 151 * Returns if a AppWidgetProvider has marked a widget restored 152 */ isAppWidgetRestored(int appWidgetId)153 public boolean isAppWidgetRestored(int appWidgetId) { 154 return WIDGETS_ENABLED && mAppWidgetManager.getAppWidgetOptions(appWidgetId) 155 .getBoolean(WIDGET_OPTION_RESTORE_COMPLETED); 156 } 157 158 159 /** 160 * Load RemoteViews preview for this provider if available. 161 * 162 * @param info The provider info for the widget you want to preview. 163 * @param widgetCategory The widget category for which you want to display previews. 164 * @return Returns the widget preview that matches selected category, if available. 165 */ 166 @Nullable 167 @RequiresApi(Build.VERSION_CODES.VANILLA_ICE_CREAM) loadGeneratedPreview(@onNull AppWidgetProviderInfo info, int widgetCategory)168 public RemoteViews loadGeneratedPreview(@NonNull AppWidgetProviderInfo info, 169 int widgetCategory) { 170 if (!android.appwidget.flags.Flags.generatedPreviews()) return null; 171 return mAppWidgetManager.getWidgetPreview(info.provider, info.getProfile(), widgetCategory); 172 } 173 allWidgetsSteam(Context context)174 private static Stream<AppWidgetProviderInfo> allWidgetsSteam(Context context) { 175 AppWidgetManager awm = context.getSystemService(AppWidgetManager.class); 176 return Stream.concat( 177 UserCache.INSTANCE.get(context) 178 .getUserProfiles() 179 .stream() 180 .flatMap(u -> awm.getInstalledProvidersForProfile(u).stream()), 181 CustomWidgetManager.INSTANCE.get(context).stream()); 182 } 183 } 184