1 /* 2 * Copyright (C) 2017 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 package com.android.launcher3.widget.custom; 17 18 import android.appwidget.AppWidgetManager; 19 import android.appwidget.AppWidgetProviderInfo; 20 import android.content.ComponentName; 21 import android.content.Context; 22 import android.content.res.TypedArray; 23 import android.content.res.XmlResourceParser; 24 import android.os.Parcel; 25 import android.os.Process; 26 import android.util.SparseArray; 27 import android.util.Xml; 28 29 import com.android.launcher3.LauncherAppWidgetInfo; 30 import com.android.launcher3.LauncherAppWidgetProviderInfo; 31 import com.android.launcher3.R; 32 33 import org.xmlpull.v1.XmlPullParser; 34 import org.xmlpull.v1.XmlPullParserException; 35 36 import java.io.IOException; 37 import java.util.ArrayList; 38 import java.util.List; 39 40 import static com.android.launcher3.LauncherAppWidgetProviderInfo.CLS_CUSTOM_WIDGET_PREFIX; 41 42 /** 43 * Utility class to parse {@ink CustomAppWidgetProviderInfo} definitions from xml 44 */ 45 public class CustomWidgetParser { 46 47 private static List<LauncherAppWidgetProviderInfo> sCustomWidgets; 48 private static SparseArray<ComponentName> sWidgetsIdMap; 49 getCustomWidgets(Context context)50 public static List<LauncherAppWidgetProviderInfo> getCustomWidgets(Context context) { 51 if (sCustomWidgets == null) { 52 // Synchronization not needed as it it safe to load multiple times 53 parseCustomWidgets(context); 54 } 55 56 return sCustomWidgets; 57 } 58 getWidgetIdForCustomProvider(Context context, ComponentName provider)59 public static int getWidgetIdForCustomProvider(Context context, ComponentName provider) { 60 if (sWidgetsIdMap == null) { 61 parseCustomWidgets(context); 62 } 63 int index = sWidgetsIdMap.indexOfValue(provider); 64 if (index >= 0) { 65 return LauncherAppWidgetInfo.CUSTOM_WIDGET_ID - sWidgetsIdMap.keyAt(index); 66 } else { 67 return AppWidgetManager.INVALID_APPWIDGET_ID; 68 } 69 } 70 getWidgetProvider(Context context, int widgetId)71 public static LauncherAppWidgetProviderInfo getWidgetProvider(Context context, int widgetId) { 72 if (sWidgetsIdMap == null || sCustomWidgets == null) { 73 parseCustomWidgets(context); 74 } 75 ComponentName cn = sWidgetsIdMap.get(LauncherAppWidgetInfo.CUSTOM_WIDGET_ID - widgetId); 76 for (LauncherAppWidgetProviderInfo info : sCustomWidgets) { 77 if (info.provider.equals(cn)) { 78 return info; 79 } 80 } 81 return null; 82 } 83 parseCustomWidgets(Context context)84 private static void parseCustomWidgets(Context context) { 85 ArrayList<LauncherAppWidgetProviderInfo> widgets = new ArrayList<>(); 86 SparseArray<ComponentName> idMap = new SparseArray<>(); 87 88 List<AppWidgetProviderInfo> providers = AppWidgetManager.getInstance(context) 89 .getInstalledProvidersForProfile(Process.myUserHandle()); 90 if (providers.isEmpty()) { 91 sCustomWidgets = widgets; 92 sWidgetsIdMap = idMap; 93 return; 94 } 95 96 Parcel parcel = Parcel.obtain(); 97 providers.get(0).writeToParcel(parcel, 0); 98 99 try (XmlResourceParser parser = context.getResources().getXml(R.xml.custom_widgets)) { 100 final int depth = parser.getDepth(); 101 int type; 102 103 while (((type = parser.next()) != XmlPullParser.END_TAG || 104 parser.getDepth() > depth) && type != XmlPullParser.END_DOCUMENT) { 105 if ((type == XmlPullParser.START_TAG) && "widget".equals(parser.getName())) { 106 TypedArray a = context.obtainStyledAttributes( 107 Xml.asAttributeSet(parser), R.styleable.CustomAppWidgetProviderInfo); 108 109 parcel.setDataPosition(0); 110 CustomAppWidgetProviderInfo info = newInfo(a, parcel, context); 111 widgets.add(info); 112 a.recycle(); 113 114 idMap.put(info.providerId, info.provider); 115 } 116 } 117 } catch (IOException | XmlPullParserException e) { 118 throw new RuntimeException(e); 119 } 120 parcel.recycle(); 121 sCustomWidgets = widgets; 122 sWidgetsIdMap = idMap; 123 } 124 newInfo(TypedArray a, Parcel parcel, Context context)125 private static CustomAppWidgetProviderInfo newInfo(TypedArray a, Parcel parcel, Context context) { 126 int providerId = a.getInt(R.styleable.CustomAppWidgetProviderInfo_providerId, 0); 127 CustomAppWidgetProviderInfo info = new CustomAppWidgetProviderInfo(parcel, false, providerId); 128 info.provider = new ComponentName(context.getPackageName(), CLS_CUSTOM_WIDGET_PREFIX + providerId); 129 130 info.label = a.getString(R.styleable.CustomAppWidgetProviderInfo_android_label); 131 info.initialLayout = a.getResourceId(R.styleable.CustomAppWidgetProviderInfo_android_initialLayout, 0); 132 info.icon = a.getResourceId(R.styleable.CustomAppWidgetProviderInfo_android_icon, 0); 133 info.previewImage = a.getResourceId(R.styleable.CustomAppWidgetProviderInfo_android_previewImage, 0); 134 info.resizeMode = a.getInt(R.styleable.CustomAppWidgetProviderInfo_android_resizeMode, 0); 135 136 info.spanX = a.getInt(R.styleable.CustomAppWidgetProviderInfo_numColumns, 1); 137 info.spanY = a.getInt(R.styleable.CustomAppWidgetProviderInfo_numRows, 1); 138 info.minSpanX = a.getInt(R.styleable.CustomAppWidgetProviderInfo_numMinColumns, 1); 139 info.minSpanY = a.getInt(R.styleable.CustomAppWidgetProviderInfo_numMinRows, 1); 140 return info; 141 } 142 } 143