Home | History | Annotate | Download | only in launcher3
      1 /*
      2  * Copyright (C) 2008 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.android.launcher3;
     18 
     19 import android.app.SearchManager;
     20 import android.appwidget.AppWidgetHost;
     21 import android.appwidget.AppWidgetManager;
     22 import android.appwidget.AppWidgetProviderInfo;
     23 import android.content.ComponentName;
     24 import android.content.ContentProvider;
     25 import android.content.ContentProviderOperation;
     26 import android.content.ContentProviderResult;
     27 import android.content.ContentResolver;
     28 import android.content.ContentUris;
     29 import android.content.ContentValues;
     30 import android.content.Context;
     31 import android.content.Intent;
     32 import android.content.OperationApplicationException;
     33 import android.content.SharedPreferences;
     34 import android.content.pm.ActivityInfo;
     35 import android.content.pm.PackageManager;
     36 import android.content.res.Resources;
     37 import android.content.res.TypedArray;
     38 import android.content.res.XmlResourceParser;
     39 import android.database.Cursor;
     40 import android.database.SQLException;
     41 import android.database.sqlite.SQLiteDatabase;
     42 import android.database.sqlite.SQLiteOpenHelper;
     43 import android.database.sqlite.SQLiteQueryBuilder;
     44 import android.database.sqlite.SQLiteStatement;
     45 import android.graphics.Bitmap;
     46 import android.graphics.BitmapFactory;
     47 import android.net.Uri;
     48 import android.os.Bundle;
     49 import android.provider.Settings;
     50 import android.text.TextUtils;
     51 import android.util.AttributeSet;
     52 import android.util.Log;
     53 import android.util.SparseArray;
     54 import android.util.Xml;
     55 
     56 import com.android.launcher3.LauncherSettings.Favorites;
     57 import com.android.launcher3.config.ProviderConfig;
     58 
     59 import org.xmlpull.v1.XmlPullParser;
     60 import org.xmlpull.v1.XmlPullParserException;
     61 
     62 import java.io.File;
     63 import java.io.IOException;
     64 import java.net.URISyntaxException;
     65 import java.util.ArrayList;
     66 import java.util.HashSet;
     67 import java.util.List;
     68 
     69 public class LauncherProvider extends ContentProvider {
     70     private static final String TAG = "Launcher.LauncherProvider";
     71     private static final boolean LOGD = false;
     72 
     73     private static final String DATABASE_NAME = "launcher.db";
     74 
     75     private static final int DATABASE_VERSION = 17;
     76 
     77     static final String OLD_AUTHORITY = "com.android.launcher2.settings";
     78     static final String AUTHORITY = ProviderConfig.AUTHORITY;
     79 
     80     // Should we attempt to load anything from the com.android.launcher2 provider?
     81     static final boolean IMPORT_LAUNCHER2_DATABASE = false;
     82 
     83     static final String TABLE_FAVORITES = "favorites";
     84     static final String TABLE_WORKSPACE_SCREENS = "workspaceScreens";
     85     static final String PARAMETER_NOTIFY = "notify";
     86     static final String UPGRADED_FROM_OLD_DATABASE =
     87             "UPGRADED_FROM_OLD_DATABASE";
     88     static final String EMPTY_DATABASE_CREATED =
     89             "EMPTY_DATABASE_CREATED";
     90     static final String DEFAULT_WORKSPACE_RESOURCE_ID =
     91             "DEFAULT_WORKSPACE_RESOURCE_ID";
     92 
     93     private static final String ACTION_APPWIDGET_DEFAULT_WORKSPACE_CONFIGURE =
     94             "com.android.launcher.action.APPWIDGET_DEFAULT_WORKSPACE_CONFIGURE";
     95 
     96     /**
     97      * {@link Uri} triggered at any registered {@link android.database.ContentObserver} when
     98      * {@link AppWidgetHost#deleteHost()} is called during database creation.
     99      * Use this to recall {@link AppWidgetHost#startListening()} if needed.
    100      */
    101     static final Uri CONTENT_APPWIDGET_RESET_URI =
    102             Uri.parse("content://" + AUTHORITY + "/appWidgetReset");
    103 
    104     private DatabaseHelper mOpenHelper;
    105     private static boolean sJustLoadedFromOldDb;
    106 
    107     @Override
    108     public boolean onCreate() {
    109         final Context context = getContext();
    110         mOpenHelper = new DatabaseHelper(context);
    111         LauncherAppState.setLauncherProvider(this);
    112         return true;
    113     }
    114 
    115     public boolean wasNewDbCreated() {
    116         return mOpenHelper.wasNewDbCreated();
    117     }
    118 
    119     @Override
    120     public String getType(Uri uri) {
    121         SqlArguments args = new SqlArguments(uri, null, null);
    122         if (TextUtils.isEmpty(args.where)) {
    123             return "vnd.android.cursor.dir/" + args.table;
    124         } else {
    125             return "vnd.android.cursor.item/" + args.table;
    126         }
    127     }
    128 
    129     @Override
    130     public Cursor query(Uri uri, String[] projection, String selection,
    131             String[] selectionArgs, String sortOrder) {
    132 
    133         SqlArguments args = new SqlArguments(uri, selection, selectionArgs);
    134         SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
    135         qb.setTables(args.table);
    136 
    137         SQLiteDatabase db = mOpenHelper.getWritableDatabase();
    138         Cursor result = qb.query(db, projection, args.where, args.args, null, null, sortOrder);
    139         result.setNotificationUri(getContext().getContentResolver(), uri);
    140 
    141         return result;
    142     }
    143 
    144     private static long dbInsertAndCheck(DatabaseHelper helper,
    145             SQLiteDatabase db, String table, String nullColumnHack, ContentValues values) {
    146         if (values == null) {
    147             throw new RuntimeException("Error: attempting to insert null values");
    148         }
    149         if (!values.containsKey(LauncherSettings.BaseLauncherColumns._ID)) {
    150             throw new RuntimeException("Error: attempting to add item without specifying an id");
    151         }
    152         helper.checkId(table, values);
    153         return db.insert(table, nullColumnHack, values);
    154     }
    155 
    156     private static void deleteId(SQLiteDatabase db, long id) {
    157         Uri uri = LauncherSettings.Favorites.getContentUri(id, false);
    158         SqlArguments args = new SqlArguments(uri, null, null);
    159         db.delete(args.table, args.where, args.args);
    160     }
    161 
    162     @Override
    163     public Uri insert(Uri uri, ContentValues initialValues) {
    164         SqlArguments args = new SqlArguments(uri);
    165 
    166         SQLiteDatabase db = mOpenHelper.getWritableDatabase();
    167         addModifiedTime(initialValues);
    168         final long rowId = dbInsertAndCheck(mOpenHelper, db, args.table, null, initialValues);
    169         if (rowId <= 0) return null;
    170 
    171         uri = ContentUris.withAppendedId(uri, rowId);
    172         sendNotify(uri);
    173 
    174         return uri;
    175     }
    176 
    177     @Override
    178     public int bulkInsert(Uri uri, ContentValues[] values) {
    179         SqlArguments args = new SqlArguments(uri);
    180 
    181         SQLiteDatabase db = mOpenHelper.getWritableDatabase();
    182         db.beginTransaction();
    183         try {
    184             int numValues = values.length;
    185             for (int i = 0; i < numValues; i++) {
    186                 addModifiedTime(values[i]);
    187                 if (dbInsertAndCheck(mOpenHelper, db, args.table, null, values[i]) < 0) {
    188                     return 0;
    189                 }
    190             }
    191             db.setTransactionSuccessful();
    192         } finally {
    193             db.endTransaction();
    194         }
    195 
    196         sendNotify(uri);
    197         return values.length;
    198     }
    199 
    200     @Override
    201     public ContentProviderResult[] applyBatch(ArrayList<ContentProviderOperation> operations)
    202             throws OperationApplicationException {
    203         SQLiteDatabase db = mOpenHelper.getWritableDatabase();
    204         db.beginTransaction();
    205         try {
    206             ContentProviderResult[] result =  super.applyBatch(operations);
    207             db.setTransactionSuccessful();
    208             return result;
    209         } finally {
    210             db.endTransaction();
    211         }
    212     }
    213 
    214     @Override
    215     public int delete(Uri uri, String selection, String[] selectionArgs) {
    216         SqlArguments args = new SqlArguments(uri, selection, selectionArgs);
    217 
    218         SQLiteDatabase db = mOpenHelper.getWritableDatabase();
    219         int count = db.delete(args.table, args.where, args.args);
    220         if (count > 0) sendNotify(uri);
    221 
    222         return count;
    223     }
    224 
    225     @Override
    226     public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
    227         SqlArguments args = new SqlArguments(uri, selection, selectionArgs);
    228 
    229         addModifiedTime(values);
    230         SQLiteDatabase db = mOpenHelper.getWritableDatabase();
    231         int count = db.update(args.table, values, args.where, args.args);
    232         if (count > 0) sendNotify(uri);
    233 
    234         return count;
    235     }
    236 
    237     private void sendNotify(Uri uri) {
    238         String notify = uri.getQueryParameter(PARAMETER_NOTIFY);
    239         if (notify == null || "true".equals(notify)) {
    240             getContext().getContentResolver().notifyChange(uri, null);
    241         }
    242 
    243         // always notify the backup agent
    244         LauncherBackupAgentHelper.dataChanged(getContext());
    245     }
    246 
    247     private void addModifiedTime(ContentValues values) {
    248         values.put(LauncherSettings.ChangeLogColumns.MODIFIED, System.currentTimeMillis());
    249     }
    250 
    251     public long generateNewItemId() {
    252         return mOpenHelper.generateNewItemId();
    253     }
    254 
    255     public void updateMaxItemId(long id) {
    256         mOpenHelper.updateMaxItemId(id);
    257     }
    258 
    259     public long generateNewScreenId() {
    260         return mOpenHelper.generateNewScreenId();
    261     }
    262 
    263     // This is only required one time while loading the workspace during the
    264     // upgrade path, and should never be called from anywhere else.
    265     public void updateMaxScreenId(long maxScreenId) {
    266         mOpenHelper.updateMaxScreenId(maxScreenId);
    267     }
    268 
    269     /**
    270      * @param Should we load the old db for upgrade? first run only.
    271      */
    272     synchronized public boolean justLoadedOldDb() {
    273         String spKey = LauncherAppState.getSharedPreferencesKey();
    274         SharedPreferences sp = getContext().getSharedPreferences(spKey, Context.MODE_PRIVATE);
    275 
    276         boolean loadedOldDb = false || sJustLoadedFromOldDb;
    277 
    278         sJustLoadedFromOldDb = false;
    279         if (sp.getBoolean(UPGRADED_FROM_OLD_DATABASE, false)) {
    280 
    281             SharedPreferences.Editor editor = sp.edit();
    282             editor.remove(UPGRADED_FROM_OLD_DATABASE);
    283             editor.commit();
    284             loadedOldDb = true;
    285         }
    286         return loadedOldDb;
    287     }
    288 
    289     /**
    290      * @param workspaceResId that can be 0 to use default or non-zero for specific resource
    291      */
    292     synchronized public void loadDefaultFavoritesIfNecessary(int origWorkspaceResId) {
    293         String spKey = LauncherAppState.getSharedPreferencesKey();
    294         SharedPreferences sp = getContext().getSharedPreferences(spKey, Context.MODE_PRIVATE);
    295 
    296         if (sp.getBoolean(EMPTY_DATABASE_CREATED, false)) {
    297             Log.d(TAG, "loading default workspace");
    298             int workspaceResId = origWorkspaceResId;
    299 
    300             // Use default workspace resource if none provided
    301             if (workspaceResId == 0) {
    302                 workspaceResId =
    303                         sp.getInt(DEFAULT_WORKSPACE_RESOURCE_ID, getDefaultWorkspaceResourceId());
    304             }
    305 
    306             // Populate favorites table with initial favorites
    307             SharedPreferences.Editor editor = sp.edit();
    308             editor.remove(EMPTY_DATABASE_CREATED);
    309             if (origWorkspaceResId != 0) {
    310                 editor.putInt(DEFAULT_WORKSPACE_RESOURCE_ID, origWorkspaceResId);
    311             }
    312 
    313             mOpenHelper.loadFavorites(mOpenHelper.getWritableDatabase(), workspaceResId);
    314             mOpenHelper.setFlagJustLoadedOldDb();
    315             editor.commit();
    316         }
    317     }
    318 
    319     public void migrateLauncher2Shortcuts() {
    320         mOpenHelper.migrateLauncher2Shortcuts(mOpenHelper.getWritableDatabase(),
    321                 LauncherSettings.Favorites.OLD_CONTENT_URI);
    322     }
    323 
    324     private static int getDefaultWorkspaceResourceId() {
    325         if (LauncherAppState.isDisableAllApps()) {
    326             return R.xml.default_workspace_no_all_apps;
    327         } else {
    328             return R.xml.default_workspace;
    329         }
    330     }
    331 
    332     private static interface ContentValuesCallback {
    333         public void onRow(ContentValues values);
    334     }
    335 
    336     private static boolean shouldImportLauncher2Database(Context context) {
    337         boolean isTablet = context.getResources().getBoolean(R.bool.is_tablet);
    338 
    339         // We don't import the old databse for tablets, as the grid size has changed.
    340         return !isTablet && IMPORT_LAUNCHER2_DATABASE;
    341     }
    342 
    343     public void deleteDatabase() {
    344         // Are you sure? (y/n)
    345         final SQLiteDatabase db = mOpenHelper.getWritableDatabase();
    346         final File dbFile = new File(db.getPath());
    347         mOpenHelper.close();
    348         if (dbFile.exists()) {
    349             SQLiteDatabase.deleteDatabase(dbFile);
    350         }
    351         mOpenHelper = new DatabaseHelper(getContext());
    352     }
    353 
    354     private static class DatabaseHelper extends SQLiteOpenHelper {
    355         private static final String TAG_FAVORITES = "favorites";
    356         private static final String TAG_FAVORITE = "favorite";
    357         private static final String TAG_CLOCK = "clock";
    358         private static final String TAG_SEARCH = "search";
    359         private static final String TAG_APPWIDGET = "appwidget";
    360         private static final String TAG_SHORTCUT = "shortcut";
    361         private static final String TAG_FOLDER = "folder";
    362         private static final String TAG_EXTRA = "extra";
    363         private static final String TAG_INCLUDE = "include";
    364 
    365         private final Context mContext;
    366         private final AppWidgetHost mAppWidgetHost;
    367         private long mMaxItemId = -1;
    368         private long mMaxScreenId = -1;
    369 
    370         private boolean mNewDbCreated = false;
    371 
    372         DatabaseHelper(Context context) {
    373             super(context, DATABASE_NAME, null, DATABASE_VERSION);
    374             mContext = context;
    375             mAppWidgetHost = new AppWidgetHost(context, Launcher.APPWIDGET_HOST_ID);
    376 
    377             // In the case where neither onCreate nor onUpgrade gets called, we read the maxId from
    378             // the DB here
    379             if (mMaxItemId == -1) {
    380                 mMaxItemId = initializeMaxItemId(getWritableDatabase());
    381             }
    382             if (mMaxScreenId == -1) {
    383                 mMaxScreenId = initializeMaxScreenId(getWritableDatabase());
    384             }
    385         }
    386 
    387         public boolean wasNewDbCreated() {
    388             return mNewDbCreated;
    389         }
    390 
    391         /**
    392          * Send notification that we've deleted the {@link AppWidgetHost},
    393          * probably as part of the initial database creation. The receiver may
    394          * want to re-call {@link AppWidgetHost#startListening()} to ensure
    395          * callbacks are correctly set.
    396          */
    397         private void sendAppWidgetResetNotify() {
    398             final ContentResolver resolver = mContext.getContentResolver();
    399             resolver.notifyChange(CONTENT_APPWIDGET_RESET_URI, null);
    400         }
    401 
    402         @Override
    403         public void onCreate(SQLiteDatabase db) {
    404             if (LOGD) Log.d(TAG, "creating new launcher database");
    405 
    406             mMaxItemId = 1;
    407             mMaxScreenId = 0;
    408             mNewDbCreated = true;
    409 
    410             db.execSQL("CREATE TABLE favorites (" +
    411                     "_id INTEGER PRIMARY KEY," +
    412                     "title TEXT," +
    413                     "intent TEXT," +
    414                     "container INTEGER," +
    415                     "screen INTEGER," +
    416                     "cellX INTEGER," +
    417                     "cellY INTEGER," +
    418                     "spanX INTEGER," +
    419                     "spanY INTEGER," +
    420                     "itemType INTEGER," +
    421                     "appWidgetId INTEGER NOT NULL DEFAULT -1," +
    422                     "isShortcut INTEGER," +
    423                     "iconType INTEGER," +
    424                     "iconPackage TEXT," +
    425                     "iconResource TEXT," +
    426                     "icon BLOB," +
    427                     "uri TEXT," +
    428                     "displayMode INTEGER," +
    429                     "appWidgetProvider TEXT," +
    430                     "modified INTEGER NOT NULL DEFAULT 0," +
    431                     "restored INTEGER NOT NULL DEFAULT 0" +
    432                     ");");
    433             addWorkspacesTable(db);
    434 
    435             // Database was just created, so wipe any previous widgets
    436             if (mAppWidgetHost != null) {
    437                 mAppWidgetHost.deleteHost();
    438                 sendAppWidgetResetNotify();
    439             }
    440 
    441             if (shouldImportLauncher2Database(mContext)) {
    442                 // Try converting the old database
    443                 ContentValuesCallback permuteScreensCb = new ContentValuesCallback() {
    444                     public void onRow(ContentValues values) {
    445                         int container = values.getAsInteger(LauncherSettings.Favorites.CONTAINER);
    446                         if (container == Favorites.CONTAINER_DESKTOP) {
    447                             int screen = values.getAsInteger(LauncherSettings.Favorites.SCREEN);
    448                             screen = (int) upgradeLauncherDb_permuteScreens(screen);
    449                             values.put(LauncherSettings.Favorites.SCREEN, screen);
    450                         }
    451                     }
    452                 };
    453                 Uri uri = Uri.parse("content://" + Settings.AUTHORITY +
    454                         "/old_favorites?notify=true");
    455                 if (!convertDatabase(db, uri, permuteScreensCb, true)) {
    456                     // Try and upgrade from the Launcher2 db
    457                     uri = LauncherSettings.Favorites.OLD_CONTENT_URI;
    458                     if (!convertDatabase(db, uri, permuteScreensCb, false)) {
    459                         // If we fail, then set a flag to load the default workspace
    460                         setFlagEmptyDbCreated();
    461                         return;
    462                     }
    463                 }
    464                 // Right now, in non-default workspace cases, we want to run the final
    465                 // upgrade code (ie. to fix workspace screen indices -> ids, etc.), so
    466                 // set that flag too.
    467                 setFlagJustLoadedOldDb();
    468             } else {
    469                 // Fresh and clean launcher DB.
    470                 mMaxItemId = initializeMaxItemId(db);
    471                 setFlagEmptyDbCreated();
    472             }
    473         }
    474 
    475         private void addWorkspacesTable(SQLiteDatabase db) {
    476             db.execSQL("CREATE TABLE " + TABLE_WORKSPACE_SCREENS + " (" +
    477                     LauncherSettings.WorkspaceScreens._ID + " INTEGER," +
    478                     LauncherSettings.WorkspaceScreens.SCREEN_RANK + " INTEGER," +
    479                     LauncherSettings.ChangeLogColumns.MODIFIED + " INTEGER NOT NULL DEFAULT 0" +
    480                     ");");
    481         }
    482 
    483         private void setFlagJustLoadedOldDb() {
    484             String spKey = LauncherAppState.getSharedPreferencesKey();
    485             SharedPreferences sp = mContext.getSharedPreferences(spKey, Context.MODE_PRIVATE);
    486             SharedPreferences.Editor editor = sp.edit();
    487             editor.putBoolean(UPGRADED_FROM_OLD_DATABASE, true);
    488             editor.putBoolean(EMPTY_DATABASE_CREATED, false);
    489             editor.commit();
    490         }
    491 
    492         private void setFlagEmptyDbCreated() {
    493             String spKey = LauncherAppState.getSharedPreferencesKey();
    494             SharedPreferences sp = mContext.getSharedPreferences(spKey, Context.MODE_PRIVATE);
    495             SharedPreferences.Editor editor = sp.edit();
    496             editor.putBoolean(EMPTY_DATABASE_CREATED, true);
    497             editor.putBoolean(UPGRADED_FROM_OLD_DATABASE, false);
    498             editor.commit();
    499         }
    500 
    501         // We rearrange the screens from the old launcher
    502         // 12345 -> 34512
    503         private long upgradeLauncherDb_permuteScreens(long screen) {
    504             if (screen >= 2) {
    505                 return screen - 2;
    506             } else {
    507                 return screen + 3;
    508             }
    509         }
    510 
    511         private boolean convertDatabase(SQLiteDatabase db, Uri uri,
    512                                         ContentValuesCallback cb, boolean deleteRows) {
    513             if (LOGD) Log.d(TAG, "converting database from an older format, but not onUpgrade");
    514             boolean converted = false;
    515 
    516             final ContentResolver resolver = mContext.getContentResolver();
    517             Cursor cursor = null;
    518 
    519             try {
    520                 cursor = resolver.query(uri, null, null, null, null);
    521             } catch (Exception e) {
    522                 // Ignore
    523             }
    524 
    525             // We already have a favorites database in the old provider
    526             if (cursor != null) {
    527                 try {
    528                      if (cursor.getCount() > 0) {
    529                         converted = copyFromCursor(db, cursor, cb) > 0;
    530                         if (converted && deleteRows) {
    531                             resolver.delete(uri, null, null);
    532                         }
    533                     }
    534                 } finally {
    535                     cursor.close();
    536                 }
    537             }
    538 
    539             if (converted) {
    540                 // Convert widgets from this import into widgets
    541                 if (LOGD) Log.d(TAG, "converted and now triggering widget upgrade");
    542                 convertWidgets(db);
    543 
    544                 // Update max item id
    545                 mMaxItemId = initializeMaxItemId(db);
    546                 if (LOGD) Log.d(TAG, "mMaxItemId: " + mMaxItemId);
    547             }
    548 
    549             return converted;
    550         }
    551 
    552         private int copyFromCursor(SQLiteDatabase db, Cursor c, ContentValuesCallback cb) {
    553             final int idIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites._ID);
    554             final int intentIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.INTENT);
    555             final int titleIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.TITLE);
    556             final int iconTypeIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ICON_TYPE);
    557             final int iconIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ICON);
    558             final int iconPackageIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ICON_PACKAGE);
    559             final int iconResourceIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ICON_RESOURCE);
    560             final int containerIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CONTAINER);
    561             final int itemTypeIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ITEM_TYPE);
    562             final int screenIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.SCREEN);
    563             final int cellXIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CELLX);
    564             final int cellYIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CELLY);
    565             final int uriIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.URI);
    566             final int displayModeIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.DISPLAY_MODE);
    567 
    568             ContentValues[] rows = new ContentValues[c.getCount()];
    569             int i = 0;
    570             while (c.moveToNext()) {
    571                 ContentValues values = new ContentValues(c.getColumnCount());
    572                 values.put(LauncherSettings.Favorites._ID, c.getLong(idIndex));
    573                 values.put(LauncherSettings.Favorites.INTENT, c.getString(intentIndex));
    574                 values.put(LauncherSettings.Favorites.TITLE, c.getString(titleIndex));
    575                 values.put(LauncherSettings.Favorites.ICON_TYPE, c.getInt(iconTypeIndex));
    576                 values.put(LauncherSettings.Favorites.ICON, c.getBlob(iconIndex));
    577                 values.put(LauncherSettings.Favorites.ICON_PACKAGE, c.getString(iconPackageIndex));
    578                 values.put(LauncherSettings.Favorites.ICON_RESOURCE, c.getString(iconResourceIndex));
    579                 values.put(LauncherSettings.Favorites.CONTAINER, c.getInt(containerIndex));
    580                 values.put(LauncherSettings.Favorites.ITEM_TYPE, c.getInt(itemTypeIndex));
    581                 values.put(LauncherSettings.Favorites.APPWIDGET_ID, -1);
    582                 values.put(LauncherSettings.Favorites.SCREEN, c.getInt(screenIndex));
    583                 values.put(LauncherSettings.Favorites.CELLX, c.getInt(cellXIndex));
    584                 values.put(LauncherSettings.Favorites.CELLY, c.getInt(cellYIndex));
    585                 values.put(LauncherSettings.Favorites.URI, c.getString(uriIndex));
    586                 values.put(LauncherSettings.Favorites.DISPLAY_MODE, c.getInt(displayModeIndex));
    587                 if (cb != null) {
    588                     cb.onRow(values);
    589                 }
    590                 rows[i++] = values;
    591             }
    592 
    593             int total = 0;
    594             if (i > 0) {
    595                 db.beginTransaction();
    596                 try {
    597                     int numValues = rows.length;
    598                     for (i = 0; i < numValues; i++) {
    599                         if (dbInsertAndCheck(this, db, TABLE_FAVORITES, null, rows[i]) < 0) {
    600                             return 0;
    601                         } else {
    602                             total++;
    603                         }
    604                     }
    605                     db.setTransactionSuccessful();
    606                 } finally {
    607                     db.endTransaction();
    608                 }
    609             }
    610 
    611             return total;
    612         }
    613 
    614         @Override
    615         public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
    616             if (LOGD) Log.d(TAG, "onUpgrade triggered: " + oldVersion);
    617 
    618             int version = oldVersion;
    619             if (version < 3) {
    620                 // upgrade 1,2 -> 3 added appWidgetId column
    621                 db.beginTransaction();
    622                 try {
    623                     // Insert new column for holding appWidgetIds
    624                     db.execSQL("ALTER TABLE favorites " +
    625                         "ADD COLUMN appWidgetId INTEGER NOT NULL DEFAULT -1;");
    626                     db.setTransactionSuccessful();
    627                     version = 3;
    628                 } catch (SQLException ex) {
    629                     // Old version remains, which means we wipe old data
    630                     Log.e(TAG, ex.getMessage(), ex);
    631                 } finally {
    632                     db.endTransaction();
    633                 }
    634 
    635                 // Convert existing widgets only if table upgrade was successful
    636                 if (version == 3) {
    637                     convertWidgets(db);
    638                 }
    639             }
    640 
    641             if (version < 4) {
    642                 version = 4;
    643             }
    644 
    645             // Where's version 5?
    646             // - Donut and sholes on 2.0 shipped with version 4 of launcher1.
    647             // - Passion shipped on 2.1 with version 6 of launcher3
    648             // - Sholes shipped on 2.1r1 (aka Mr. 3) with version 5 of launcher 1
    649             //   but version 5 on there was the updateContactsShortcuts change
    650             //   which was version 6 in launcher 2 (first shipped on passion 2.1r1).
    651             // The updateContactsShortcuts change is idempotent, so running it twice
    652             // is okay so we'll do that when upgrading the devices that shipped with it.
    653             if (version < 6) {
    654                 // We went from 3 to 5 screens. Move everything 1 to the right
    655                 db.beginTransaction();
    656                 try {
    657                     db.execSQL("UPDATE favorites SET screen=(screen + 1);");
    658                     db.setTransactionSuccessful();
    659                 } catch (SQLException ex) {
    660                     // Old version remains, which means we wipe old data
    661                     Log.e(TAG, ex.getMessage(), ex);
    662                 } finally {
    663                     db.endTransaction();
    664                 }
    665 
    666                // We added the fast track.
    667                 if (updateContactsShortcuts(db)) {
    668                     version = 6;
    669                 }
    670             }
    671 
    672             if (version < 7) {
    673                 // Version 7 gets rid of the special search widget.
    674                 convertWidgets(db);
    675                 version = 7;
    676             }
    677 
    678             if (version < 8) {
    679                 // Version 8 (froyo) has the icons all normalized.  This should
    680                 // already be the case in practice, but we now rely on it and don't
    681                 // resample the images each time.
    682                 normalizeIcons(db);
    683                 version = 8;
    684             }
    685 
    686             if (version < 9) {
    687                 // The max id is not yet set at this point (onUpgrade is triggered in the ctor
    688                 // before it gets a change to get set, so we need to read it here when we use it)
    689                 if (mMaxItemId == -1) {
    690                     mMaxItemId = initializeMaxItemId(db);
    691                 }
    692 
    693                 // Add default hotseat icons
    694                 loadFavorites(db, R.xml.update_workspace);
    695                 version = 9;
    696             }
    697 
    698             // We bumped the version three time during JB, once to update the launch flags, once to
    699             // update the override for the default launch animation and once to set the mimetype
    700             // to improve startup performance
    701             if (version < 12) {
    702                 // Contact shortcuts need a different set of flags to be launched now
    703                 // The updateContactsShortcuts change is idempotent, so we can keep using it like
    704                 // back in the Donut days
    705                 updateContactsShortcuts(db);
    706                 version = 12;
    707             }
    708 
    709             if (version < 13) {
    710                 // With the new shrink-wrapped and re-orderable workspaces, it makes sense
    711                 // to persist workspace screens and their relative order.
    712                 mMaxScreenId = 0;
    713 
    714                 // This will never happen in the wild, but when we switch to using workspace
    715                 // screen ids, redo the import from old launcher.
    716                 sJustLoadedFromOldDb = true;
    717 
    718                 addWorkspacesTable(db);
    719                 version = 13;
    720             }
    721 
    722             if (version < 14) {
    723                 db.beginTransaction();
    724                 try {
    725                     // Insert new column for holding widget provider name
    726                     db.execSQL("ALTER TABLE favorites " +
    727                             "ADD COLUMN appWidgetProvider TEXT;");
    728                     db.setTransactionSuccessful();
    729                     version = 14;
    730                 } catch (SQLException ex) {
    731                     // Old version remains, which means we wipe old data
    732                     Log.e(TAG, ex.getMessage(), ex);
    733                 } finally {
    734                     db.endTransaction();
    735                 }
    736             }
    737 
    738             if (version < 15) {
    739                 db.beginTransaction();
    740                 try {
    741                     // Insert new column for holding update timestamp
    742                     db.execSQL("ALTER TABLE favorites " +
    743                             "ADD COLUMN modified INTEGER NOT NULL DEFAULT 0;");
    744                     db.execSQL("ALTER TABLE workspaceScreens " +
    745                             "ADD COLUMN modified INTEGER NOT NULL DEFAULT 0;");
    746                     db.setTransactionSuccessful();
    747                     version = 15;
    748                 } catch (SQLException ex) {
    749                     // Old version remains, which means we wipe old data
    750                     Log.e(TAG, ex.getMessage(), ex);
    751                 } finally {
    752                     db.endTransaction();
    753                 }
    754             }
    755 
    756 
    757             if (version < 16) {
    758                 db.beginTransaction();
    759                 try {
    760                     // Insert new column for holding restore status
    761                     db.execSQL("ALTER TABLE favorites " +
    762                             "ADD COLUMN restored INTEGER NOT NULL DEFAULT 0;");
    763                     db.setTransactionSuccessful();
    764                     version = 16;
    765                 } catch (SQLException ex) {
    766                     // Old version remains, which means we wipe old data
    767                     Log.e(TAG, ex.getMessage(), ex);
    768                 } finally {
    769                     db.endTransaction();
    770                 }
    771             }
    772 
    773             if (version < 17) {
    774                 // We use the db version upgrade here to identify users who may not have seen
    775                 // clings yet (because they weren't available), but for whom the clings are now
    776                 // available (tablet users). Because one of the possible cling flows (migration)
    777                 // is very destructive (wipes out workspaces), we want to prevent this from showing
    778                 // until clear data. We do so by marking that the clings have been shown.
    779                 LauncherClings.synchonouslyMarkFirstRunClingDismissed(mContext);
    780                 version = 17;
    781             }
    782 
    783             if (version != DATABASE_VERSION) {
    784                 Log.w(TAG, "Destroying all old data.");
    785                 db.execSQL("DROP TABLE IF EXISTS " + TABLE_FAVORITES);
    786                 db.execSQL("DROP TABLE IF EXISTS " + TABLE_WORKSPACE_SCREENS);
    787 
    788                 onCreate(db);
    789             }
    790         }
    791 
    792         private boolean updateContactsShortcuts(SQLiteDatabase db) {
    793             final String selectWhere = buildOrWhereString(Favorites.ITEM_TYPE,
    794                     new int[] { Favorites.ITEM_TYPE_SHORTCUT });
    795 
    796             Cursor c = null;
    797             final String actionQuickContact = "com.android.contacts.action.QUICK_CONTACT";
    798             db.beginTransaction();
    799             try {
    800                 // Select and iterate through each matching widget
    801                 c = db.query(TABLE_FAVORITES,
    802                         new String[] { Favorites._ID, Favorites.INTENT },
    803                         selectWhere, null, null, null, null);
    804                 if (c == null) return false;
    805 
    806                 if (LOGD) Log.d(TAG, "found upgrade cursor count=" + c.getCount());
    807 
    808                 final int idIndex = c.getColumnIndex(Favorites._ID);
    809                 final int intentIndex = c.getColumnIndex(Favorites.INTENT);
    810 
    811                 while (c.moveToNext()) {
    812                     long favoriteId = c.getLong(idIndex);
    813                     final String intentUri = c.getString(intentIndex);
    814                     if (intentUri != null) {
    815                         try {
    816                             final Intent intent = Intent.parseUri(intentUri, 0);
    817                             android.util.Log.d("Home", intent.toString());
    818                             final Uri uri = intent.getData();
    819                             if (uri != null) {
    820                                 final String data = uri.toString();
    821                                 if ((Intent.ACTION_VIEW.equals(intent.getAction()) ||
    822                                         actionQuickContact.equals(intent.getAction())) &&
    823                                         (data.startsWith("content://contacts/people/") ||
    824                                         data.startsWith("content://com.android.contacts/" +
    825                                                 "contacts/lookup/"))) {
    826 
    827                                     final Intent newIntent = new Intent(actionQuickContact);
    828                                     // When starting from the launcher, start in a new, cleared task
    829                                     // CLEAR_WHEN_TASK_RESET cannot reset the root of a task, so we
    830                                     // clear the whole thing preemptively here since
    831                                     // QuickContactActivity will finish itself when launching other
    832                                     // detail activities.
    833                                     newIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK |
    834                                             Intent.FLAG_ACTIVITY_CLEAR_TASK);
    835                                     newIntent.putExtra(
    836                                             Launcher.INTENT_EXTRA_IGNORE_LAUNCH_ANIMATION, true);
    837                                     newIntent.setData(uri);
    838                                     // Determine the type and also put that in the shortcut
    839                                     // (that can speed up launch a bit)
    840                                     newIntent.setDataAndType(uri, newIntent.resolveType(mContext));
    841 
    842                                     final ContentValues values = new ContentValues();
    843                                     values.put(LauncherSettings.Favorites.INTENT,
    844                                             newIntent.toUri(0));
    845 
    846                                     String updateWhere = Favorites._ID + "=" + favoriteId;
    847                                     db.update(TABLE_FAVORITES, values, updateWhere, null);
    848                                 }
    849                             }
    850                         } catch (RuntimeException ex) {
    851                             Log.e(TAG, "Problem upgrading shortcut", ex);
    852                         } catch (URISyntaxException e) {
    853                             Log.e(TAG, "Problem upgrading shortcut", e);
    854                         }
    855                     }
    856                 }
    857 
    858                 db.setTransactionSuccessful();
    859             } catch (SQLException ex) {
    860                 Log.w(TAG, "Problem while upgrading contacts", ex);
    861                 return false;
    862             } finally {
    863                 db.endTransaction();
    864                 if (c != null) {
    865                     c.close();
    866                 }
    867             }
    868 
    869             return true;
    870         }
    871 
    872         private void normalizeIcons(SQLiteDatabase db) {
    873             Log.d(TAG, "normalizing icons");
    874 
    875             db.beginTransaction();
    876             Cursor c = null;
    877             SQLiteStatement update = null;
    878             try {
    879                 boolean logged = false;
    880                 update = db.compileStatement("UPDATE favorites "
    881                         + "SET icon=? WHERE _id=?");
    882 
    883                 c = db.rawQuery("SELECT _id, icon FROM favorites WHERE iconType=" +
    884                         Favorites.ICON_TYPE_BITMAP, null);
    885 
    886                 final int idIndex = c.getColumnIndexOrThrow(Favorites._ID);
    887                 final int iconIndex = c.getColumnIndexOrThrow(Favorites.ICON);
    888 
    889                 while (c.moveToNext()) {
    890                     long id = c.getLong(idIndex);
    891                     byte[] data = c.getBlob(iconIndex);
    892                     try {
    893                         Bitmap bitmap = Utilities.resampleIconBitmap(
    894                                 BitmapFactory.decodeByteArray(data, 0, data.length),
    895                                 mContext);
    896                         if (bitmap != null) {
    897                             update.bindLong(1, id);
    898                             data = ItemInfo.flattenBitmap(bitmap);
    899                             if (data != null) {
    900                                 update.bindBlob(2, data);
    901                                 update.execute();
    902                             }
    903                             bitmap.recycle();
    904                         }
    905                     } catch (Exception e) {
    906                         if (!logged) {
    907                             Log.e(TAG, "Failed normalizing icon " + id, e);
    908                         } else {
    909                             Log.e(TAG, "Also failed normalizing icon " + id);
    910                         }
    911                         logged = true;
    912                     }
    913                 }
    914                 db.setTransactionSuccessful();
    915             } catch (SQLException ex) {
    916                 Log.w(TAG, "Problem while allocating appWidgetIds for existing widgets", ex);
    917             } finally {
    918                 db.endTransaction();
    919                 if (update != null) {
    920                     update.close();
    921                 }
    922                 if (c != null) {
    923                     c.close();
    924                 }
    925             }
    926         }
    927 
    928         // Generates a new ID to use for an object in your database. This method should be only
    929         // called from the main UI thread. As an exception, we do call it when we call the
    930         // constructor from the worker thread; however, this doesn't extend until after the
    931         // constructor is called, and we only pass a reference to LauncherProvider to LauncherApp
    932         // after that point
    933         public long generateNewItemId() {
    934             if (mMaxItemId < 0) {
    935                 throw new RuntimeException("Error: max item id was not initialized");
    936             }
    937             mMaxItemId += 1;
    938             return mMaxItemId;
    939         }
    940 
    941         public void updateMaxItemId(long id) {
    942             mMaxItemId = id + 1;
    943         }
    944 
    945         public void checkId(String table, ContentValues values) {
    946             long id = values.getAsLong(LauncherSettings.BaseLauncherColumns._ID);
    947             if (table == LauncherProvider.TABLE_WORKSPACE_SCREENS) {
    948                 mMaxScreenId = Math.max(id, mMaxScreenId);
    949             }  else {
    950                 mMaxItemId = Math.max(id, mMaxItemId);
    951             }
    952         }
    953 
    954         private long initializeMaxItemId(SQLiteDatabase db) {
    955             Cursor c = db.rawQuery("SELECT MAX(_id) FROM favorites", null);
    956 
    957             // get the result
    958             final int maxIdIndex = 0;
    959             long id = -1;
    960             if (c != null && c.moveToNext()) {
    961                 id = c.getLong(maxIdIndex);
    962             }
    963             if (c != null) {
    964                 c.close();
    965             }
    966 
    967             if (id == -1) {
    968                 throw new RuntimeException("Error: could not query max item id");
    969             }
    970 
    971             return id;
    972         }
    973 
    974         // Generates a new ID to use for an workspace screen in your database. This method
    975         // should be only called from the main UI thread. As an exception, we do call it when we
    976         // call the constructor from the worker thread; however, this doesn't extend until after the
    977         // constructor is called, and we only pass a reference to LauncherProvider to LauncherApp
    978         // after that point
    979         public long generateNewScreenId() {
    980             if (mMaxScreenId < 0) {
    981                 throw new RuntimeException("Error: max screen id was not initialized");
    982             }
    983             mMaxScreenId += 1;
    984             // Log to disk
    985             Launcher.addDumpLog(TAG, "11683562 - generateNewScreenId(): " + mMaxScreenId, true);
    986             return mMaxScreenId;
    987         }
    988 
    989         public void updateMaxScreenId(long maxScreenId) {
    990             // Log to disk
    991             Launcher.addDumpLog(TAG, "11683562 - updateMaxScreenId(): " + maxScreenId, true);
    992             mMaxScreenId = maxScreenId;
    993         }
    994 
    995         private long initializeMaxScreenId(SQLiteDatabase db) {
    996             Cursor c = db.rawQuery("SELECT MAX(" + LauncherSettings.WorkspaceScreens._ID + ") FROM " + TABLE_WORKSPACE_SCREENS, null);
    997 
    998             // get the result
    999             final int maxIdIndex = 0;
   1000             long id = -1;
   1001             if (c != null && c.moveToNext()) {
   1002                 id = c.getLong(maxIdIndex);
   1003             }
   1004             if (c != null) {
   1005                 c.close();
   1006             }
   1007 
   1008             if (id == -1) {
   1009                 throw new RuntimeException("Error: could not query max screen id");
   1010             }
   1011 
   1012             // Log to disk
   1013             Launcher.addDumpLog(TAG, "11683562 - initializeMaxScreenId(): " + id, true);
   1014             return id;
   1015         }
   1016 
   1017         /**
   1018          * Upgrade existing clock and photo frame widgets into their new widget
   1019          * equivalents.
   1020          */
   1021         private void convertWidgets(SQLiteDatabase db) {
   1022             final AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(mContext);
   1023             final int[] bindSources = new int[] {
   1024                     Favorites.ITEM_TYPE_WIDGET_CLOCK,
   1025                     Favorites.ITEM_TYPE_WIDGET_PHOTO_FRAME,
   1026                     Favorites.ITEM_TYPE_WIDGET_SEARCH,
   1027             };
   1028 
   1029             final String selectWhere = buildOrWhereString(Favorites.ITEM_TYPE, bindSources);
   1030 
   1031             Cursor c = null;
   1032 
   1033             db.beginTransaction();
   1034             try {
   1035                 // Select and iterate through each matching widget
   1036                 c = db.query(TABLE_FAVORITES, new String[] { Favorites._ID, Favorites.ITEM_TYPE },
   1037                         selectWhere, null, null, null, null);
   1038 
   1039                 if (LOGD) Log.d(TAG, "found upgrade cursor count=" + c.getCount());
   1040 
   1041                 final ContentValues values = new ContentValues();
   1042                 while (c != null && c.moveToNext()) {
   1043                     long favoriteId = c.getLong(0);
   1044                     int favoriteType = c.getInt(1);
   1045 
   1046                     // Allocate and update database with new appWidgetId
   1047                     try {
   1048                         int appWidgetId = mAppWidgetHost.allocateAppWidgetId();
   1049 
   1050                         if (LOGD) {
   1051                             Log.d(TAG, "allocated appWidgetId=" + appWidgetId
   1052                                     + " for favoriteId=" + favoriteId);
   1053                         }
   1054                         values.clear();
   1055                         values.put(Favorites.ITEM_TYPE, Favorites.ITEM_TYPE_APPWIDGET);
   1056                         values.put(Favorites.APPWIDGET_ID, appWidgetId);
   1057 
   1058                         // Original widgets might not have valid spans when upgrading
   1059                         if (favoriteType == Favorites.ITEM_TYPE_WIDGET_SEARCH) {
   1060                             values.put(LauncherSettings.Favorites.SPANX, 4);
   1061                             values.put(LauncherSettings.Favorites.SPANY, 1);
   1062                         } else {
   1063                             values.put(LauncherSettings.Favorites.SPANX, 2);
   1064                             values.put(LauncherSettings.Favorites.SPANY, 2);
   1065                         }
   1066 
   1067                         String updateWhere = Favorites._ID + "=" + favoriteId;
   1068                         db.update(TABLE_FAVORITES, values, updateWhere, null);
   1069 
   1070                         if (favoriteType == Favorites.ITEM_TYPE_WIDGET_CLOCK) {
   1071                             // TODO: check return value
   1072                             appWidgetManager.bindAppWidgetIdIfAllowed(appWidgetId,
   1073                                     new ComponentName("com.android.alarmclock",
   1074                                     "com.android.alarmclock.AnalogAppWidgetProvider"));
   1075                         } else if (favoriteType == Favorites.ITEM_TYPE_WIDGET_PHOTO_FRAME) {
   1076                             // TODO: check return value
   1077                             appWidgetManager.bindAppWidgetIdIfAllowed(appWidgetId,
   1078                                     new ComponentName("com.android.camera",
   1079                                     "com.android.camera.PhotoAppWidgetProvider"));
   1080                         } else if (favoriteType == Favorites.ITEM_TYPE_WIDGET_SEARCH) {
   1081                             // TODO: check return value
   1082                             appWidgetManager.bindAppWidgetIdIfAllowed(appWidgetId,
   1083                                     getSearchWidgetProvider());
   1084                         }
   1085                     } catch (RuntimeException ex) {
   1086                         Log.e(TAG, "Problem allocating appWidgetId", ex);
   1087                     }
   1088                 }
   1089 
   1090                 db.setTransactionSuccessful();
   1091             } catch (SQLException ex) {
   1092                 Log.w(TAG, "Problem while allocating appWidgetIds for existing widgets", ex);
   1093             } finally {
   1094                 db.endTransaction();
   1095                 if (c != null) {
   1096                     c.close();
   1097                 }
   1098             }
   1099 
   1100             // Update max item id
   1101             mMaxItemId = initializeMaxItemId(db);
   1102             if (LOGD) Log.d(TAG, "mMaxItemId: " + mMaxItemId);
   1103         }
   1104 
   1105         private static final void beginDocument(XmlPullParser parser, String firstElementName)
   1106                 throws XmlPullParserException, IOException {
   1107             int type;
   1108             while ((type = parser.next()) != XmlPullParser.START_TAG
   1109                     && type != XmlPullParser.END_DOCUMENT) {
   1110                 ;
   1111             }
   1112 
   1113             if (type != XmlPullParser.START_TAG) {
   1114                 throw new XmlPullParserException("No start tag found");
   1115             }
   1116 
   1117             if (!parser.getName().equals(firstElementName)) {
   1118                 throw new XmlPullParserException("Unexpected start tag: found " + parser.getName() +
   1119                         ", expected " + firstElementName);
   1120             }
   1121         }
   1122 
   1123         /**
   1124          * Loads the default set of favorite packages from an xml file.
   1125          *
   1126          * @param db The database to write the values into
   1127          * @param filterContainerId The specific container id of items to load
   1128          */
   1129         private int loadFavorites(SQLiteDatabase db, int workspaceResourceId) {
   1130             Intent intent = new Intent(Intent.ACTION_MAIN, null);
   1131             intent.addCategory(Intent.CATEGORY_LAUNCHER);
   1132             ContentValues values = new ContentValues();
   1133 
   1134             if (LOGD) Log.v(TAG, String.format("Loading favorites from resid=0x%08x", workspaceResourceId));
   1135 
   1136             PackageManager packageManager = mContext.getPackageManager();
   1137             int i = 0;
   1138             try {
   1139                 XmlResourceParser parser = mContext.getResources().getXml(workspaceResourceId);
   1140                 AttributeSet attrs = Xml.asAttributeSet(parser);
   1141                 beginDocument(parser, TAG_FAVORITES);
   1142 
   1143                 final int depth = parser.getDepth();
   1144 
   1145                 int type;
   1146                 while (((type = parser.next()) != XmlPullParser.END_TAG ||
   1147                         parser.getDepth() > depth) && type != XmlPullParser.END_DOCUMENT) {
   1148 
   1149                     if (type != XmlPullParser.START_TAG) {
   1150                         continue;
   1151                     }
   1152 
   1153                     boolean added = false;
   1154                     final String name = parser.getName();
   1155 
   1156                     if (TAG_INCLUDE.equals(name)) {
   1157                         final TypedArray a = mContext.obtainStyledAttributes(attrs, R.styleable.Include);
   1158 
   1159                         final int resId = a.getResourceId(R.styleable.Include_workspace, 0);
   1160 
   1161                         if (LOGD) Log.v(TAG, String.format(("%" + (2*(depth+1)) + "s<include workspace=%08x>"),
   1162                                 "", resId));
   1163 
   1164                         if (resId != 0 && resId != workspaceResourceId) {
   1165                             // recursively load some more favorites, why not?
   1166                             i += loadFavorites(db, resId);
   1167                             added = false;
   1168                         } else {
   1169                             Log.w(TAG, String.format("Skipping <include workspace=0x%08x>", resId));
   1170                         }
   1171 
   1172                         a.recycle();
   1173 
   1174                         if (LOGD) Log.v(TAG, String.format(("%" + (2*(depth+1)) + "s</include>"), ""));
   1175                         continue;
   1176                     }
   1177 
   1178                     // Assuming it's a <favorite> at this point
   1179                     TypedArray a = mContext.obtainStyledAttributes(attrs, R.styleable.Favorite);
   1180 
   1181                     long container = LauncherSettings.Favorites.CONTAINER_DESKTOP;
   1182                     if (a.hasValue(R.styleable.Favorite_container)) {
   1183                         container = Long.valueOf(a.getString(R.styleable.Favorite_container));
   1184                     }
   1185 
   1186                     String screen = a.getString(R.styleable.Favorite_screen);
   1187                     String x = a.getString(R.styleable.Favorite_x);
   1188                     String y = a.getString(R.styleable.Favorite_y);
   1189 
   1190                     values.clear();
   1191                     values.put(LauncherSettings.Favorites.CONTAINER, container);
   1192                     values.put(LauncherSettings.Favorites.SCREEN, screen);
   1193                     values.put(LauncherSettings.Favorites.CELLX, x);
   1194                     values.put(LauncherSettings.Favorites.CELLY, y);
   1195 
   1196                     if (LOGD) {
   1197                         final String title = a.getString(R.styleable.Favorite_title);
   1198                         final String pkg = a.getString(R.styleable.Favorite_packageName);
   1199                         final String something = title != null ? title : pkg;
   1200                         Log.v(TAG, String.format(
   1201                                 ("%" + (2*(depth+1)) + "s<%s%s c=%d s=%s x=%s y=%s>"),
   1202                                 "", name,
   1203                                 (something == null ? "" : (" \"" + something + "\"")),
   1204                                 container, screen, x, y));
   1205                     }
   1206 
   1207                     if (TAG_FAVORITE.equals(name)) {
   1208                         long id = addAppShortcut(db, values, a, packageManager, intent);
   1209                         added = id >= 0;
   1210                     } else if (TAG_SEARCH.equals(name)) {
   1211                         added = addSearchWidget(db, values);
   1212                     } else if (TAG_CLOCK.equals(name)) {
   1213                         added = addClockWidget(db, values);
   1214                     } else if (TAG_APPWIDGET.equals(name)) {
   1215                         added = addAppWidget(parser, attrs, type, db, values, a, packageManager);
   1216                     } else if (TAG_SHORTCUT.equals(name)) {
   1217                         long id = addUriShortcut(db, values, a);
   1218                         added = id >= 0;
   1219                     } else if (TAG_FOLDER.equals(name)) {
   1220                         String title;
   1221                         int titleResId =  a.getResourceId(R.styleable.Favorite_title, -1);
   1222                         if (titleResId != -1) {
   1223                             title = mContext.getResources().getString(titleResId);
   1224                         } else {
   1225                             title = mContext.getResources().getString(R.string.folder_name);
   1226                         }
   1227                         values.put(LauncherSettings.Favorites.TITLE, title);
   1228                         long folderId = addFolder(db, values);
   1229                         added = folderId >= 0;
   1230 
   1231                         ArrayList<Long> folderItems = new ArrayList<Long>();
   1232 
   1233                         int folderDepth = parser.getDepth();
   1234                         while ((type = parser.next()) != XmlPullParser.END_TAG ||
   1235                                 parser.getDepth() > folderDepth) {
   1236                             if (type != XmlPullParser.START_TAG) {
   1237                                 continue;
   1238                             }
   1239                             final String folder_item_name = parser.getName();
   1240 
   1241                             TypedArray ar = mContext.obtainStyledAttributes(attrs,
   1242                                     R.styleable.Favorite);
   1243                             values.clear();
   1244                             values.put(LauncherSettings.Favorites.CONTAINER, folderId);
   1245 
   1246                             if (LOGD) {
   1247                                 final String pkg = ar.getString(R.styleable.Favorite_packageName);
   1248                                 final String uri = ar.getString(R.styleable.Favorite_uri);
   1249                                 Log.v(TAG, String.format(("%" + (2*(folderDepth+1)) + "s<%s \"%s\">"), "",
   1250                                         folder_item_name, uri != null ? uri : pkg));
   1251                             }
   1252 
   1253                             if (TAG_FAVORITE.equals(folder_item_name) && folderId >= 0) {
   1254                                 long id =
   1255                                     addAppShortcut(db, values, ar, packageManager, intent);
   1256                                 if (id >= 0) {
   1257                                     folderItems.add(id);
   1258                                 }
   1259                             } else if (TAG_SHORTCUT.equals(folder_item_name) && folderId >= 0) {
   1260                                 long id = addUriShortcut(db, values, ar);
   1261                                 if (id >= 0) {
   1262                                     folderItems.add(id);
   1263                                 }
   1264                             } else {
   1265                                 throw new RuntimeException("Folders can " +
   1266                                         "contain only shortcuts");
   1267                             }
   1268                             ar.recycle();
   1269                         }
   1270                         // We can only have folders with >= 2 items, so we need to remove the
   1271                         // folder and clean up if less than 2 items were included, or some
   1272                         // failed to add, and less than 2 were actually added
   1273                         if (folderItems.size() < 2 && folderId >= 0) {
   1274                             // We just delete the folder and any items that made it
   1275                             deleteId(db, folderId);
   1276                             if (folderItems.size() > 0) {
   1277                                 deleteId(db, folderItems.get(0));
   1278                             }
   1279                             added = false;
   1280                         }
   1281                     }
   1282                     if (added) i++;
   1283                     a.recycle();
   1284                 }
   1285             } catch (XmlPullParserException e) {
   1286                 Log.w(TAG, "Got exception parsing favorites.", e);
   1287             } catch (IOException e) {
   1288                 Log.w(TAG, "Got exception parsing favorites.", e);
   1289             } catch (RuntimeException e) {
   1290                 Log.w(TAG, "Got exception parsing favorites.", e);
   1291             }
   1292 
   1293             // Update the max item id after we have loaded the database
   1294             if (mMaxItemId == -1) {
   1295                 mMaxItemId = initializeMaxItemId(db);
   1296             }
   1297 
   1298             return i;
   1299         }
   1300 
   1301         private long addAppShortcut(SQLiteDatabase db, ContentValues values, TypedArray a,
   1302                 PackageManager packageManager, Intent intent) {
   1303             long id = -1;
   1304             ActivityInfo info;
   1305             String packageName = a.getString(R.styleable.Favorite_packageName);
   1306             String className = a.getString(R.styleable.Favorite_className);
   1307             try {
   1308                 ComponentName cn;
   1309                 try {
   1310                     cn = new ComponentName(packageName, className);
   1311                     info = packageManager.getActivityInfo(cn, 0);
   1312                 } catch (PackageManager.NameNotFoundException nnfe) {
   1313                     String[] packages = packageManager.currentToCanonicalPackageNames(
   1314                         new String[] { packageName });
   1315                     cn = new ComponentName(packages[0], className);
   1316                     info = packageManager.getActivityInfo(cn, 0);
   1317                 }
   1318                 id = generateNewItemId();
   1319                 intent.setComponent(cn);
   1320                 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK |
   1321                         Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
   1322                 values.put(Favorites.INTENT, intent.toUri(0));
   1323                 values.put(Favorites.TITLE, info.loadLabel(packageManager).toString());
   1324                 values.put(Favorites.ITEM_TYPE, Favorites.ITEM_TYPE_APPLICATION);
   1325                 values.put(Favorites.SPANX, 1);
   1326                 values.put(Favorites.SPANY, 1);
   1327                 values.put(Favorites._ID, generateNewItemId());
   1328                 if (dbInsertAndCheck(this, db, TABLE_FAVORITES, null, values) < 0) {
   1329                     return -1;
   1330                 }
   1331             } catch (PackageManager.NameNotFoundException e) {
   1332                 Log.w(TAG, "Unable to add favorite: " + packageName +
   1333                         "/" + className, e);
   1334             }
   1335             return id;
   1336         }
   1337 
   1338         private long addFolder(SQLiteDatabase db, ContentValues values) {
   1339             values.put(Favorites.ITEM_TYPE, Favorites.ITEM_TYPE_FOLDER);
   1340             values.put(Favorites.SPANX, 1);
   1341             values.put(Favorites.SPANY, 1);
   1342             long id = generateNewItemId();
   1343             values.put(Favorites._ID, id);
   1344             if (dbInsertAndCheck(this, db, TABLE_FAVORITES, null, values) <= 0) {
   1345                 return -1;
   1346             } else {
   1347                 return id;
   1348             }
   1349         }
   1350 
   1351         private ComponentName getSearchWidgetProvider() {
   1352             SearchManager searchManager =
   1353                     (SearchManager) mContext.getSystemService(Context.SEARCH_SERVICE);
   1354             ComponentName searchComponent = searchManager.getGlobalSearchActivity();
   1355             if (searchComponent == null) return null;
   1356             return getProviderInPackage(searchComponent.getPackageName());
   1357         }
   1358 
   1359         /**
   1360          * Gets an appwidget provider from the given package. If the package contains more than
   1361          * one appwidget provider, an arbitrary one is returned.
   1362          */
   1363         private ComponentName getProviderInPackage(String packageName) {
   1364             AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(mContext);
   1365             List<AppWidgetProviderInfo> providers = appWidgetManager.getInstalledProviders();
   1366             if (providers == null) return null;
   1367             final int providerCount = providers.size();
   1368             for (int i = 0; i < providerCount; i++) {
   1369                 ComponentName provider = providers.get(i).provider;
   1370                 if (provider != null && provider.getPackageName().equals(packageName)) {
   1371                     return provider;
   1372                 }
   1373             }
   1374             return null;
   1375         }
   1376 
   1377         private boolean addSearchWidget(SQLiteDatabase db, ContentValues values) {
   1378             ComponentName cn = getSearchWidgetProvider();
   1379             return addAppWidget(db, values, cn, 4, 1, null);
   1380         }
   1381 
   1382         private boolean addClockWidget(SQLiteDatabase db, ContentValues values) {
   1383             ComponentName cn = new ComponentName("com.android.alarmclock",
   1384                     "com.android.alarmclock.AnalogAppWidgetProvider");
   1385             return addAppWidget(db, values, cn, 2, 2, null);
   1386         }
   1387 
   1388         private boolean addAppWidget(XmlResourceParser parser, AttributeSet attrs, int type,
   1389                 SQLiteDatabase db, ContentValues values, TypedArray a,
   1390                 PackageManager packageManager) throws XmlPullParserException, IOException {
   1391 
   1392             String packageName = a.getString(R.styleable.Favorite_packageName);
   1393             String className = a.getString(R.styleable.Favorite_className);
   1394 
   1395             if (packageName == null || className == null) {
   1396                 return false;
   1397             }
   1398 
   1399             boolean hasPackage = true;
   1400             ComponentName cn = new ComponentName(packageName, className);
   1401             try {
   1402                 packageManager.getReceiverInfo(cn, 0);
   1403             } catch (Exception e) {
   1404                 String[] packages = packageManager.currentToCanonicalPackageNames(
   1405                         new String[] { packageName });
   1406                 cn = new ComponentName(packages[0], className);
   1407                 try {
   1408                     packageManager.getReceiverInfo(cn, 0);
   1409                 } catch (Exception e1) {
   1410                     hasPackage = false;
   1411                 }
   1412             }
   1413 
   1414             if (hasPackage) {
   1415                 int spanX = a.getInt(R.styleable.Favorite_spanX, 0);
   1416                 int spanY = a.getInt(R.styleable.Favorite_spanY, 0);
   1417 
   1418                 // Read the extras
   1419                 Bundle extras = new Bundle();
   1420                 int widgetDepth = parser.getDepth();
   1421                 while ((type = parser.next()) != XmlPullParser.END_TAG ||
   1422                         parser.getDepth() > widgetDepth) {
   1423                     if (type != XmlPullParser.START_TAG) {
   1424                         continue;
   1425                     }
   1426 
   1427                     TypedArray ar = mContext.obtainStyledAttributes(attrs, R.styleable.Extra);
   1428                     if (TAG_EXTRA.equals(parser.getName())) {
   1429                         String key = ar.getString(R.styleable.Extra_key);
   1430                         String value = ar.getString(R.styleable.Extra_value);
   1431                         if (key != null && value != null) {
   1432                             extras.putString(key, value);
   1433                         } else {
   1434                             throw new RuntimeException("Widget extras must have a key and value");
   1435                         }
   1436                     } else {
   1437                         throw new RuntimeException("Widgets can contain only extras");
   1438                     }
   1439                     ar.recycle();
   1440                 }
   1441 
   1442                 return addAppWidget(db, values, cn, spanX, spanY, extras);
   1443             }
   1444 
   1445             return false;
   1446         }
   1447         private boolean addAppWidget(SQLiteDatabase db, ContentValues values, ComponentName cn,
   1448                 int spanX, int spanY, Bundle extras) {
   1449             boolean allocatedAppWidgets = false;
   1450             final AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(mContext);
   1451 
   1452             try {
   1453                 int appWidgetId = mAppWidgetHost.allocateAppWidgetId();
   1454 
   1455                 values.put(Favorites.ITEM_TYPE, Favorites.ITEM_TYPE_APPWIDGET);
   1456                 values.put(Favorites.SPANX, spanX);
   1457                 values.put(Favorites.SPANY, spanY);
   1458                 values.put(Favorites.APPWIDGET_ID, appWidgetId);
   1459                 values.put(Favorites.APPWIDGET_PROVIDER, cn.flattenToString());
   1460                 values.put(Favorites._ID, generateNewItemId());
   1461                 dbInsertAndCheck(this, db, TABLE_FAVORITES, null, values);
   1462 
   1463                 allocatedAppWidgets = true;
   1464 
   1465                 // TODO: need to check return value
   1466                 appWidgetManager.bindAppWidgetIdIfAllowed(appWidgetId, cn);
   1467 
   1468                 // Send a broadcast to configure the widget
   1469                 if (extras != null && !extras.isEmpty()) {
   1470                     Intent intent = new Intent(ACTION_APPWIDGET_DEFAULT_WORKSPACE_CONFIGURE);
   1471                     intent.setComponent(cn);
   1472                     intent.putExtras(extras);
   1473                     intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);
   1474                     mContext.sendBroadcast(intent);
   1475                 }
   1476             } catch (RuntimeException ex) {
   1477                 Log.e(TAG, "Problem allocating appWidgetId", ex);
   1478             }
   1479 
   1480             return allocatedAppWidgets;
   1481         }
   1482 
   1483         private long addUriShortcut(SQLiteDatabase db, ContentValues values,
   1484                 TypedArray a) {
   1485             Resources r = mContext.getResources();
   1486 
   1487             final int iconResId = a.getResourceId(R.styleable.Favorite_icon, 0);
   1488             final int titleResId = a.getResourceId(R.styleable.Favorite_title, 0);
   1489 
   1490             Intent intent;
   1491             String uri = null;
   1492             try {
   1493                 uri = a.getString(R.styleable.Favorite_uri);
   1494                 intent = Intent.parseUri(uri, 0);
   1495             } catch (URISyntaxException e) {
   1496                 Log.w(TAG, "Shortcut has malformed uri: " + uri);
   1497                 return -1; // Oh well
   1498             }
   1499 
   1500             if (iconResId == 0 || titleResId == 0) {
   1501                 Log.w(TAG, "Shortcut is missing title or icon resource ID");
   1502                 return -1;
   1503             }
   1504 
   1505             long id = generateNewItemId();
   1506             intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
   1507             values.put(Favorites.INTENT, intent.toUri(0));
   1508             values.put(Favorites.TITLE, r.getString(titleResId));
   1509             values.put(Favorites.ITEM_TYPE, Favorites.ITEM_TYPE_SHORTCUT);
   1510             values.put(Favorites.SPANX, 1);
   1511             values.put(Favorites.SPANY, 1);
   1512             values.put(Favorites.ICON_TYPE, Favorites.ICON_TYPE_RESOURCE);
   1513             values.put(Favorites.ICON_PACKAGE, mContext.getPackageName());
   1514             values.put(Favorites.ICON_RESOURCE, r.getResourceName(iconResId));
   1515             values.put(Favorites._ID, id);
   1516 
   1517             if (dbInsertAndCheck(this, db, TABLE_FAVORITES, null, values) < 0) {
   1518                 return -1;
   1519             }
   1520             return id;
   1521         }
   1522 
   1523         public void migrateLauncher2Shortcuts(SQLiteDatabase db, Uri uri) {
   1524             final ContentResolver resolver = mContext.getContentResolver();
   1525             Cursor c = null;
   1526             int count = 0;
   1527             int curScreen = 0;
   1528 
   1529             try {
   1530                 c = resolver.query(uri, null, null, null, "title ASC");
   1531             } catch (Exception e) {
   1532                 // Ignore
   1533             }
   1534 
   1535             // We already have a favorites database in the old provider
   1536             if (c != null) {
   1537                 try {
   1538                     if (c.getCount() > 0) {
   1539                         final int idIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites._ID);
   1540                         final int intentIndex
   1541                                 = c.getColumnIndexOrThrow(LauncherSettings.Favorites.INTENT);
   1542                         final int titleIndex
   1543                                 = c.getColumnIndexOrThrow(LauncherSettings.Favorites.TITLE);
   1544                         final int iconTypeIndex
   1545                                 = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ICON_TYPE);
   1546                         final int iconIndex
   1547                                 = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ICON);
   1548                         final int iconPackageIndex
   1549                                 = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ICON_PACKAGE);
   1550                         final int iconResourceIndex
   1551                                 = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ICON_RESOURCE);
   1552                         final int containerIndex
   1553                                 = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CONTAINER);
   1554                         final int itemTypeIndex
   1555                                 = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ITEM_TYPE);
   1556                         final int screenIndex
   1557                                 = c.getColumnIndexOrThrow(LauncherSettings.Favorites.SCREEN);
   1558                         final int cellXIndex
   1559                                 = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CELLX);
   1560                         final int cellYIndex
   1561                                 = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CELLY);
   1562                         final int uriIndex
   1563                                 = c.getColumnIndexOrThrow(LauncherSettings.Favorites.URI);
   1564                         final int displayModeIndex
   1565                                 = c.getColumnIndexOrThrow(LauncherSettings.Favorites.DISPLAY_MODE);
   1566 
   1567                         int i = 0;
   1568                         int curX = 0;
   1569                         int curY = 0;
   1570 
   1571                         final LauncherAppState app = LauncherAppState.getInstance();
   1572                         final DeviceProfile grid = app.getDynamicGrid().getDeviceProfile();
   1573                         final int width = (int) grid.numColumns;
   1574                         final int height = (int) grid.numRows;
   1575                         final int hotseatWidth = (int) grid.numHotseatIcons;
   1576                         PackageManager pm = mContext.getPackageManager();
   1577 
   1578                         final HashSet<String> seenIntents = new HashSet<String>(c.getCount());
   1579 
   1580                         final ArrayList<ContentValues> shortcuts = new ArrayList<ContentValues>();
   1581                         final ArrayList<ContentValues> folders = new ArrayList<ContentValues>();
   1582                         final SparseArray<ContentValues> hotseat = new SparseArray<ContentValues>();
   1583 
   1584                         while (c.moveToNext()) {
   1585                             final int itemType = c.getInt(itemTypeIndex);
   1586                             if (itemType != Favorites.ITEM_TYPE_APPLICATION
   1587                                     && itemType != Favorites.ITEM_TYPE_SHORTCUT
   1588                                     && itemType != Favorites.ITEM_TYPE_FOLDER) {
   1589                                 continue;
   1590                             }
   1591 
   1592                             final int cellX = c.getInt(cellXIndex);
   1593                             final int cellY = c.getInt(cellYIndex);
   1594                             final int screen = c.getInt(screenIndex);
   1595                             int container = c.getInt(containerIndex);
   1596                             final String intentStr = c.getString(intentIndex);
   1597                             Launcher.addDumpLog(TAG, "migrating \""
   1598                                 + c.getString(titleIndex) + "\" ("
   1599                                 + cellX + "," + cellY + "@"
   1600                                 + LauncherSettings.Favorites.containerToString(container)
   1601                                 + "/" + screen
   1602                                 + "): " + intentStr, true);
   1603 
   1604                             if (itemType != Favorites.ITEM_TYPE_FOLDER) {
   1605 
   1606                                 final Intent intent;
   1607                                 final ComponentName cn;
   1608                                 try {
   1609                                     intent = Intent.parseUri(intentStr, 0);
   1610                                 } catch (URISyntaxException e) {
   1611                                     // bogus intent?
   1612                                     Launcher.addDumpLog(TAG,
   1613                                             "skipping invalid intent uri", true);
   1614                                     continue;
   1615                                 }
   1616 
   1617                                 cn = intent.getComponent();
   1618                                 if (TextUtils.isEmpty(intentStr)) {
   1619                                     // no intent? no icon
   1620                                     Launcher.addDumpLog(TAG, "skipping empty intent", true);
   1621                                     continue;
   1622                                 } else if (cn != null &&
   1623                                         !LauncherModel.isValidPackageComponent(pm, cn)) {
   1624                                     // component no longer exists.
   1625                                     Launcher.addDumpLog(TAG, "skipping item whose component " +
   1626                                             "no longer exists.", true);
   1627                                     continue;
   1628                                 } else if (container ==
   1629                                         LauncherSettings.Favorites.CONTAINER_DESKTOP) {
   1630                                     // Dedupe icons directly on the workspace
   1631 
   1632                                     // Canonicalize
   1633                                     // the Play Store sets the package parameter, but Launcher
   1634                                     // does not, so we clear that out to keep them the same
   1635                                     intent.setPackage(null);
   1636                                     final String key = intent.toUri(0);
   1637                                     if (seenIntents.contains(key)) {
   1638                                         Launcher.addDumpLog(TAG, "skipping duplicate", true);
   1639                                         continue;
   1640                                     } else {
   1641                                         seenIntents.add(key);
   1642                                     }
   1643                                 }
   1644                             }
   1645 
   1646                             ContentValues values = new ContentValues(c.getColumnCount());
   1647                             values.put(LauncherSettings.Favorites._ID, c.getInt(idIndex));
   1648                             values.put(LauncherSettings.Favorites.INTENT, intentStr);
   1649                             values.put(LauncherSettings.Favorites.TITLE, c.getString(titleIndex));
   1650                             values.put(LauncherSettings.Favorites.ICON_TYPE,
   1651                                     c.getInt(iconTypeIndex));
   1652                             values.put(LauncherSettings.Favorites.ICON, c.getBlob(iconIndex));
   1653                             values.put(LauncherSettings.Favorites.ICON_PACKAGE,
   1654                                     c.getString(iconPackageIndex));
   1655                             values.put(LauncherSettings.Favorites.ICON_RESOURCE,
   1656                                     c.getString(iconResourceIndex));
   1657                             values.put(LauncherSettings.Favorites.ITEM_TYPE, itemType);
   1658                             values.put(LauncherSettings.Favorites.APPWIDGET_ID, -1);
   1659                             values.put(LauncherSettings.Favorites.URI, c.getString(uriIndex));
   1660                             values.put(LauncherSettings.Favorites.DISPLAY_MODE,
   1661                                     c.getInt(displayModeIndex));
   1662 
   1663                             if (container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
   1664                                 hotseat.put(screen, values);
   1665                             }
   1666 
   1667                             if (container != LauncherSettings.Favorites.CONTAINER_DESKTOP) {
   1668                                 // In a folder or in the hotseat, preserve position
   1669                                 values.put(LauncherSettings.Favorites.SCREEN, screen);
   1670                                 values.put(LauncherSettings.Favorites.CELLX, cellX);
   1671                                 values.put(LauncherSettings.Favorites.CELLY, cellY);
   1672                             } else {
   1673                                 // For items contained directly on one of the workspace screen,
   1674                                 // we'll determine their location (screen, x, y) in a second pass.
   1675                             }
   1676 
   1677                             values.put(LauncherSettings.Favorites.CONTAINER, container);
   1678 
   1679                             if (itemType != Favorites.ITEM_TYPE_FOLDER) {
   1680                                 shortcuts.add(values);
   1681                             } else {
   1682                                 folders.add(values);
   1683                             }
   1684                         }
   1685 
   1686                         // Now that we have all the hotseat icons, let's go through them left-right
   1687                         // and assign valid locations for them in the new hotseat
   1688                         final int N = hotseat.size();
   1689                         for (int idx=0; idx<N; idx++) {
   1690                             int hotseatX = hotseat.keyAt(idx);
   1691                             ContentValues values = hotseat.valueAt(idx);
   1692 
   1693                             if (hotseatX == grid.hotseatAllAppsRank) {
   1694                                 // let's drop this in the next available hole in the hotseat
   1695                                 while (++hotseatX < hotseatWidth) {
   1696                                     if (hotseat.get(hotseatX) == null) {
   1697                                         // found a spot! move it here
   1698                                         values.put(LauncherSettings.Favorites.SCREEN,
   1699                                                 hotseatX);
   1700                                         break;
   1701                                     }
   1702                                 }
   1703                             }
   1704                             if (hotseatX >= hotseatWidth) {
   1705                                 // no room for you in the hotseat? it's off to the desktop with you
   1706                                 values.put(LauncherSettings.Favorites.CONTAINER,
   1707                                            Favorites.CONTAINER_DESKTOP);
   1708                             }
   1709                         }
   1710 
   1711                         final ArrayList<ContentValues> allItems = new ArrayList<ContentValues>();
   1712                         // Folders first
   1713                         allItems.addAll(folders);
   1714                         // Then shortcuts
   1715                         allItems.addAll(shortcuts);
   1716 
   1717                         // Layout all the folders
   1718                         for (ContentValues values: allItems) {
   1719                             if (values.getAsInteger(LauncherSettings.Favorites.CONTAINER) !=
   1720                                     LauncherSettings.Favorites.CONTAINER_DESKTOP) {
   1721                                 // Hotseat items and folder items have already had their
   1722                                 // location information set. Nothing to be done here.
   1723                                 continue;
   1724                             }
   1725                             values.put(LauncherSettings.Favorites.SCREEN, curScreen);
   1726                             values.put(LauncherSettings.Favorites.CELLX, curX);
   1727                             values.put(LauncherSettings.Favorites.CELLY, curY);
   1728                             curX = (curX + 1) % width;
   1729                             if (curX == 0) {
   1730                                 curY = (curY + 1);
   1731                             }
   1732                             // Leave the last row of icons blank on every screen
   1733                             if (curY == height - 1) {
   1734                                 curScreen = (int) generateNewScreenId();
   1735                                 curY = 0;
   1736                             }
   1737                         }
   1738 
   1739                         if (allItems.size() > 0) {
   1740                             db.beginTransaction();
   1741                             try {
   1742                                 for (ContentValues row: allItems) {
   1743                                     if (row == null) continue;
   1744                                     if (dbInsertAndCheck(this, db, TABLE_FAVORITES, null, row)
   1745                                             < 0) {
   1746                                         return;
   1747                                     } else {
   1748                                         count++;
   1749                                     }
   1750                                 }
   1751                                 db.setTransactionSuccessful();
   1752                             } finally {
   1753                                 db.endTransaction();
   1754                             }
   1755                         }
   1756 
   1757                         db.beginTransaction();
   1758                         try {
   1759                             for (i=0; i<=curScreen; i++) {
   1760                                 final ContentValues values = new ContentValues();
   1761                                 values.put(LauncherSettings.WorkspaceScreens._ID, i);
   1762                                 values.put(LauncherSettings.WorkspaceScreens.SCREEN_RANK, i);
   1763                                 if (dbInsertAndCheck(this, db, TABLE_WORKSPACE_SCREENS, null, values)
   1764                                         < 0) {
   1765                                     return;
   1766                                 }
   1767                             }
   1768                             db.setTransactionSuccessful();
   1769                         } finally {
   1770                             db.endTransaction();
   1771                         }
   1772                     }
   1773                 } finally {
   1774                     c.close();
   1775                 }
   1776             }
   1777 
   1778             Launcher.addDumpLog(TAG, "migrated " + count + " icons from Launcher2 into "
   1779                     + (curScreen+1) + " screens", true);
   1780 
   1781             // ensure that new screens are created to hold these icons
   1782             setFlagJustLoadedOldDb();
   1783 
   1784             // Update max IDs; very important since we just grabbed IDs from another database
   1785             mMaxItemId = initializeMaxItemId(db);
   1786             mMaxScreenId = initializeMaxScreenId(db);
   1787             if (LOGD) Log.d(TAG, "mMaxItemId: " + mMaxItemId + " mMaxScreenId: " + mMaxScreenId);
   1788         }
   1789     }
   1790 
   1791     /**
   1792      * Build a query string that will match any row where the column matches
   1793      * anything in the values list.
   1794      */
   1795     static String buildOrWhereString(String column, int[] values) {
   1796         StringBuilder selectWhere = new StringBuilder();
   1797         for (int i = values.length - 1; i >= 0; i--) {
   1798             selectWhere.append(column).append("=").append(values[i]);
   1799             if (i > 0) {
   1800                 selectWhere.append(" OR ");
   1801             }
   1802         }
   1803         return selectWhere.toString();
   1804     }
   1805 
   1806     static class SqlArguments {
   1807         public final String table;
   1808         public final String where;
   1809         public final String[] args;
   1810 
   1811         SqlArguments(Uri url, String where, String[] args) {
   1812             if (url.getPathSegments().size() == 1) {
   1813                 this.table = url.getPathSegments().get(0);
   1814                 this.where = where;
   1815                 this.args = args;
   1816             } else if (url.getPathSegments().size() != 2) {
   1817                 throw new IllegalArgumentException("Invalid URI: " + url);
   1818             } else if (!TextUtils.isEmpty(where)) {
   1819                 throw new UnsupportedOperationException("WHERE clause not supported: " + url);
   1820             } else {
   1821                 this.table = url.getPathSegments().get(0);
   1822                 this.where = "_id=" + ContentUris.parseId(url);
   1823                 this.args = null;
   1824             }
   1825         }
   1826 
   1827         SqlArguments(Uri url) {
   1828             if (url.getPathSegments().size() == 1) {
   1829                 table = url.getPathSegments().get(0);
   1830                 where = null;
   1831                 args = null;
   1832             } else {
   1833                 throw new IllegalArgumentException("Invalid URI: " + url);
   1834             }
   1835         }
   1836     }
   1837 }
   1838