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