10fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal/* 20fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal * Copyright (C) 2014 The Android Open Source Project 30fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal * 40fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal * Licensed under the Apache License, Version 2.0 (the "License"); 50fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal * you may not use this file except in compliance with the License. 60fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal * You may obtain a copy of the License at 70fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal * 80fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal * http://www.apache.org/licenses/LICENSE-2.0 90fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal * 100fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal * Unless required by applicable law or agreed to in writing, software 110fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal * distributed under the License is distributed on an "AS IS" BASIS, 120fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 130fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal * See the License for the specific language governing permissions and 140fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal * limitations under the License. 150fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal */ 160fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal 170fe505bf82a265e51c556d7204976651cde7f55cSunny Goyalpackage com.android.launcher3; 180fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal 190fe505bf82a265e51c556d7204976651cde7f55cSunny Goyalimport android.appwidget.AppWidgetHost; 200fe505bf82a265e51c556d7204976651cde7f55cSunny Goyalimport android.appwidget.AppWidgetManager; 210fe505bf82a265e51c556d7204976651cde7f55cSunny Goyalimport android.content.ComponentName; 220fe505bf82a265e51c556d7204976651cde7f55cSunny Goyalimport android.content.ContentValues; 230fe505bf82a265e51c556d7204976651cde7f55cSunny Goyalimport android.content.Context; 240fe505bf82a265e51c556d7204976651cde7f55cSunny Goyalimport android.content.Intent; 250fe505bf82a265e51c556d7204976651cde7f55cSunny Goyalimport android.content.pm.ActivityInfo; 260fe505bf82a265e51c556d7204976651cde7f55cSunny Goyalimport android.content.pm.PackageManager; 270fe505bf82a265e51c556d7204976651cde7f55cSunny Goyalimport android.content.res.Resources; 280fe505bf82a265e51c556d7204976651cde7f55cSunny Goyalimport android.content.res.XmlResourceParser; 290fe505bf82a265e51c556d7204976651cde7f55cSunny Goyalimport android.database.sqlite.SQLiteDatabase; 300fe505bf82a265e51c556d7204976651cde7f55cSunny Goyalimport android.graphics.drawable.Drawable; 310fe505bf82a265e51c556d7204976651cde7f55cSunny Goyalimport android.net.Uri; 320fe505bf82a265e51c556d7204976651cde7f55cSunny Goyalimport android.os.Bundle; 330fe505bf82a265e51c556d7204976651cde7f55cSunny Goyalimport android.text.TextUtils; 340fe505bf82a265e51c556d7204976651cde7f55cSunny Goyalimport android.util.Log; 350fe505bf82a265e51c556d7204976651cde7f55cSunny Goyalimport android.util.Pair; 360fe505bf82a265e51c556d7204976651cde7f55cSunny Goyalimport android.util.Patterns; 370fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal 380fe505bf82a265e51c556d7204976651cde7f55cSunny Goyalimport com.android.launcher3.LauncherProvider.SqlArguments; 390fe505bf82a265e51c556d7204976651cde7f55cSunny Goyalimport com.android.launcher3.LauncherProvider.WorkspaceLoader; 400fe505bf82a265e51c556d7204976651cde7f55cSunny Goyalimport com.android.launcher3.LauncherSettings.Favorites; 410fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal 420fe505bf82a265e51c556d7204976651cde7f55cSunny Goyalimport org.xmlpull.v1.XmlPullParser; 430fe505bf82a265e51c556d7204976651cde7f55cSunny Goyalimport org.xmlpull.v1.XmlPullParserException; 440fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal 450fe505bf82a265e51c556d7204976651cde7f55cSunny Goyalimport java.io.IOException; 460fe505bf82a265e51c556d7204976651cde7f55cSunny Goyalimport java.util.ArrayList; 470fe505bf82a265e51c556d7204976651cde7f55cSunny Goyalimport java.util.HashMap; 480fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal 490fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal/** 500fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal * This class contains contains duplication of functionality as found in 510fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal * LauncherProvider#DatabaseHelper. It has been isolated and differentiated in order 520fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal * to cleanly and separately represent AutoInstall default layout format and policy. 530fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal */ 540fe505bf82a265e51c556d7204976651cde7f55cSunny Goyalpublic class AutoInstallsLayout implements WorkspaceLoader { 550fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal private static final String TAG = "AutoInstalls"; 560fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal private static final boolean LOGD = true; 570fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal 580fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal /** Marker action used to discover a package which defines launcher customization */ 590fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal static final String ACTION_LAUNCHER_CUSTOMIZATION = 602233c8850d521defa82e358e3c5bae3ec75df426Sunny Goyal "android.autoinstalls.config.action.PLAY_AUTO_INSTALL"; 610fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal 620fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal private static final String LAYOUT_RES = "default_layout"; 630fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal 640fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal static AutoInstallsLayout get(Context context, AppWidgetHost appWidgetHost, 650fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal LayoutParserCallback callback) { 660fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal Pair<String, Resources> customizationApkInfo = Utilities.findSystemApk( 670fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal ACTION_LAUNCHER_CUSTOMIZATION, context.getPackageManager()); 680fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal if (customizationApkInfo == null) { 690fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal return null; 700fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal } 710fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal 720fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal String pkg = customizationApkInfo.first; 730fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal Resources res = customizationApkInfo.second; 740fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal int layoutId = res.getIdentifier(LAYOUT_RES, "xml", pkg); 750fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal if (layoutId == 0) { 760fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal Log.e(TAG, "Layout definition not found in package: " + pkg); 770fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal return null; 780fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal } 790fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal return new AutoInstallsLayout(context, appWidgetHost, callback, pkg, res, layoutId); 800fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal } 810fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal 820fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal // Object Tags 830fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal private static final String TAG_WORKSPACE = "workspace"; 840fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal private static final String TAG_APP_ICON = "appicon"; 850fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal private static final String TAG_AUTO_INSTALL = "autoinstall"; 860fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal private static final String TAG_FOLDER = "folder"; 870fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal private static final String TAG_APPWIDGET = "appwidget"; 880fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal private static final String TAG_SHORTCUT = "shortcut"; 890fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal private static final String TAG_EXTRA = "extra"; 900fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal 910fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal private static final String ATTR_CONTAINER = "container"; 920fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal private static final String ATTR_RANK = "rank"; 930fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal 940fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal private static final String ATTR_PACKAGE_NAME = "packageName"; 950fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal private static final String ATTR_CLASS_NAME = "className"; 960fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal private static final String ATTR_TITLE = "title"; 970fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal private static final String ATTR_SCREEN = "screen"; 980fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal private static final String ATTR_X = "x"; 990fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal private static final String ATTR_Y = "y"; 1000fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal private static final String ATTR_SPAN_X = "spanX"; 1010fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal private static final String ATTR_SPAN_Y = "spanY"; 1020fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal private static final String ATTR_ICON = "icon"; 1030fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal private static final String ATTR_URL = "url"; 1040fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal 1050fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal // Style attrs -- "Extra" 1060fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal private static final String ATTR_KEY = "key"; 1070fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal private static final String ATTR_VALUE = "value"; 1080fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal 1090fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal private static final String HOTSEAT_CONTAINER_NAME = 1100fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal Favorites.containerToString(Favorites.CONTAINER_HOTSEAT); 1110fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal 1120fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal private static final String ACTION_APPWIDGET_DEFAULT_WORKSPACE_CONFIGURE = 1130fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal "com.android.launcher.action.APPWIDGET_DEFAULT_WORKSPACE_CONFIGURE"; 1140fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal 1150fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal private final Context mContext; 1160fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal private final AppWidgetHost mAppWidgetHost; 1170fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal private final LayoutParserCallback mCallback; 1180fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal 1190fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal private final PackageManager mPackageManager; 1200fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal private final ContentValues mValues; 1210fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal 1220fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal private final Resources mRes; 1230fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal private final int mLayoutId; 1240fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal 1250fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal private SQLiteDatabase mDb; 1260fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal 1270fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal public AutoInstallsLayout(Context context, AppWidgetHost appWidgetHost, 1280fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal LayoutParserCallback callback, String packageName, Resources res, int layoutId) { 1290fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal mContext = context; 1300fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal mAppWidgetHost = appWidgetHost; 1310fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal mCallback = callback; 1320fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal 1330fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal mPackageManager = context.getPackageManager(); 1340fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal mValues = new ContentValues(); 1350fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal 1360fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal mRes = res; 1370fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal mLayoutId = layoutId; 1380fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal } 1390fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal 1400fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal @Override 1410fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal public int loadLayout(SQLiteDatabase db, ArrayList<Long> screenIds) { 1420fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal mDb = db; 1430fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal try { 1440fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal return parseLayout(mRes, mLayoutId, screenIds); 1450fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal } catch (XmlPullParserException | IOException | RuntimeException e) { 1460fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal Log.w(TAG, "Got exception parsing layout.", e); 1470fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal return -1; 1480fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal } 1490fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal } 1500fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal 1510fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal private int parseLayout(Resources res, int layoutId, ArrayList<Long> screenIds) 1520fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal throws XmlPullParserException, IOException { 1530fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal final int hotseatAllAppsRank = LauncherAppState.getInstance() 1540fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal .getDynamicGrid().getDeviceProfile().hotseatAllAppsRank; 1550fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal 1560fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal XmlResourceParser parser = res.getXml(layoutId); 1570fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal beginDocument(parser, TAG_WORKSPACE); 1580fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal final int depth = parser.getDepth(); 1590fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal int type; 1600fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal HashMap<String, TagParser> tagParserMap = getLayoutElementsMap(); 1610fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal int count = 0; 1620fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal 1630fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal while (((type = parser.next()) != XmlPullParser.END_TAG || 1640fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal parser.getDepth() > depth) && type != XmlPullParser.END_DOCUMENT) { 1650fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal if (type != XmlPullParser.START_TAG) { 1660fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal continue; 1670fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal } 1680fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal 1690fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal mValues.clear(); 1700fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal final int container; 1710fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal final long screenId; 1720fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal 1730fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal if (HOTSEAT_CONTAINER_NAME.equals(getAttributeValue(parser, ATTR_CONTAINER))) { 1740fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal container = Favorites.CONTAINER_HOTSEAT; 1750fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal 1760fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal // Hack: hotseat items are stored using screen ids 1770fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal long rank = Long.parseLong(getAttributeValue(parser, ATTR_RANK)); 1780fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal screenId = (rank < hotseatAllAppsRank) ? rank : (rank + 1); 1790fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal 1800fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal } else { 1810fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal container = Favorites.CONTAINER_DESKTOP; 1820fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal screenId = Long.parseLong(getAttributeValue(parser, ATTR_SCREEN)); 1830fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal 1840fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal mValues.put(Favorites.CELLX, getAttributeValue(parser, ATTR_X)); 1850fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal mValues.put(Favorites.CELLY, getAttributeValue(parser, ATTR_Y)); 1860fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal } 1870fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal 1880fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal mValues.put(Favorites.CONTAINER, container); 1890fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal mValues.put(Favorites.SCREEN, screenId); 1900fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal 1910fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal TagParser tagParser = tagParserMap.get(parser.getName()); 1920fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal if (tagParser == null) { 1930fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal if (LOGD) Log.d(TAG, "Ignoring unknown element tag: " + parser.getName()); 1940fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal continue; 1950fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal } 1960fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal long newElementId = tagParser.parseAndAdd(parser, res); 1970fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal if (newElementId >= 0) { 1980fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal // Keep track of the set of screens which need to be added to the db. 1990fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal if (!screenIds.contains(screenId) && 2000fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal container == Favorites.CONTAINER_DESKTOP) { 2010fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal screenIds.add(screenId); 2020fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal } 2030fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal count++; 2040fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal } 2050fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal } 2060fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal return count; 2070fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal } 2080fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal 2090fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal protected long addShortcut(String title, Intent intent, int type) { 2100fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal long id = mCallback.generateNewItemId(); 2110fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal mValues.put(Favorites.INTENT, intent.toUri(0)); 2120fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal mValues.put(Favorites.TITLE, title); 2130fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal mValues.put(Favorites.ITEM_TYPE, type); 2140fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal mValues.put(Favorites.SPANX, 1); 2150fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal mValues.put(Favorites.SPANY, 1); 2160fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal mValues.put(Favorites._ID, id); 2170fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal if (mCallback.insertAndCheck(mDb, mValues) < 0) { 2180fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal return -1; 2190fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal } else { 2200fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal return id; 2210fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal } 2220fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal } 2230fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal 2240fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal protected HashMap<String, TagParser> getFolderElementsMap() { 2250fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal HashMap<String, TagParser> parsers = new HashMap<String, TagParser>(); 2260fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal parsers.put(TAG_APP_ICON, new AppShortcutParser()); 2270fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal parsers.put(TAG_AUTO_INSTALL, new AutoInstallParser()); 2280fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal parsers.put(TAG_SHORTCUT, new ShortcutParser()); 2290fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal return parsers; 2300fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal } 2310fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal 2320fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal protected HashMap<String, TagParser> getLayoutElementsMap() { 2330fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal HashMap<String, TagParser> parsers = new HashMap<String, TagParser>(); 2340fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal parsers.put(TAG_APP_ICON, new AppShortcutParser()); 2350fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal parsers.put(TAG_AUTO_INSTALL, new AutoInstallParser()); 2360fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal parsers.put(TAG_FOLDER, new FolderParser()); 2370fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal parsers.put(TAG_APPWIDGET, new AppWidgetParser()); 2380fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal parsers.put(TAG_SHORTCUT, new ShortcutParser()); 2390fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal return parsers; 2400fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal } 2410fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal 2420fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal private interface TagParser { 2430fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal /** 2440fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal * Parses the tag and adds to the db 2450fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal * @return the id of the row added or -1; 2460fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal */ 2470fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal long parseAndAdd(XmlResourceParser parser, Resources res) 2480fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal throws XmlPullParserException, IOException; 2490fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal } 2500fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal 2510fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal private class AppShortcutParser implements TagParser { 2520fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal 2530fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal @Override 2540fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal public long parseAndAdd(XmlResourceParser parser, Resources res) { 2550fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal final String packageName = getAttributeValue(parser, ATTR_PACKAGE_NAME); 2560fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal final String className = getAttributeValue(parser, ATTR_CLASS_NAME); 2570fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal 2580fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal if (!TextUtils.isEmpty(packageName) && !TextUtils.isEmpty(className)) { 2590fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal ActivityInfo info; 2600fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal try { 2610fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal ComponentName cn; 2620fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal try { 2630fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal cn = new ComponentName(packageName, className); 2640fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal info = mPackageManager.getActivityInfo(cn, 0); 2650fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal } catch (PackageManager.NameNotFoundException nnfe) { 2660fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal String[] packages = mPackageManager.currentToCanonicalPackageNames( 2670fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal new String[] { packageName }); 2680fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal cn = new ComponentName(packages[0], className); 2690fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal info = mPackageManager.getActivityInfo(cn, 0); 2700fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal } 2710fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal final Intent intent = new Intent(Intent.ACTION_MAIN, null) 2720fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal .addCategory(Intent.CATEGORY_LAUNCHER) 2730fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal .setComponent(cn) 2740fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | 2750fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED); 2760fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal 2770fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal return addShortcut(info.loadLabel(mPackageManager).toString(), 2780fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal intent, Favorites.ITEM_TYPE_APPLICATION); 2790fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal } catch (PackageManager.NameNotFoundException e) { 2800fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal Log.w(TAG, "Unable to add favorite: " + packageName + "/" + className, e); 2810fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal } 2820fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal return -1; 2830fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal } else { 2840fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal if (LOGD) Log.d(TAG, "Skipping invalid <favorite> with no component or uri"); 2850fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal return -1; 2860fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal } 2870fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal } 2880fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal } 2890fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal 2900fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal private class AutoInstallParser implements TagParser { 2910fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal 2920fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal @Override 2930fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal public long parseAndAdd(XmlResourceParser parser, Resources res) { 2940fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal final String packageName = getAttributeValue(parser, ATTR_PACKAGE_NAME); 2950fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal final String className = getAttributeValue(parser, ATTR_CLASS_NAME); 2960fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal if (TextUtils.isEmpty(packageName) || TextUtils.isEmpty(className)) { 2970fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal if (LOGD) Log.d(TAG, "Skipping invalid <favorite> with no component"); 2980fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal return -1; 2990fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal } 3000fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal 301349426234e8c5a0e5bcf2c8d94dbb9844b5f724aSunny Goyal mValues.put(Favorites.RESTORED, ShortcutInfo.FLAG_AUTOINTALL_ICON); 3020fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal final Intent intent = new Intent(Intent.ACTION_MAIN, null) 3030fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal .addCategory(Intent.CATEGORY_LAUNCHER) 3040fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal .setComponent(new ComponentName(packageName, className)) 3050fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | 3060fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED); 3070fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal return addShortcut(mContext.getString(R.string.package_state_unknown), intent, 3080fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal Favorites.ITEM_TYPE_APPLICATION); 3090fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal } 3100fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal } 3110fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal 3120fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal private class ShortcutParser implements TagParser { 3130fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal 3140fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal @Override 3150fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal public long parseAndAdd(XmlResourceParser parser, Resources res) { 3160fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal final String url = getAttributeValue(parser, ATTR_URL); 3170fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal final int titleResId = getAttributeResourceValue(parser, ATTR_TITLE, 0); 3180fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal final int iconId = getAttributeResourceValue(parser, ATTR_ICON, 0); 3190fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal 3200fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal if (titleResId == 0 || iconId == 0) { 3210fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal if (LOGD) Log.d(TAG, "Ignoring shortcut"); 3220fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal return -1; 3230fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal } 3240fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal 3250fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal if (TextUtils.isEmpty(url) || !Patterns.WEB_URL.matcher(url).matches()) { 3260fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal if (LOGD) Log.d(TAG, "Ignoring shortcut, invalid url: " + url); 3270fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal return -1; 3280fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal } 3290fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal Drawable icon = res.getDrawable(iconId); 3300fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal if (icon == null) { 3310fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal if (LOGD) Log.d(TAG, "Ignoring shortcut, can't load icon"); 3320fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal return -1; 3330fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal } 3340fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal 3350fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal ItemInfo.writeBitmap(mValues, Utilities.createIconBitmap(icon, mContext)); 3360fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal final Intent intent = new Intent(Intent.ACTION_VIEW, null) 3370fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal .setData(Uri.parse(url)) 3380fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | 3390fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED); 3400fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal return addShortcut(res.getString(titleResId), intent, Favorites.ITEM_TYPE_SHORTCUT); 3410fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal } 3420fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal } 3430fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal 3440fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal private class AppWidgetParser implements TagParser { 3450fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal 3460fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal @Override 3470fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal public long parseAndAdd(XmlResourceParser parser, Resources res) 3480fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal throws XmlPullParserException, IOException { 3490fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal final String packageName = getAttributeValue(parser, ATTR_PACKAGE_NAME); 3500fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal final String className = getAttributeValue(parser, ATTR_CLASS_NAME); 3510fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal if (TextUtils.isEmpty(packageName) || TextUtils.isEmpty(className)) { 3520fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal if (LOGD) Log.d(TAG, "Skipping invalid <favorite> with no component"); 3530fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal return -1; 3540fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal } 3550fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal 3560fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal ComponentName cn = new ComponentName(packageName, className); 3570fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal try { 3580fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal mPackageManager.getReceiverInfo(cn, 0); 3590fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal } catch (Exception e) { 3600fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal String[] packages = mPackageManager.currentToCanonicalPackageNames( 3610fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal new String[] { packageName }); 3620fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal cn = new ComponentName(packages[0], className); 3630fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal try { 3640fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal mPackageManager.getReceiverInfo(cn, 0); 3650fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal } catch (Exception e1) { 3660fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal if (LOGD) Log.d(TAG, "Can't find widget provider: " + className); 3670fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal return -1; 3680fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal } 3690fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal } 3700fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal 3710fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal mValues.put(Favorites.SPANX, getAttributeValue(parser, ATTR_SPAN_X)); 3720fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal mValues.put(Favorites.SPANY, getAttributeValue(parser, ATTR_SPAN_Y)); 3730fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal 3740fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal // Read the extras 3750fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal Bundle extras = new Bundle(); 3760fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal int widgetDepth = parser.getDepth(); 3770fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal int type; 3780fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal while ((type = parser.next()) != XmlPullParser.END_TAG || 3790fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal parser.getDepth() > widgetDepth) { 3800fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal if (type != XmlPullParser.START_TAG) { 3810fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal continue; 3820fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal } 3830fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal 3840fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal if (TAG_EXTRA.equals(parser.getName())) { 3850fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal String key = getAttributeValue(parser, ATTR_KEY); 3860fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal String value = getAttributeValue(parser, ATTR_VALUE); 3870fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal if (key != null && value != null) { 3880fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal extras.putString(key, value); 3890fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal } else { 3900fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal throw new RuntimeException("Widget extras must have a key and value"); 3910fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal } 3920fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal } else { 3930fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal throw new RuntimeException("Widgets can contain only extras"); 3940fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal } 3950fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal } 3960fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal 3970fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal final AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(mContext); 3980fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal long insertedId = -1; 3990fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal try { 4000fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal int appWidgetId = mAppWidgetHost.allocateAppWidgetId(); 4010fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal 4020fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal if (!appWidgetManager.bindAppWidgetIdIfAllowed(appWidgetId, cn)) { 4030fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal if (LOGD) Log.e(TAG, "Unable to bind app widget id " + cn); 4040fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal return -1; 4050fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal } 4060fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal 4070fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal mValues.put(Favorites.ITEM_TYPE, Favorites.ITEM_TYPE_APPWIDGET); 4080fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal mValues.put(Favorites.APPWIDGET_ID, appWidgetId); 4090fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal mValues.put(Favorites.APPWIDGET_PROVIDER, cn.flattenToString()); 4100fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal mValues.put(Favorites._ID, mCallback.generateNewItemId()); 4110fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal insertedId = mCallback.insertAndCheck(mDb, mValues); 4120fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal if (insertedId < 0) { 4130fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal mAppWidgetHost.deleteAppWidgetId(appWidgetId); 4140fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal return insertedId; 4150fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal } 4160fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal 4170fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal // Send a broadcast to configure the widget 4180fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal if (!extras.isEmpty()) { 4190fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal Intent intent = new Intent(ACTION_APPWIDGET_DEFAULT_WORKSPACE_CONFIGURE); 4200fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal intent.setComponent(cn); 4210fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal intent.putExtras(extras); 4220fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId); 4230fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal mContext.sendBroadcast(intent); 4240fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal } 4250fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal } catch (RuntimeException ex) { 4260fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal if (LOGD) Log.e(TAG, "Problem allocating appWidgetId", ex); 4270fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal } 4280fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal return insertedId; 4290fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal } 4300fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal } 4310fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal 4320fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal private class FolderParser implements TagParser { 4330fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal private final HashMap<String, TagParser> mFolderElements = getFolderElementsMap(); 4340fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal 4350fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal @Override 4360fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal public long parseAndAdd(XmlResourceParser parser, Resources res) 4370fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal throws XmlPullParserException, IOException { 4380fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal final String title; 4390fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal final int titleResId = getAttributeResourceValue(parser, ATTR_TITLE, 0); 4400fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal if (titleResId != 0) { 4410fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal title = res.getString(titleResId); 4420fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal } else { 4430fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal title = mContext.getResources().getString(R.string.folder_name); 4440fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal } 4450fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal 4460fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal mValues.put(Favorites.TITLE, title); 4470fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal mValues.put(Favorites.ITEM_TYPE, Favorites.ITEM_TYPE_FOLDER); 4480fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal mValues.put(Favorites.SPANX, 1); 4490fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal mValues.put(Favorites.SPANY, 1); 4500fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal mValues.put(Favorites._ID, mCallback.generateNewItemId()); 4510fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal long folderId = mCallback.insertAndCheck(mDb, mValues); 4520fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal if (folderId < 0) { 4530fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal if (LOGD) Log.e(TAG, "Unable to add folder"); 4540fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal return -1; 4550fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal } 4560fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal 4570fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal final ContentValues myValues = new ContentValues(mValues); 4580fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal ArrayList<Long> folderItems = new ArrayList<Long>(); 4590fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal 4600fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal int type; 4610fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal int folderDepth = parser.getDepth(); 4620fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal while ((type = parser.next()) != XmlPullParser.END_TAG || 4630fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal parser.getDepth() > folderDepth) { 4640fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal if (type != XmlPullParser.START_TAG) { 4650fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal continue; 4660fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal } 4670fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal mValues.clear(); 4680fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal mValues.put(Favorites.CONTAINER, folderId); 4690fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal 4700fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal TagParser tagParser = mFolderElements.get(parser.getName()); 4710fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal if (tagParser != null) { 4720fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal final long id = tagParser.parseAndAdd(parser, res); 4730fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal if (id >= 0) { 4740fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal folderItems.add(id); 4750fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal } 4760fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal } else { 4770fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal throw new RuntimeException("Invalid folder item " + parser.getName()); 4780fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal } 4790fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal } 4800fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal 4810fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal long addedId = folderId; 4820fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal 4830fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal // We can only have folders with >= 2 items, so we need to remove the 4840fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal // folder and clean up if less than 2 items were included, or some 4850fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal // failed to add, and less than 2 were actually added 4860fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal if (folderItems.size() < 2) { 4870fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal // Delete the folder 4880fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal Uri uri = Favorites.getContentUri(folderId, false); 4890fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal SqlArguments args = new SqlArguments(uri, null, null); 4900fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal mDb.delete(args.table, args.where, args.args); 4910fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal addedId = -1; 4920fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal 4930fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal // If we have a single item, promote it to where the folder 4940fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal // would have been. 4950fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal if (folderItems.size() == 1) { 4960fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal final ContentValues childValues = new ContentValues(); 4970fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal copyInteger(myValues, childValues, Favorites.CONTAINER); 4980fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal copyInteger(myValues, childValues, Favorites.SCREEN); 4990fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal copyInteger(myValues, childValues, Favorites.CELLX); 5000fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal copyInteger(myValues, childValues, Favorites.CELLY); 5010fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal 5020fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal addedId = folderItems.get(0); 5030fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal mDb.update(LauncherProvider.TABLE_FAVORITES, childValues, 5040fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal Favorites._ID + "=" + addedId, null); 5050fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal } 5060fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal } 5070fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal return addedId; 5080fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal } 5090fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal } 5100fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal 5110fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal private static final void beginDocument(XmlPullParser parser, String firstElementName) 5120fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal throws XmlPullParserException, IOException { 5130fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal int type; 5140fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal while ((type = parser.next()) != XmlPullParser.START_TAG 5150fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal && type != XmlPullParser.END_DOCUMENT); 5160fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal 5170fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal if (type != XmlPullParser.START_TAG) { 5180fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal throw new XmlPullParserException("No start tag found"); 5190fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal } 5200fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal 5210fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal if (!parser.getName().equals(firstElementName)) { 5220fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal throw new XmlPullParserException("Unexpected start tag: found " + parser.getName() + 5230fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal ", expected " + firstElementName); 5240fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal } 5250fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal } 5260fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal 5270fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal /** 5280fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal * Return attribute value, attempting launcher-specific namespace first 5290fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal * before falling back to anonymous attribute. 5300fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal */ 5310fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal private static String getAttributeValue(XmlResourceParser parser, String attribute) { 5320fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal String value = parser.getAttributeValue( 5330fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal "http://schemas.android.com/apk/res-auto/com.android.launcher3", attribute); 5340fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal if (value == null) { 5350fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal value = parser.getAttributeValue(null, attribute); 5360fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal } 5370fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal return value; 5380fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal } 5390fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal 5400fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal /** 5410fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal * Return attribute resource value, attempting launcher-specific namespace 5420fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal * first before falling back to anonymous attribute. 5430fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal */ 5440fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal private static int getAttributeResourceValue(XmlResourceParser parser, String attribute, 5450fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal int defaultValue) { 5460fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal int value = parser.getAttributeResourceValue( 5470fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal "http://schemas.android.com/apk/res-auto/com.android.launcher3", attribute, 5480fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal defaultValue); 5490fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal if (value == defaultValue) { 5500fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal value = parser.getAttributeResourceValue(null, attribute, defaultValue); 5510fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal } 5520fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal return value; 5530fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal } 5540fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal 5550fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal public static interface LayoutParserCallback { 5560fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal long generateNewItemId(); 5570fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal 5580fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal long insertAndCheck(SQLiteDatabase db, ContentValues values); 5590fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal } 5600fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal 5610fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal private static void copyInteger(ContentValues from, ContentValues to, String key) { 5620fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal to.put(key, from.getAsInteger(key)); 5630fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal } 5640fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal} 565