1 /* 2 * Copyright (C) 2019 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.custom; 18 19 import static com.android.launcher3.widget.LauncherAppWidgetProviderInfo.CLS_CUSTOM_WIDGET_PREFIX; 20 21 import android.appwidget.AppWidgetManager; 22 import android.appwidget.AppWidgetProviderInfo; 23 import android.content.ComponentName; 24 import android.content.Context; 25 import android.os.Parcel; 26 import android.os.Process; 27 import android.util.SparseArray; 28 29 import androidx.annotation.NonNull; 30 import androidx.annotation.Nullable; 31 32 import com.android.launcher3.model.data.LauncherAppWidgetInfo; 33 import com.android.launcher3.uioverrides.plugins.PluginManagerWrapper; 34 import com.android.launcher3.util.MainThreadInitializedObject; 35 import com.android.launcher3.util.PackageUserKey; 36 import com.android.launcher3.widget.LauncherAppWidgetHostView; 37 import com.android.launcher3.widget.LauncherAppWidgetProviderInfo; 38 import com.android.systemui.plugins.CustomWidgetPlugin; 39 import com.android.systemui.plugins.PluginListener; 40 41 import java.util.ArrayList; 42 import java.util.List; 43 import java.util.function.Consumer; 44 import java.util.stream.Stream; 45 46 /** 47 * CustomWidgetManager handles custom widgets implemented as a plugin. 48 */ 49 public class CustomWidgetManager implements PluginListener<CustomWidgetPlugin> { 50 51 public static final MainThreadInitializedObject<CustomWidgetManager> INSTANCE = 52 new MainThreadInitializedObject<>(CustomWidgetManager::new); 53 54 private final Context mContext; 55 /** 56 * auto provider Id is an ever-increasing number that serves as the providerId whenever a new 57 * custom widget has been connected. 58 */ 59 private int mAutoProviderId = 0; 60 private final SparseArray<CustomWidgetPlugin> mPlugins; 61 private final List<CustomAppWidgetProviderInfo> mCustomWidgets; 62 private final SparseArray<ComponentName> mWidgetsIdMap; 63 private Consumer<PackageUserKey> mWidgetRefreshCallback; 64 CustomWidgetManager(Context context)65 private CustomWidgetManager(Context context) { 66 mContext = context; 67 mPlugins = new SparseArray<>(); 68 mCustomWidgets = new ArrayList<>(); 69 mWidgetsIdMap = new SparseArray<>(); 70 PluginManagerWrapper.INSTANCE.get(context) 71 .addPluginListener(this, CustomWidgetPlugin.class, true); 72 } 73 onDestroy()74 public void onDestroy() { 75 PluginManagerWrapper.INSTANCE.get(mContext).removePluginListener(this); 76 } 77 78 @Override onPluginConnected(CustomWidgetPlugin plugin, Context context)79 public void onPluginConnected(CustomWidgetPlugin plugin, Context context) { 80 mPlugins.put(mAutoProviderId, plugin); 81 List<AppWidgetProviderInfo> providers = AppWidgetManager.getInstance(context) 82 .getInstalledProvidersForProfile(Process.myUserHandle()); 83 if (providers.isEmpty()) return; 84 Parcel parcel = Parcel.obtain(); 85 providers.get(0).writeToParcel(parcel, 0); 86 parcel.setDataPosition(0); 87 CustomAppWidgetProviderInfo info = newInfo(mAutoProviderId, plugin, parcel, context); 88 parcel.recycle(); 89 mCustomWidgets.add(info); 90 mWidgetsIdMap.put(mAutoProviderId, info.provider); 91 mWidgetRefreshCallback.accept(null); 92 mAutoProviderId++; 93 } 94 95 @Override onPluginDisconnected(CustomWidgetPlugin plugin)96 public void onPluginDisconnected(CustomWidgetPlugin plugin) { 97 int providerId = findProviderId(plugin); 98 if (providerId == -1) return; 99 mPlugins.remove(providerId); 100 mCustomWidgets.remove(getWidgetProvider(providerId)); 101 mWidgetsIdMap.remove(providerId); 102 } 103 104 /** 105 * Inject a callback function to refresh the widgets. 106 */ setWidgetRefreshCallback(Consumer<PackageUserKey> cb)107 public void setWidgetRefreshCallback(Consumer<PackageUserKey> cb) { 108 mWidgetRefreshCallback = cb; 109 } 110 111 /** 112 * Callback method to inform a plugin it's corresponding widget has been created. 113 */ onViewCreated(LauncherAppWidgetHostView view)114 public void onViewCreated(LauncherAppWidgetHostView view) { 115 CustomAppWidgetProviderInfo info = (CustomAppWidgetProviderInfo) view.getAppWidgetInfo(); 116 CustomWidgetPlugin plugin = mPlugins.get(info.providerId); 117 if (plugin == null) return; 118 plugin.onViewCreated(view); 119 } 120 121 /** 122 * Returns the stream of custom widgets. 123 */ 124 @NonNull stream()125 public Stream<CustomAppWidgetProviderInfo> stream() { 126 return mCustomWidgets.stream(); 127 } 128 129 /** 130 * Returns the widget id for a specific provider. 131 */ getWidgetIdForCustomProvider(@onNull ComponentName provider)132 public int getWidgetIdForCustomProvider(@NonNull ComponentName provider) { 133 int index = mWidgetsIdMap.indexOfValue(provider); 134 if (index >= 0) { 135 return LauncherAppWidgetInfo.CUSTOM_WIDGET_ID - mWidgetsIdMap.keyAt(index); 136 } else { 137 return AppWidgetManager.INVALID_APPWIDGET_ID; 138 } 139 } 140 141 /** 142 * Returns the widget provider in respect to given widget id. 143 */ 144 @Nullable getWidgetProvider(int widgetId)145 public LauncherAppWidgetProviderInfo getWidgetProvider(int widgetId) { 146 ComponentName cn = mWidgetsIdMap.get(LauncherAppWidgetInfo.CUSTOM_WIDGET_ID - widgetId); 147 for (LauncherAppWidgetProviderInfo info : mCustomWidgets) { 148 if (info.provider.equals(cn)) return info; 149 } 150 return null; 151 } 152 newInfo(int providerId, CustomWidgetPlugin plugin, Parcel parcel, Context context)153 private static CustomAppWidgetProviderInfo newInfo(int providerId, CustomWidgetPlugin plugin, 154 Parcel parcel, Context context) { 155 CustomAppWidgetProviderInfo info = new CustomAppWidgetProviderInfo( 156 parcel, false, providerId); 157 info.provider = new ComponentName( 158 context.getPackageName(), CLS_CUSTOM_WIDGET_PREFIX + providerId); 159 160 info.label = plugin.getLabel(); 161 info.resizeMode = plugin.getResizeMode(); 162 163 info.spanX = plugin.getSpanX(); 164 info.spanY = plugin.getSpanY(); 165 info.minSpanX = plugin.getMinSpanX(); 166 info.minSpanY = plugin.getMinSpanY(); 167 return info; 168 } 169 findProviderId(CustomWidgetPlugin plugin)170 private int findProviderId(CustomWidgetPlugin plugin) { 171 for (int i = 0; i < mPlugins.size(); i++) { 172 int providerId = mPlugins.keyAt(i); 173 if (mPlugins.get(providerId) == plugin) { 174 return providerId; 175 } 176 } 177 return -1; 178 } 179 } 180