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