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