131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project/*
231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project * Copyright (C) 2008 The Android Open Source Project
331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project *
431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project * Licensed under the Apache License, Version 2.0 (the "License");
531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project * you may not use this file except in compliance with the License.
631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project * You may obtain a copy of the License at
731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project *
831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project *      http://www.apache.org/licenses/LICENSE-2.0
931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project *
1031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project * Unless required by applicable law or agreed to in writing, software
1131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project * distributed under the License is distributed on an "AS IS" BASIS,
1231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project * See the License for the specific language governing permissions and
1431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project * limitations under the License.
1531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project */
1631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
17325dc23624160689e59fbac708cf6f222b20d025Daniel Sandlerpackage com.android.launcher3;
1831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
19b2d46ceaaa32ece85f18e049d9e30a5ccaf80e45Sunny Goyalimport android.annotation.TargetApi;
207376faefbbcbe30cc4e3f706ab95c254a4707d98The Android Open Source Projectimport android.appwidget.AppWidgetHost;
21b87bd16c8e3f88a9ba78ed00c9f150b38dc39f33Mike Cleronimport android.appwidget.AppWidgetManager;
22228da5a24e5a680cfb7bec14a24d20b5cb595a34Adam Cohenimport android.content.ComponentName;
2331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Projectimport android.content.ContentProvider;
24085c853a5702c45865e9b017d21fa15cf2b151b9Yuraimport android.content.ContentProviderOperation;
25085c853a5702c45865e9b017d21fa15cf2b151b9Yuraimport android.content.ContentProviderResult;
26228da5a24e5a680cfb7bec14a24d20b5cb595a34Adam Cohenimport android.content.ContentUris;
2731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Projectimport android.content.ContentValues;
28228da5a24e5a680cfb7bec14a24d20b5cb595a34Adam Cohenimport android.content.Context;
2931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Projectimport android.content.Intent;
30085c853a5702c45865e9b017d21fa15cf2b151b9Yuraimport android.content.OperationApplicationException;
31b85f8a44b51258f22938773ca30dd85845345010Michael Jurkaimport android.content.SharedPreferences;
32b2d46ceaaa32ece85f18e049d9e30a5ccaf80e45Sunny Goyalimport android.content.pm.PackageManager.NameNotFoundException;
33b87bd16c8e3f88a9ba78ed00c9f150b38dc39f33Mike Cleronimport android.content.res.Resources;
3431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Projectimport android.database.Cursor;
3531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Projectimport android.database.SQLException;
36228da5a24e5a680cfb7bec14a24d20b5cb595a34Adam Cohenimport android.database.sqlite.SQLiteDatabase;
37228da5a24e5a680cfb7bec14a24d20b5cb595a34Adam Cohenimport android.database.sqlite.SQLiteOpenHelper;
38228da5a24e5a680cfb7bec14a24d20b5cb595a34Adam Cohenimport android.database.sqlite.SQLiteQueryBuilder;
390b037789662d1c16627dc5b509d1ac55bc76971bSunny Goyalimport android.database.sqlite.SQLiteStatement;
4031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Projectimport android.net.Uri;
417779d62308b87ca26e3be47df836893f6f7693ecSunny Goyalimport android.os.Binder;
42b2d46ceaaa32ece85f18e049d9e30a5ccaf80e45Sunny Goyalimport android.os.Build;
43b2d46ceaaa32ece85f18e049d9e30a5ccaf80e45Sunny Goyalimport android.os.Bundle;
44b5b55c8012c124182c1bd07bdd4be1e734d972d0Sunny Goyalimport android.os.Handler;
45b5b55c8012c124182c1bd07bdd4be1e734d972d0Sunny Goyalimport android.os.Message;
467779d62308b87ca26e3be47df836893f6f7693ecSunny Goyalimport android.os.Process;
47e26d1008eb587303db25797ca02cc32464e45656Sunny Goyalimport android.os.Trace;
48b2d46ceaaa32ece85f18e049d9e30a5ccaf80e45Sunny Goyalimport android.os.UserManager;
49228da5a24e5a680cfb7bec14a24d20b5cb595a34Adam Cohenimport android.text.TextUtils;
50228da5a24e5a680cfb7bec14a24d20b5cb595a34Adam Cohenimport android.util.Log;
51094fc7ac6daef2463a8885499ae80bcf1ae7f849Dianne Hackborn
520fe505bf82a265e51c556d7204976651cde7f55cSunny Goyalimport com.android.launcher3.AutoInstallsLayout.LayoutParserCallback;
530fe505bf82a265e51c556d7204976651cde7f55cSunny Goyalimport com.android.launcher3.LauncherSettings.Favorites;
54b5b55c8012c124182c1bd07bdd4be1e734d972d0Sunny Goyalimport com.android.launcher3.LauncherSettings.WorkspaceScreens;
55ed13187a745866483139e2878037e1f8427ce567Kenny Guyimport com.android.launcher3.compat.UserHandleCompat;
56ed13187a745866483139e2878037e1f8427ce567Kenny Guyimport com.android.launcher3.compat.UserManagerCompat;
57a9e2f5abb3c21d9721939c625ffb0caabb34e8d9Sunny Goyalimport com.android.launcher3.config.FeatureFlags;
58e523e70f75ff90ca5d613fb8398d37208e6cd32dChris Wrenimport com.android.launcher3.config.ProviderConfig;
59827cef203f386cb548b98a7fa9888b75478c8e20Tony Wickhamimport com.android.launcher3.dynamicui.ExtractionUtils;
60a9e2f5abb3c21d9721939c625ffb0caabb34e8d9Sunny Goyalimport com.android.launcher3.provider.LauncherDbUtils;
61e8f7d5a67ef71b1c6488f52145482a8876c1b708Sunny Goyalimport com.android.launcher3.provider.RestoreDbTask;
62e2fba6cba10d9b7996ebb86dfd67bed96cff0dedSunny Goyalimport com.android.launcher3.util.ManagedProfileHeuristic;
63bf67f3b1847c435bbaf19600ec7babbb9d4c7674Sunny Goyalimport com.android.launcher3.util.NoLocaleSqliteContext;
64b5b55c8012c124182c1bd07bdd4be1e734d972d0Sunny Goyalimport com.android.launcher3.util.Preconditions;
65091440a9cb9d4f42406631004aa484cbb79214caAdam Cohenimport com.android.launcher3.util.Thunk;
668b805b17158886035b38261eb611d8641701ae43Michael Jurka
67228da5a24e5a680cfb7bec14a24d20b5cb595a34Adam Cohenimport java.net.URISyntaxException;
68228da5a24e5a680cfb7bec14a24d20b5cb595a34Adam Cohenimport java.util.ArrayList;
6971483f417bb19f524cda41081f5ccac6084dc103Adam Cohenimport java.util.Collections;
70a9c21c624ca15fe7aa5e65e17cdd9fb26d1ec691Louis Beginimport java.util.Locale;
71edcce099c98a6c40d10109ac092ab50f9d2668f3Romain Guy
7231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Projectpublic class LauncherProvider extends ContentProvider {
73c74e41973a74aa416e16aeadaa2f491153da26bcSunny Goyal    private static final String TAG = "LauncherProvider";
74a30ce8e6b25e41f392a41fd4d0d3e0a424a84dadJoe Onorato    private static final boolean LOGD = false;
7531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
76a9e2f5abb3c21d9721939c625ffb0caabb34e8d9Sunny Goyal    private static final int DATABASE_VERSION = 27;
7731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
78e5bb705fb79f18df8680958dcf2c5460e16c90b6Sunny Goyal    public static final String AUTHORITY = ProviderConfig.AUTHORITY;
793d503fbd9468fb2b9fa645f4f7b91e11229edbfaWinson Chung
80e87e6abc3639559f2746171999fee352cb5bd946Sunny Goyal    static final String EMPTY_DATABASE_CREATED = "EMPTY_DATABASE_CREATED";
8131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
82b2d46ceaaa32ece85f18e049d9e30a5ccaf80e45Sunny Goyal    private static final String RESTRICTION_PACKAGE_NAME = "workspace.configuration.package.name";
83b2d46ceaaa32ece85f18e049d9e30a5ccaf80e45Sunny Goyal
84b5b55c8012c124182c1bd07bdd4be1e734d972d0Sunny Goyal    private final ChangeListenerWrapper mListenerWrapper = new ChangeListenerWrapper();
85b5b55c8012c124182c1bd07bdd4be1e734d972d0Sunny Goyal    private Handler mListenerHandler;
86b5b55c8012c124182c1bd07bdd4be1e734d972d0Sunny Goyal
87f076eae0cab10f035f7b187c72a680cd220acf1bSunny Goyal    protected DatabaseHelper mOpenHelper;
8831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
8931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    @Override
9031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    public boolean onCreate() {
91e26d1008eb587303db25797ca02cc32464e45656Sunny Goyal        if (ProviderConfig.IS_DOGFOOD_BUILD) {
92e26d1008eb587303db25797ca02cc32464e45656Sunny Goyal            Log.d(TAG, "Launcher process started");
93e26d1008eb587303db25797ca02cc32464e45656Sunny Goyal        }
94b5b55c8012c124182c1bd07bdd4be1e734d972d0Sunny Goyal        mListenerHandler = new Handler(mListenerWrapper);
95b5b55c8012c124182c1bd07bdd4be1e734d972d0Sunny Goyal
96e4f9891f01bdc10d8f96e4e2429e2f4d0558238bDaniel Sandler        LauncherAppState.setLauncherProvider(this);
9731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        return true;
9831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    }
9931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
100d3849d191bc0bd2d561cf03018ad90b896b13c56Sunny Goyal    /**
101d3849d191bc0bd2d561cf03018ad90b896b13c56Sunny Goyal     * Sets a provider listener.
102d3849d191bc0bd2d561cf03018ad90b896b13c56Sunny Goyal     */
10367e7cae5ad97ec5702bbd739c4272e3cfa17556dAnjali Koppal    public void setLauncherProviderChangeListener(LauncherProviderChangeListener listener) {
104b5b55c8012c124182c1bd07bdd4be1e734d972d0Sunny Goyal        Preconditions.assertUIThread();
105b5b55c8012c124182c1bd07bdd4be1e734d972d0Sunny Goyal        mListenerWrapper.mListener = listener;
10667e7cae5ad97ec5702bbd739c4272e3cfa17556dAnjali Koppal    }
10767e7cae5ad97ec5702bbd739c4272e3cfa17556dAnjali Koppal
10831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    @Override
10931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    public String getType(Uri uri) {
11031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        SqlArguments args = new SqlArguments(uri, null, null);
11131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        if (TextUtils.isEmpty(args.where)) {
11231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            return "vnd.android.cursor.dir/" + args.table;
11331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        } else {
11431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            return "vnd.android.cursor.item/" + args.table;
11531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        }
11631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    }
11731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
118f076eae0cab10f035f7b187c72a680cd220acf1bSunny Goyal    /**
119f076eae0cab10f035f7b187c72a680cd220acf1bSunny Goyal     * Overridden in tests
120f076eae0cab10f035f7b187c72a680cd220acf1bSunny Goyal     */
121f076eae0cab10f035f7b187c72a680cd220acf1bSunny Goyal    protected synchronized void createDbIfNotExists() {
122d3849d191bc0bd2d561cf03018ad90b896b13c56Sunny Goyal        if (mOpenHelper == null) {
123e26d1008eb587303db25797ca02cc32464e45656Sunny Goyal            if (LauncherAppState.PROFILE_STARTUP) {
124e26d1008eb587303db25797ca02cc32464e45656Sunny Goyal                Trace.beginSection("Opening workspace DB");
125e26d1008eb587303db25797ca02cc32464e45656Sunny Goyal            }
126b5b55c8012c124182c1bd07bdd4be1e734d972d0Sunny Goyal            mOpenHelper = new DatabaseHelper(getContext(), mListenerHandler);
127e8f7d5a67ef71b1c6488f52145482a8876c1b708Sunny Goyal
128e8f7d5a67ef71b1c6488f52145482a8876c1b708Sunny Goyal            if (RestoreDbTask.isPending(getContext())) {
129e8f7d5a67ef71b1c6488f52145482a8876c1b708Sunny Goyal                if (!RestoreDbTask.performRestore(mOpenHelper)) {
130e8f7d5a67ef71b1c6488f52145482a8876c1b708Sunny Goyal                    mOpenHelper.createEmptyDB(mOpenHelper.getWritableDatabase());
131e8f7d5a67ef71b1c6488f52145482a8876c1b708Sunny Goyal                }
132e8f7d5a67ef71b1c6488f52145482a8876c1b708Sunny Goyal                // Set is pending to false irrespective of the result, so that it doesn't get
133e8f7d5a67ef71b1c6488f52145482a8876c1b708Sunny Goyal                // executed again.
134e8f7d5a67ef71b1c6488f52145482a8876c1b708Sunny Goyal                RestoreDbTask.setPending(getContext(), false);
135e8f7d5a67ef71b1c6488f52145482a8876c1b708Sunny Goyal            }
136e26d1008eb587303db25797ca02cc32464e45656Sunny Goyal
137e26d1008eb587303db25797ca02cc32464e45656Sunny Goyal            if (LauncherAppState.PROFILE_STARTUP) {
138e26d1008eb587303db25797ca02cc32464e45656Sunny Goyal                Trace.endSection();
139e26d1008eb587303db25797ca02cc32464e45656Sunny Goyal            }
140d3849d191bc0bd2d561cf03018ad90b896b13c56Sunny Goyal        }
141d3849d191bc0bd2d561cf03018ad90b896b13c56Sunny Goyal    }
142d3849d191bc0bd2d561cf03018ad90b896b13c56Sunny Goyal
14331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    @Override
14431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    public Cursor query(Uri uri, String[] projection, String selection,
14531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            String[] selectionArgs, String sortOrder) {
146d3849d191bc0bd2d561cf03018ad90b896b13c56Sunny Goyal        createDbIfNotExists();
14731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
14831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        SqlArguments args = new SqlArguments(uri, selection, selectionArgs);
14931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
15031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        qb.setTables(args.table);
15131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
15273b979d8c141c7ceac82dad7c5b271a6a42afa67Romain Guy        SQLiteDatabase db = mOpenHelper.getWritableDatabase();
15331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        Cursor result = qb.query(db, projection, args.where, args.args, null, null, sortOrder);
15431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        result.setNotificationUri(getContext().getContentResolver(), uri);
15531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
15631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        return result;
15731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    }
15831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
159091440a9cb9d4f42406631004aa484cbb79214caAdam Cohen    @Thunk static long dbInsertAndCheck(DatabaseHelper helper,
160a8c760d395e1d2a78522427738302fbca3a72453Michael Jurka            SQLiteDatabase db, String table, String nullColumnHack, ContentValues values) {
161d502404a44fb7c4ea739622d7f8bdd2a764d97a1Dan Sandler        if (values == null) {
162d502404a44fb7c4ea739622d7f8bdd2a764d97a1Dan Sandler            throw new RuntimeException("Error: attempting to insert null values");
163d502404a44fb7c4ea739622d7f8bdd2a764d97a1Dan Sandler        }
16471483f417bb19f524cda41081f5ccac6084dc103Adam Cohen        if (!values.containsKey(LauncherSettings.ChangeLogColumns._ID)) {
165a8c760d395e1d2a78522427738302fbca3a72453Michael Jurka            throw new RuntimeException("Error: attempting to add item without specifying an id");
166a8c760d395e1d2a78522427738302fbca3a72453Michael Jurka        }
1675dee7aff5fa8774d37977d12df7ce8986752321dChris Wren        helper.checkId(table, values);
168a8c760d395e1d2a78522427738302fbca3a72453Michael Jurka        return db.insert(table, nullColumnHack, values);
169a8c760d395e1d2a78522427738302fbca3a72453Michael Jurka    }
170a8c760d395e1d2a78522427738302fbca3a72453Michael Jurka
171d106418dee16b041f30682540fbd3ca3d841b7afSunny Goyal    private void reloadLauncherIfExternal() {
172c74e41973a74aa416e16aeadaa2f491153da26bcSunny Goyal        if (Utilities.ATLEAST_MARSHMALLOW && Binder.getCallingPid() != Process.myPid()) {
173d106418dee16b041f30682540fbd3ca3d841b7afSunny Goyal            LauncherAppState app = LauncherAppState.getInstanceNoCreate();
174d106418dee16b041f30682540fbd3ca3d841b7afSunny Goyal            if (app != null) {
175d106418dee16b041f30682540fbd3ca3d841b7afSunny Goyal                app.reloadWorkspace();
176d106418dee16b041f30682540fbd3ca3d841b7afSunny Goyal            }
177d106418dee16b041f30682540fbd3ca3d841b7afSunny Goyal        }
178d106418dee16b041f30682540fbd3ca3d841b7afSunny Goyal    }
179d106418dee16b041f30682540fbd3ca3d841b7afSunny Goyal
18031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    @Override
18131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    public Uri insert(Uri uri, ContentValues initialValues) {
182d3849d191bc0bd2d561cf03018ad90b896b13c56Sunny Goyal        createDbIfNotExists();
18331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        SqlArguments args = new SqlArguments(uri);
18431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
185d106418dee16b041f30682540fbd3ca3d841b7afSunny Goyal        // In very limited cases, we support system|signature permission apps to modify the db.
186d106418dee16b041f30682540fbd3ca3d841b7afSunny Goyal        if (Binder.getCallingPid() != Process.myPid()) {
187b5b55c8012c124182c1bd07bdd4be1e734d972d0Sunny Goyal            if (!initializeExternalAdd(initialValues)) {
188a043fa89211541c71b5bef052ebc49f722d338b9Adam Cohen                return null;
189a043fa89211541c71b5bef052ebc49f722d338b9Adam Cohen            }
190a043fa89211541c71b5bef052ebc49f722d338b9Adam Cohen        }
191a043fa89211541c71b5bef052ebc49f722d338b9Adam Cohen
19231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        SQLiteDatabase db = mOpenHelper.getWritableDatabase();
1931ada10d5950f45aba998a7722bfde2e529201f48Chris Wren        addModifiedTime(initialValues);
194a8c760d395e1d2a78522427738302fbca3a72453Michael Jurka        final long rowId = dbInsertAndCheck(mOpenHelper, db, args.table, null, initialValues);
195e72f3d53f8c35b94aece6fafa6fd12cce9d09fe5Sunny Goyal        if (rowId < 0) return null;
19631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
19731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        uri = ContentUris.withAppendedId(uri, rowId);
1981d4a2df091e2b5928406ea503321563a0ec8a01eSunny Goyal        notifyListeners();
1991d4a2df091e2b5928406ea503321563a0ec8a01eSunny Goyal
200c74e41973a74aa416e16aeadaa2f491153da26bcSunny Goyal        if (Utilities.ATLEAST_MARSHMALLOW) {
201c74e41973a74aa416e16aeadaa2f491153da26bcSunny Goyal            reloadLauncherIfExternal();
202c74e41973a74aa416e16aeadaa2f491153da26bcSunny Goyal        } else {
203c74e41973a74aa416e16aeadaa2f491153da26bcSunny Goyal            // Deprecated behavior to support legacy devices which rely on provider callbacks.
204c74e41973a74aa416e16aeadaa2f491153da26bcSunny Goyal            LauncherAppState app = LauncherAppState.getInstanceNoCreate();
205c74e41973a74aa416e16aeadaa2f491153da26bcSunny Goyal            if (app != null && "true".equals(uri.getQueryParameter("isExternalAdd"))) {
206c74e41973a74aa416e16aeadaa2f491153da26bcSunny Goyal                app.reloadWorkspace();
207c74e41973a74aa416e16aeadaa2f491153da26bcSunny Goyal            }
208c74e41973a74aa416e16aeadaa2f491153da26bcSunny Goyal
209c74e41973a74aa416e16aeadaa2f491153da26bcSunny Goyal            String notify = uri.getQueryParameter("notify");
210c74e41973a74aa416e16aeadaa2f491153da26bcSunny Goyal            if (notify == null || "true".equals(notify)) {
211c74e41973a74aa416e16aeadaa2f491153da26bcSunny Goyal                getContext().getContentResolver().notifyChange(uri, null);
212c74e41973a74aa416e16aeadaa2f491153da26bcSunny Goyal            }
213c74e41973a74aa416e16aeadaa2f491153da26bcSunny Goyal        }
21431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        return uri;
21531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    }
21631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
217b5b55c8012c124182c1bd07bdd4be1e734d972d0Sunny Goyal    private boolean initializeExternalAdd(ContentValues values) {
218b5b55c8012c124182c1bd07bdd4be1e734d972d0Sunny Goyal        // 1. Ensure that externally added items have a valid item id
219b5b55c8012c124182c1bd07bdd4be1e734d972d0Sunny Goyal        long id = mOpenHelper.generateNewItemId();
220b5b55c8012c124182c1bd07bdd4be1e734d972d0Sunny Goyal        values.put(LauncherSettings.Favorites._ID, id);
221b5b55c8012c124182c1bd07bdd4be1e734d972d0Sunny Goyal
222b5b55c8012c124182c1bd07bdd4be1e734d972d0Sunny Goyal        // 2. In the case of an app widget, and if no app widget id is specified, we
223b5b55c8012c124182c1bd07bdd4be1e734d972d0Sunny Goyal        // attempt allocate and bind the widget.
224b5b55c8012c124182c1bd07bdd4be1e734d972d0Sunny Goyal        Integer itemType = values.getAsInteger(LauncherSettings.Favorites.ITEM_TYPE);
225b5b55c8012c124182c1bd07bdd4be1e734d972d0Sunny Goyal        if (itemType != null &&
226b5b55c8012c124182c1bd07bdd4be1e734d972d0Sunny Goyal                itemType.intValue() == LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET &&
227b5b55c8012c124182c1bd07bdd4be1e734d972d0Sunny Goyal                !values.containsKey(LauncherSettings.Favorites.APPWIDGET_ID)) {
228b5b55c8012c124182c1bd07bdd4be1e734d972d0Sunny Goyal
229b5b55c8012c124182c1bd07bdd4be1e734d972d0Sunny Goyal            final AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(getContext());
230b5b55c8012c124182c1bd07bdd4be1e734d972d0Sunny Goyal            ComponentName cn = ComponentName.unflattenFromString(
231b5b55c8012c124182c1bd07bdd4be1e734d972d0Sunny Goyal                    values.getAsString(Favorites.APPWIDGET_PROVIDER));
232b5b55c8012c124182c1bd07bdd4be1e734d972d0Sunny Goyal
233b5b55c8012c124182c1bd07bdd4be1e734d972d0Sunny Goyal            if (cn != null) {
234b5b55c8012c124182c1bd07bdd4be1e734d972d0Sunny Goyal                try {
235b5b55c8012c124182c1bd07bdd4be1e734d972d0Sunny Goyal                    int appWidgetId = new AppWidgetHost(getContext(), Launcher.APPWIDGET_HOST_ID)
236b5b55c8012c124182c1bd07bdd4be1e734d972d0Sunny Goyal                            .allocateAppWidgetId();
237b5b55c8012c124182c1bd07bdd4be1e734d972d0Sunny Goyal                    values.put(LauncherSettings.Favorites.APPWIDGET_ID, appWidgetId);
238b5b55c8012c124182c1bd07bdd4be1e734d972d0Sunny Goyal                    if (!appWidgetManager.bindAppWidgetIdIfAllowed(appWidgetId,cn)) {
239b5b55c8012c124182c1bd07bdd4be1e734d972d0Sunny Goyal                        return false;
240b5b55c8012c124182c1bd07bdd4be1e734d972d0Sunny Goyal                    }
241b5b55c8012c124182c1bd07bdd4be1e734d972d0Sunny Goyal                } catch (RuntimeException e) {
242b5b55c8012c124182c1bd07bdd4be1e734d972d0Sunny Goyal                    Log.e(TAG, "Failed to initialize external widget", e);
243b5b55c8012c124182c1bd07bdd4be1e734d972d0Sunny Goyal                    return false;
244b5b55c8012c124182c1bd07bdd4be1e734d972d0Sunny Goyal                }
245b5b55c8012c124182c1bd07bdd4be1e734d972d0Sunny Goyal            } else {
246b5b55c8012c124182c1bd07bdd4be1e734d972d0Sunny Goyal                return false;
247b5b55c8012c124182c1bd07bdd4be1e734d972d0Sunny Goyal            }
248b5b55c8012c124182c1bd07bdd4be1e734d972d0Sunny Goyal        }
249b5b55c8012c124182c1bd07bdd4be1e734d972d0Sunny Goyal
250b5b55c8012c124182c1bd07bdd4be1e734d972d0Sunny Goyal        // Add screen id if not present
251b5b55c8012c124182c1bd07bdd4be1e734d972d0Sunny Goyal        long screenId = values.getAsLong(LauncherSettings.Favorites.SCREEN);
252b5b55c8012c124182c1bd07bdd4be1e734d972d0Sunny Goyal        SQLiteStatement stmp = null;
253b5b55c8012c124182c1bd07bdd4be1e734d972d0Sunny Goyal        try {
254b5b55c8012c124182c1bd07bdd4be1e734d972d0Sunny Goyal            stmp = mOpenHelper.getWritableDatabase().compileStatement(
255b5b55c8012c124182c1bd07bdd4be1e734d972d0Sunny Goyal                    "INSERT OR IGNORE INTO workspaceScreens (_id, screenRank) " +
256b5b55c8012c124182c1bd07bdd4be1e734d972d0Sunny Goyal                            "select ?, (ifnull(MAX(screenRank), -1)+1) from workspaceScreens");
257b5b55c8012c124182c1bd07bdd4be1e734d972d0Sunny Goyal            stmp.bindLong(1, screenId);
258b5b55c8012c124182c1bd07bdd4be1e734d972d0Sunny Goyal
259b5b55c8012c124182c1bd07bdd4be1e734d972d0Sunny Goyal            ContentValues valuesInserted = new ContentValues();
260b5b55c8012c124182c1bd07bdd4be1e734d972d0Sunny Goyal            valuesInserted.put(LauncherSettings.BaseLauncherColumns._ID, stmp.executeInsert());
261b5b55c8012c124182c1bd07bdd4be1e734d972d0Sunny Goyal            mOpenHelper.checkId(WorkspaceScreens.TABLE_NAME, valuesInserted);
262b5b55c8012c124182c1bd07bdd4be1e734d972d0Sunny Goyal            return true;
263b5b55c8012c124182c1bd07bdd4be1e734d972d0Sunny Goyal        } catch (Exception e) {
264b5b55c8012c124182c1bd07bdd4be1e734d972d0Sunny Goyal            return false;
265b5b55c8012c124182c1bd07bdd4be1e734d972d0Sunny Goyal        } finally {
266b5b55c8012c124182c1bd07bdd4be1e734d972d0Sunny Goyal            Utilities.closeSilently(stmp);
267b5b55c8012c124182c1bd07bdd4be1e734d972d0Sunny Goyal        }
268b5b55c8012c124182c1bd07bdd4be1e734d972d0Sunny Goyal    }
269b5b55c8012c124182c1bd07bdd4be1e734d972d0Sunny Goyal
27031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    @Override
27131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    public int bulkInsert(Uri uri, ContentValues[] values) {
272d3849d191bc0bd2d561cf03018ad90b896b13c56Sunny Goyal        createDbIfNotExists();
27331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        SqlArguments args = new SqlArguments(uri);
27431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
27531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        SQLiteDatabase db = mOpenHelper.getWritableDatabase();
27631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        db.beginTransaction();
27731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        try {
27831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            int numValues = values.length;
27931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            for (int i = 0; i < numValues; i++) {
2801ada10d5950f45aba998a7722bfde2e529201f48Chris Wren                addModifiedTime(values[i]);
281a8c760d395e1d2a78522427738302fbca3a72453Michael Jurka                if (dbInsertAndCheck(mOpenHelper, db, args.table, null, values[i]) < 0) {
282a8c760d395e1d2a78522427738302fbca3a72453Michael Jurka                    return 0;
283a8c760d395e1d2a78522427738302fbca3a72453Michael Jurka                }
28431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            }
28531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            db.setTransactionSuccessful();
28631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        } finally {
28731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            db.endTransaction();
28831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        }
28931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
2901d4a2df091e2b5928406ea503321563a0ec8a01eSunny Goyal        notifyListeners();
291d106418dee16b041f30682540fbd3ca3d841b7afSunny Goyal        reloadLauncherIfExternal();
29231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        return values.length;
29331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    }
29431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
29531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    @Override
296085c853a5702c45865e9b017d21fa15cf2b151b9Yura    public ContentProviderResult[] applyBatch(ArrayList<ContentProviderOperation> operations)
297085c853a5702c45865e9b017d21fa15cf2b151b9Yura            throws OperationApplicationException {
298d3849d191bc0bd2d561cf03018ad90b896b13c56Sunny Goyal        createDbIfNotExists();
299085c853a5702c45865e9b017d21fa15cf2b151b9Yura        SQLiteDatabase db = mOpenHelper.getWritableDatabase();
300085c853a5702c45865e9b017d21fa15cf2b151b9Yura        db.beginTransaction();
301085c853a5702c45865e9b017d21fa15cf2b151b9Yura        try {
302085c853a5702c45865e9b017d21fa15cf2b151b9Yura            ContentProviderResult[] result =  super.applyBatch(operations);
303085c853a5702c45865e9b017d21fa15cf2b151b9Yura            db.setTransactionSuccessful();
304d106418dee16b041f30682540fbd3ca3d841b7afSunny Goyal            reloadLauncherIfExternal();
305085c853a5702c45865e9b017d21fa15cf2b151b9Yura            return result;
306085c853a5702c45865e9b017d21fa15cf2b151b9Yura        } finally {
307085c853a5702c45865e9b017d21fa15cf2b151b9Yura            db.endTransaction();
308085c853a5702c45865e9b017d21fa15cf2b151b9Yura        }
309085c853a5702c45865e9b017d21fa15cf2b151b9Yura    }
310085c853a5702c45865e9b017d21fa15cf2b151b9Yura
311085c853a5702c45865e9b017d21fa15cf2b151b9Yura    @Override
31231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    public int delete(Uri uri, String selection, String[] selectionArgs) {
313d3849d191bc0bd2d561cf03018ad90b896b13c56Sunny Goyal        createDbIfNotExists();
31431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        SqlArguments args = new SqlArguments(uri, selection, selectionArgs);
31531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
31631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        SQLiteDatabase db = mOpenHelper.getWritableDatabase();
31731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
318a9c21c624ca15fe7aa5e65e17cdd9fb26d1ec691Louis Begin        if (Binder.getCallingPid() != Process.myPid()
319a9c21c624ca15fe7aa5e65e17cdd9fb26d1ec691Louis Begin                && Favorites.TABLE_NAME.equalsIgnoreCase(args.table)) {
320a9c21c624ca15fe7aa5e65e17cdd9fb26d1ec691Louis Begin            String widgetSelection = TextUtils.isEmpty(args.where) ? "1=1" : args.where;
321a9c21c624ca15fe7aa5e65e17cdd9fb26d1ec691Louis Begin            widgetSelection = String.format(Locale.ENGLISH, "%1$s = %2$d AND ( %3$s )",
322a9c21c624ca15fe7aa5e65e17cdd9fb26d1ec691Louis Begin                    Favorites.ITEM_TYPE, Favorites.ITEM_TYPE_APPWIDGET, widgetSelection);
323a9c21c624ca15fe7aa5e65e17cdd9fb26d1ec691Louis Begin            try (Cursor c = db.query(Favorites.TABLE_NAME, new String[] { Favorites.APPWIDGET_ID },
324a9c21c624ca15fe7aa5e65e17cdd9fb26d1ec691Louis Begin                    widgetSelection, args.args, null, null, null)) {
325a9c21c624ca15fe7aa5e65e17cdd9fb26d1ec691Louis Begin                AppWidgetHost host = new AppWidgetHost(getContext(), Launcher.APPWIDGET_HOST_ID);
326a9c21c624ca15fe7aa5e65e17cdd9fb26d1ec691Louis Begin                while (c.moveToNext()) {
327a9c21c624ca15fe7aa5e65e17cdd9fb26d1ec691Louis Begin                    int widgetId = c.getInt(0);
328a9c21c624ca15fe7aa5e65e17cdd9fb26d1ec691Louis Begin                    if (widgetId != AppWidgetManager.INVALID_APPWIDGET_ID) {
329a9c21c624ca15fe7aa5e65e17cdd9fb26d1ec691Louis Begin                        try {
330a9c21c624ca15fe7aa5e65e17cdd9fb26d1ec691Louis Begin                            host.deleteAppWidgetId(widgetId);
331a9c21c624ca15fe7aa5e65e17cdd9fb26d1ec691Louis Begin                        } catch (RuntimeException e) {
332a9c21c624ca15fe7aa5e65e17cdd9fb26d1ec691Louis Begin                            Log.e(TAG, "Error deleting widget id " + widgetId, e);
333a9c21c624ca15fe7aa5e65e17cdd9fb26d1ec691Louis Begin                        }
334a9c21c624ca15fe7aa5e65e17cdd9fb26d1ec691Louis Begin                    }
335a9c21c624ca15fe7aa5e65e17cdd9fb26d1ec691Louis Begin                }
336a9c21c624ca15fe7aa5e65e17cdd9fb26d1ec691Louis Begin            }
337a9c21c624ca15fe7aa5e65e17cdd9fb26d1ec691Louis Begin        }
338a9c21c624ca15fe7aa5e65e17cdd9fb26d1ec691Louis Begin        int count = db.delete(args.table, args.where, args.args);
339a9c21c624ca15fe7aa5e65e17cdd9fb26d1ec691Louis Begin        if (count > 0) {
340a9c21c624ca15fe7aa5e65e17cdd9fb26d1ec691Louis Begin            notifyListeners();
341a9c21c624ca15fe7aa5e65e17cdd9fb26d1ec691Louis Begin            reloadLauncherIfExternal();
342a9c21c624ca15fe7aa5e65e17cdd9fb26d1ec691Louis Begin        }
34331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        return count;
34431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    }
34531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
34631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    @Override
34731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
348d3849d191bc0bd2d561cf03018ad90b896b13c56Sunny Goyal        createDbIfNotExists();
34931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        SqlArguments args = new SqlArguments(uri, selection, selectionArgs);
35031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
3511ada10d5950f45aba998a7722bfde2e529201f48Chris Wren        addModifiedTime(values);
35231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        SQLiteDatabase db = mOpenHelper.getWritableDatabase();
35331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        int count = db.update(args.table, values, args.where, args.args);
3541d4a2df091e2b5928406ea503321563a0ec8a01eSunny Goyal        if (count > 0) notifyListeners();
35531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
356d106418dee16b041f30682540fbd3ca3d841b7afSunny Goyal        reloadLauncherIfExternal();
35731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        return count;
35831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    }
35931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
3607779d62308b87ca26e3be47df836893f6f7693ecSunny Goyal    @Override
361827cef203f386cb548b98a7fa9888b75478c8e20Tony Wickham    public Bundle call(String method, final String arg, final Bundle extras) {
3627779d62308b87ca26e3be47df836893f6f7693ecSunny Goyal        if (Binder.getCallingUid() != Process.myUid()) {
3637779d62308b87ca26e3be47df836893f6f7693ecSunny Goyal            return null;
3647779d62308b87ca26e3be47df836893f6f7693ecSunny Goyal        }
365d3849d191bc0bd2d561cf03018ad90b896b13c56Sunny Goyal        createDbIfNotExists();
3667779d62308b87ca26e3be47df836893f6f7693ecSunny Goyal
3677779d62308b87ca26e3be47df836893f6f7693ecSunny Goyal        switch (method) {
368827cef203f386cb548b98a7fa9888b75478c8e20Tony Wickham            case LauncherSettings.Settings.METHOD_SET_EXTRACTED_COLORS_AND_WALLPAPER_ID: {
369827cef203f386cb548b98a7fa9888b75478c8e20Tony Wickham                String extractedColors = extras.getString(
370827cef203f386cb548b98a7fa9888b75478c8e20Tony Wickham                        LauncherSettings.Settings.EXTRA_EXTRACTED_COLORS);
371827cef203f386cb548b98a7fa9888b75478c8e20Tony Wickham                int wallpaperId = extras.getInt(LauncherSettings.Settings.EXTRA_WALLPAPER_ID);
372827cef203f386cb548b98a7fa9888b75478c8e20Tony Wickham                Utilities.getPrefs(getContext()).edit()
373827cef203f386cb548b98a7fa9888b75478c8e20Tony Wickham                        .putString(ExtractionUtils.EXTRACTED_COLORS_PREFERENCE_KEY, extractedColors)
374827cef203f386cb548b98a7fa9888b75478c8e20Tony Wickham                        .putInt(ExtractionUtils.WALLPAPER_ID_PREFERENCE_KEY, wallpaperId)
375827cef203f386cb548b98a7fa9888b75478c8e20Tony Wickham                        .apply();
376b5b55c8012c124182c1bd07bdd4be1e734d972d0Sunny Goyal                mListenerHandler.sendEmptyMessage(ChangeListenerWrapper.MSG_EXTRACTED_COLORS_CHANGED);
377827cef203f386cb548b98a7fa9888b75478c8e20Tony Wickham                Bundle result = new Bundle();
378827cef203f386cb548b98a7fa9888b75478c8e20Tony Wickham                result.putString(LauncherSettings.Settings.EXTRA_VALUE, extractedColors);
379827cef203f386cb548b98a7fa9888b75478c8e20Tony Wickham                return result;
380827cef203f386cb548b98a7fa9888b75478c8e20Tony Wickham            }
381d249748abf357925b326d57ab68eb6c2b23c4ef6Sunny Goyal            case LauncherSettings.Settings.METHOD_CLEAR_EMPTY_DB_FLAG: {
382d249748abf357925b326d57ab68eb6c2b23c4ef6Sunny Goyal                clearFlagEmptyDbCreated();
383d249748abf357925b326d57ab68eb6c2b23c4ef6Sunny Goyal                return null;
384d249748abf357925b326d57ab68eb6c2b23c4ef6Sunny Goyal            }
385a5c8a9eb666da16bc4c9ea4412868e22ace8d1f0Sunny Goyal            case LauncherSettings.Settings.METHOD_WAS_EMPTY_DB_CREATED : {
386a5c8a9eb666da16bc4c9ea4412868e22ace8d1f0Sunny Goyal                Bundle result = new Bundle();
387a5c8a9eb666da16bc4c9ea4412868e22ace8d1f0Sunny Goyal                result.putBoolean(LauncherSettings.Settings.EXTRA_VALUE,
388a5c8a9eb666da16bc4c9ea4412868e22ace8d1f0Sunny Goyal                        Utilities.getPrefs(getContext()).getBoolean(EMPTY_DATABASE_CREATED, false));
389a5c8a9eb666da16bc4c9ea4412868e22ace8d1f0Sunny Goyal                return result;
390a5c8a9eb666da16bc4c9ea4412868e22ace8d1f0Sunny Goyal            }
391d249748abf357925b326d57ab68eb6c2b23c4ef6Sunny Goyal            case LauncherSettings.Settings.METHOD_DELETE_EMPTY_FOLDERS: {
392d249748abf357925b326d57ab68eb6c2b23c4ef6Sunny Goyal                Bundle result = new Bundle();
393d249748abf357925b326d57ab68eb6c2b23c4ef6Sunny Goyal                result.putSerializable(LauncherSettings.Settings.EXTRA_VALUE, deleteEmptyFolders());
394d249748abf357925b326d57ab68eb6c2b23c4ef6Sunny Goyal                return result;
395d249748abf357925b326d57ab68eb6c2b23c4ef6Sunny Goyal            }
396d249748abf357925b326d57ab68eb6c2b23c4ef6Sunny Goyal            case LauncherSettings.Settings.METHOD_NEW_ITEM_ID: {
397d249748abf357925b326d57ab68eb6c2b23c4ef6Sunny Goyal                Bundle result = new Bundle();
398d249748abf357925b326d57ab68eb6c2b23c4ef6Sunny Goyal                result.putLong(LauncherSettings.Settings.EXTRA_VALUE, mOpenHelper.generateNewItemId());
399d249748abf357925b326d57ab68eb6c2b23c4ef6Sunny Goyal                return result;
400d249748abf357925b326d57ab68eb6c2b23c4ef6Sunny Goyal            }
401d249748abf357925b326d57ab68eb6c2b23c4ef6Sunny Goyal            case LauncherSettings.Settings.METHOD_NEW_SCREEN_ID: {
402d249748abf357925b326d57ab68eb6c2b23c4ef6Sunny Goyal                Bundle result = new Bundle();
403d249748abf357925b326d57ab68eb6c2b23c4ef6Sunny Goyal                result.putLong(LauncherSettings.Settings.EXTRA_VALUE, mOpenHelper.generateNewScreenId());
404d249748abf357925b326d57ab68eb6c2b23c4ef6Sunny Goyal                return result;
405d249748abf357925b326d57ab68eb6c2b23c4ef6Sunny Goyal            }
406d249748abf357925b326d57ab68eb6c2b23c4ef6Sunny Goyal            case LauncherSettings.Settings.METHOD_CREATE_EMPTY_DB: {
407d249748abf357925b326d57ab68eb6c2b23c4ef6Sunny Goyal                createEmptyDB();
408d249748abf357925b326d57ab68eb6c2b23c4ef6Sunny Goyal                return null;
409d249748abf357925b326d57ab68eb6c2b23c4ef6Sunny Goyal            }
410d249748abf357925b326d57ab68eb6c2b23c4ef6Sunny Goyal            case LauncherSettings.Settings.METHOD_LOAD_DEFAULT_FAVORITES: {
411d249748abf357925b326d57ab68eb6c2b23c4ef6Sunny Goyal                loadDefaultFavoritesIfNecessary();
412d249748abf357925b326d57ab68eb6c2b23c4ef6Sunny Goyal                return null;
413d249748abf357925b326d57ab68eb6c2b23c4ef6Sunny Goyal            }
414d249748abf357925b326d57ab68eb6c2b23c4ef6Sunny Goyal            case LauncherSettings.Settings.METHOD_DELETE_DB: {
415aefc0c4dd0589d2ef423caf26c11068b5cb59722Sunny Goyal                // Are you sure? (y/n)
416aefc0c4dd0589d2ef423caf26c11068b5cb59722Sunny Goyal                mOpenHelper.createEmptyDB(mOpenHelper.getWritableDatabase());
417d249748abf357925b326d57ab68eb6c2b23c4ef6Sunny Goyal                return null;
418d249748abf357925b326d57ab68eb6c2b23c4ef6Sunny Goyal            }
4197779d62308b87ca26e3be47df836893f6f7693ecSunny Goyal        }
4207779d62308b87ca26e3be47df836893f6f7693ecSunny Goyal        return null;
4217779d62308b87ca26e3be47df836893f6f7693ecSunny Goyal    }
4227779d62308b87ca26e3be47df836893f6f7693ecSunny Goyal
423b1622cc30f2fd9b579cb918083e063685950df92Sunny Goyal    /**
424b1622cc30f2fd9b579cb918083e063685950df92Sunny Goyal     * Deletes any empty folder from the DB.
425b1622cc30f2fd9b579cb918083e063685950df92Sunny Goyal     * @return Ids of deleted folders.
426b1622cc30f2fd9b579cb918083e063685950df92Sunny Goyal     */
427d249748abf357925b326d57ab68eb6c2b23c4ef6Sunny Goyal    private ArrayList<Long> deleteEmptyFolders() {
428d249748abf357925b326d57ab68eb6c2b23c4ef6Sunny Goyal        ArrayList<Long> folderIds = new ArrayList<>();
429b1622cc30f2fd9b579cb918083e063685950df92Sunny Goyal        SQLiteDatabase db = mOpenHelper.getWritableDatabase();
430b1622cc30f2fd9b579cb918083e063685950df92Sunny Goyal        db.beginTransaction();
431b1622cc30f2fd9b579cb918083e063685950df92Sunny Goyal        try {
432b1622cc30f2fd9b579cb918083e063685950df92Sunny Goyal            // Select folders whose id do not match any container value.
433b1622cc30f2fd9b579cb918083e063685950df92Sunny Goyal            String selection = LauncherSettings.Favorites.ITEM_TYPE + " = "
434b1622cc30f2fd9b579cb918083e063685950df92Sunny Goyal                    + LauncherSettings.Favorites.ITEM_TYPE_FOLDER + " AND "
435b1622cc30f2fd9b579cb918083e063685950df92Sunny Goyal                    + LauncherSettings.Favorites._ID +  " NOT IN (SELECT " +
436b1622cc30f2fd9b579cb918083e063685950df92Sunny Goyal                            LauncherSettings.Favorites.CONTAINER + " FROM "
437b5b55c8012c124182c1bd07bdd4be1e734d972d0Sunny Goyal                                + Favorites.TABLE_NAME + ")";
438b5b55c8012c124182c1bd07bdd4be1e734d972d0Sunny Goyal            Cursor c = db.query(Favorites.TABLE_NAME,
439b1622cc30f2fd9b579cb918083e063685950df92Sunny Goyal                    new String[] {LauncherSettings.Favorites._ID},
440b1622cc30f2fd9b579cb918083e063685950df92Sunny Goyal                    selection, null, null, null, null);
441b1622cc30f2fd9b579cb918083e063685950df92Sunny Goyal            while (c.moveToNext()) {
442b1622cc30f2fd9b579cb918083e063685950df92Sunny Goyal                folderIds.add(c.getLong(0));
443b1622cc30f2fd9b579cb918083e063685950df92Sunny Goyal            }
444b1622cc30f2fd9b579cb918083e063685950df92Sunny Goyal            c.close();
445d249748abf357925b326d57ab68eb6c2b23c4ef6Sunny Goyal            if (!folderIds.isEmpty()) {
446b5b55c8012c124182c1bd07bdd4be1e734d972d0Sunny Goyal                db.delete(Favorites.TABLE_NAME, Utilities.createDbSelectionQuery(
447b1622cc30f2fd9b579cb918083e063685950df92Sunny Goyal                        LauncherSettings.Favorites._ID, folderIds), null);
448b1622cc30f2fd9b579cb918083e063685950df92Sunny Goyal            }
449b1622cc30f2fd9b579cb918083e063685950df92Sunny Goyal            db.setTransactionSuccessful();
450b1622cc30f2fd9b579cb918083e063685950df92Sunny Goyal        } catch (SQLException ex) {
451b1622cc30f2fd9b579cb918083e063685950df92Sunny Goyal            Log.e(TAG, ex.getMessage(), ex);
452b1622cc30f2fd9b579cb918083e063685950df92Sunny Goyal            folderIds.clear();
453b1622cc30f2fd9b579cb918083e063685950df92Sunny Goyal        } finally {
454b1622cc30f2fd9b579cb918083e063685950df92Sunny Goyal            db.endTransaction();
455b1622cc30f2fd9b579cb918083e063685950df92Sunny Goyal        }
456b1622cc30f2fd9b579cb918083e063685950df92Sunny Goyal        return folderIds;
457b1622cc30f2fd9b579cb918083e063685950df92Sunny Goyal    }
458b1622cc30f2fd9b579cb918083e063685950df92Sunny Goyal
459f076eae0cab10f035f7b187c72a680cd220acf1bSunny Goyal    /**
460f076eae0cab10f035f7b187c72a680cd220acf1bSunny Goyal     * Overridden in tests
461f076eae0cab10f035f7b187c72a680cd220acf1bSunny Goyal     */
462f076eae0cab10f035f7b187c72a680cd220acf1bSunny Goyal    protected void notifyListeners() {
463b5b55c8012c124182c1bd07bdd4be1e734d972d0Sunny Goyal        mListenerHandler.sendEmptyMessage(ChangeListenerWrapper.MSG_LAUNCHER_PROVIDER_CHANGED);
4641ada10d5950f45aba998a7722bfde2e529201f48Chris Wren    }
4651ada10d5950f45aba998a7722bfde2e529201f48Chris Wren
466091440a9cb9d4f42406631004aa484cbb79214caAdam Cohen    @Thunk static void addModifiedTime(ContentValues values) {
4671ada10d5950f45aba998a7722bfde2e529201f48Chris Wren        values.put(LauncherSettings.ChangeLogColumns.MODIFIED, System.currentTimeMillis());
46831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    }
46931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
4705524b4957fc7c1c858037327429411efdb8fe7d6Brian Muramatsu    /**
47142de82ff8d2aa182594709e63e608bbcd4610cfaSunny Goyal     * Clears all the data for a fresh start.
47242de82ff8d2aa182594709e63e608bbcd4610cfaSunny Goyal     */
473d249748abf357925b326d57ab68eb6c2b23c4ef6Sunny Goyal    synchronized private void createEmptyDB() {
47442de82ff8d2aa182594709e63e608bbcd4610cfaSunny Goyal        mOpenHelper.createEmptyDB(mOpenHelper.getWritableDatabase());
47542de82ff8d2aa182594709e63e608bbcd4610cfaSunny Goyal    }
47642de82ff8d2aa182594709e63e608bbcd4610cfaSunny Goyal
477d249748abf357925b326d57ab68eb6c2b23c4ef6Sunny Goyal    private void clearFlagEmptyDbCreated() {
478f725824fa2091cab44cf1bcbfe5b5b8d06475560Sunny Goyal        Utilities.getPrefs(getContext()).edit().remove(EMPTY_DATABASE_CREATED).commit();
47933d443897658e6ad8b76bd2e58e3598161fd3eadSunny Goyal    }
48033d443897658e6ad8b76bd2e58e3598161fd3eadSunny Goyal
48142de82ff8d2aa182594709e63e608bbcd4610cfaSunny Goyal    /**
4820fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal     * Loads the default workspace based on the following priority scheme:
483b2d46ceaaa32ece85f18e049d9e30a5ccaf80e45Sunny Goyal     *   1) From the app restrictions
484b2d46ceaaa32ece85f18e049d9e30a5ccaf80e45Sunny Goyal     *   2) From a package provided by play store
485b2d46ceaaa32ece85f18e049d9e30a5ccaf80e45Sunny Goyal     *   3) From a partner configuration APK, already in the system image
486b2d46ceaaa32ece85f18e049d9e30a5ccaf80e45Sunny Goyal     *   4) The default configuration for the particular device
4875524b4957fc7c1c858037327429411efdb8fe7d6Brian Muramatsu     */
488d249748abf357925b326d57ab68eb6c2b23c4ef6Sunny Goyal    synchronized private void loadDefaultFavoritesIfNecessary() {
489f725824fa2091cab44cf1bcbfe5b5b8d06475560Sunny Goyal        SharedPreferences sp = Utilities.getPrefs(getContext());
490e25af795647a19f1a0d60bc4baea5a996d215fdfAdam Cohen
491c763c4e4d28c256d1368be3fc1c4526c8b9bd232Winson Chung        if (sp.getBoolean(EMPTY_DATABASE_CREATED, false)) {
4925dee7aff5fa8774d37977d12df7ce8986752321dChris Wren            Log.d(TAG, "loading default workspace");
49345355c4596f396d7e7247e91de850646356bd104Michael Jurka
494b5b55c8012c124182c1bd07bdd4be1e734d972d0Sunny Goyal            AppWidgetHost widgetHost = new AppWidgetHost(getContext(), Launcher.APPWIDGET_HOST_ID);
495b5b55c8012c124182c1bd07bdd4be1e734d972d0Sunny Goyal            AutoInstallsLayout loader = createWorkspaceLoaderFromAppRestriction(widgetHost);
496b2d46ceaaa32ece85f18e049d9e30a5ccaf80e45Sunny Goyal            if (loader == null) {
497b5b55c8012c124182c1bd07bdd4be1e734d972d0Sunny Goyal                loader = AutoInstallsLayout.get(getContext(),widgetHost, mOpenHelper);
498b2d46ceaaa32ece85f18e049d9e30a5ccaf80e45Sunny Goyal            }
4990fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal            if (loader == null) {
5009b8f51fcc85805436bee619a927e2222005e9e09Adam Cohen                final Partner partner = Partner.get(getContext().getPackageManager());
5019b8f51fcc85805436bee619a927e2222005e9e09Adam Cohen                if (partner != null && partner.hasDefaultLayout()) {
5029b8f51fcc85805436bee619a927e2222005e9e09Adam Cohen                    final Resources partnerRes = partner.getResources();
5034ae96ce92df7aad3c767c68b8795b80734e01829Adam Cohen                    int workspaceResId = partnerRes.getIdentifier(Partner.RES_DEFAULT_LAYOUT,
5049b8f51fcc85805436bee619a927e2222005e9e09Adam Cohen                            "xml", partner.getPackageName());
5050fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal                    if (workspaceResId != 0) {
506b5b55c8012c124182c1bd07bdd4be1e734d972d0Sunny Goyal                        loader = new DefaultLayoutParser(getContext(), widgetHost,
5073a5a9d18daacb878a3eb379fce540ee6c67063e6Sunny Goyal                                mOpenHelper, partnerRes, workspaceResId);
5080fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal                    }
5099b8f51fcc85805436bee619a927e2222005e9e09Adam Cohen                }
5109b8f51fcc85805436bee619a927e2222005e9e09Adam Cohen            }
5115524b4957fc7c1c858037327429411efdb8fe7d6Brian Muramatsu
5129d219685c5f008ddfb0f72ef01831308d8a81c2eSunny Goyal            final boolean usingExternallyProvidedLayout = loader != null;
5130fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal            if (loader == null) {
514b5b55c8012c124182c1bd07bdd4be1e734d972d0Sunny Goyal                loader = getDefaultLayoutParser(widgetHost);
51545355c4596f396d7e7247e91de850646356bd104Michael Jurka            }
516c6c8fef3e90f84b26c3b8550ec6a1d668ca9200dSunny Goyal
517c6c8fef3e90f84b26c3b8550ec6a1d668ca9200dSunny Goyal            // There might be some partially restored DB items, due to buggy restore logic in
518c6c8fef3e90f84b26c3b8550ec6a1d668ca9200dSunny Goyal            // previous versions of launcher.
519c6c8fef3e90f84b26c3b8550ec6a1d668ca9200dSunny Goyal            createEmptyDB();
5200fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal            // Populate favorites table with initial favorites
5219d219685c5f008ddfb0f72ef01831308d8a81c2eSunny Goyal            if ((mOpenHelper.loadFavorites(mOpenHelper.getWritableDatabase(), loader) <= 0)
5229d219685c5f008ddfb0f72ef01831308d8a81c2eSunny Goyal                    && usingExternallyProvidedLayout) {
5239d219685c5f008ddfb0f72ef01831308d8a81c2eSunny Goyal                // Unable to load external layout. Cleanup and load the internal layout.
5249d219685c5f008ddfb0f72ef01831308d8a81c2eSunny Goyal                createEmptyDB();
5259d219685c5f008ddfb0f72ef01831308d8a81c2eSunny Goyal                mOpenHelper.loadFavorites(mOpenHelper.getWritableDatabase(),
526b5b55c8012c124182c1bd07bdd4be1e734d972d0Sunny Goyal                        getDefaultLayoutParser(widgetHost));
5279d219685c5f008ddfb0f72ef01831308d8a81c2eSunny Goyal            }
52833d443897658e6ad8b76bd2e58e3598161fd3eadSunny Goyal            clearFlagEmptyDbCreated();
529b85f8a44b51258f22938773ca30dd85845345010Michael Jurka        }
530b85f8a44b51258f22938773ca30dd85845345010Michael Jurka    }
531b85f8a44b51258f22938773ca30dd85845345010Michael Jurka
532b2d46ceaaa32ece85f18e049d9e30a5ccaf80e45Sunny Goyal    /**
533b2d46ceaaa32ece85f18e049d9e30a5ccaf80e45Sunny Goyal     * Creates workspace loader from an XML resource listed in the app restrictions.
534b2d46ceaaa32ece85f18e049d9e30a5ccaf80e45Sunny Goyal     *
535b2d46ceaaa32ece85f18e049d9e30a5ccaf80e45Sunny Goyal     * @return the loader if the restrictions are set and the resource exists; null otherwise.
536b2d46ceaaa32ece85f18e049d9e30a5ccaf80e45Sunny Goyal     */
537b2d46ceaaa32ece85f18e049d9e30a5ccaf80e45Sunny Goyal    @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR2)
538b5b55c8012c124182c1bd07bdd4be1e734d972d0Sunny Goyal    private AutoInstallsLayout createWorkspaceLoaderFromAppRestriction(AppWidgetHost widgetHost) {
539b2d46ceaaa32ece85f18e049d9e30a5ccaf80e45Sunny Goyal        // UserManager.getApplicationRestrictions() requires minSdkVersion >= 18
5409fc953b94dbc6b99e6de08c9dcc80a0cb8e3e319Sunny Goyal        if (!Utilities.ATLEAST_JB_MR2) {
541b2d46ceaaa32ece85f18e049d9e30a5ccaf80e45Sunny Goyal            return null;
542b2d46ceaaa32ece85f18e049d9e30a5ccaf80e45Sunny Goyal        }
543b2d46ceaaa32ece85f18e049d9e30a5ccaf80e45Sunny Goyal
544b2d46ceaaa32ece85f18e049d9e30a5ccaf80e45Sunny Goyal        Context ctx = getContext();
545b2d46ceaaa32ece85f18e049d9e30a5ccaf80e45Sunny Goyal        UserManager um = (UserManager) ctx.getSystemService(Context.USER_SERVICE);
546b2d46ceaaa32ece85f18e049d9e30a5ccaf80e45Sunny Goyal        Bundle bundle = um.getApplicationRestrictions(ctx.getPackageName());
54735ca873bd9e1331765343a6cae8387431794b0afSunny Goyal        if (bundle == null) {
54835ca873bd9e1331765343a6cae8387431794b0afSunny Goyal            return null;
54935ca873bd9e1331765343a6cae8387431794b0afSunny Goyal        }
550b2d46ceaaa32ece85f18e049d9e30a5ccaf80e45Sunny Goyal
55135ca873bd9e1331765343a6cae8387431794b0afSunny Goyal        String packageName = bundle.getString(RESTRICTION_PACKAGE_NAME);
552b2d46ceaaa32ece85f18e049d9e30a5ccaf80e45Sunny Goyal        if (packageName != null) {
553b2d46ceaaa32ece85f18e049d9e30a5ccaf80e45Sunny Goyal            try {
554b2d46ceaaa32ece85f18e049d9e30a5ccaf80e45Sunny Goyal                Resources targetResources = ctx.getPackageManager()
555b2d46ceaaa32ece85f18e049d9e30a5ccaf80e45Sunny Goyal                        .getResourcesForApplication(packageName);
556b2d46ceaaa32ece85f18e049d9e30a5ccaf80e45Sunny Goyal                return AutoInstallsLayout.get(ctx, packageName, targetResources,
557b5b55c8012c124182c1bd07bdd4be1e734d972d0Sunny Goyal                        widgetHost, mOpenHelper);
558b2d46ceaaa32ece85f18e049d9e30a5ccaf80e45Sunny Goyal            } catch (NameNotFoundException e) {
559b2d46ceaaa32ece85f18e049d9e30a5ccaf80e45Sunny Goyal                Log.e(TAG, "Target package for restricted profile not found", e);
560b2d46ceaaa32ece85f18e049d9e30a5ccaf80e45Sunny Goyal                return null;
561b2d46ceaaa32ece85f18e049d9e30a5ccaf80e45Sunny Goyal            }
562b2d46ceaaa32ece85f18e049d9e30a5ccaf80e45Sunny Goyal        }
563b2d46ceaaa32ece85f18e049d9e30a5ccaf80e45Sunny Goyal        return null;
564b2d46ceaaa32ece85f18e049d9e30a5ccaf80e45Sunny Goyal    }
565b2d46ceaaa32ece85f18e049d9e30a5ccaf80e45Sunny Goyal
566b5b55c8012c124182c1bd07bdd4be1e734d972d0Sunny Goyal    private DefaultLayoutParser getDefaultLayoutParser(AppWidgetHost widgetHost) {
5679d219685c5f008ddfb0f72ef01831308d8a81c2eSunny Goyal        int defaultLayout = LauncherAppState.getInstance()
5682e6da1539bc7286336b3c24d96ab76434939ce4dAdam Cohen                .getInvariantDeviceProfile().defaultLayoutId;
569b5b55c8012c124182c1bd07bdd4be1e734d972d0Sunny Goyal        return new DefaultLayoutParser(getContext(), widgetHost,
5709d219685c5f008ddfb0f72ef01831308d8a81c2eSunny Goyal                mOpenHelper, getContext().getResources(), defaultLayout);
5719d219685c5f008ddfb0f72ef01831308d8a81c2eSunny Goyal    }
5729d219685c5f008ddfb0f72ef01831308d8a81c2eSunny Goyal
573d3849d191bc0bd2d561cf03018ad90b896b13c56Sunny Goyal    /**
574f076eae0cab10f035f7b187c72a680cd220acf1bSunny Goyal     * The class is subclassed in tests to create an in-memory db.
575f076eae0cab10f035f7b187c72a680cd220acf1bSunny Goyal     */
576c190dbfb784c0bca629b6966ce314d735cf98db3Sunny Goyal    public static class DatabaseHelper extends SQLiteOpenHelper implements LayoutParserCallback {
577b5b55c8012c124182c1bd07bdd4be1e734d972d0Sunny Goyal        private final Handler mWidgetHostResetHandler;
57831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        private final Context mContext;
579dcd297f05a866e07090d6e2af8fb4b15f28cb555Adam Cohen        private long mMaxItemId = -1;
580dcd297f05a866e07090d6e2af8fb4b15f28cb555Adam Cohen        private long mMaxScreenId = -1;
58131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
582b5b55c8012c124182c1bd07bdd4be1e734d972d0Sunny Goyal        DatabaseHelper(Context context, Handler widgetHostResetHandler) {
583b5b55c8012c124182c1bd07bdd4be1e734d972d0Sunny Goyal            this(context, widgetHostResetHandler, LauncherFiles.LAUNCHER_DB);
5846fb929e0038a9d03b60540fdbf6b6914146f7b21Sunny Goyal            // Table creation sometimes fails silently, which leads to a crash loop.
5856fb929e0038a9d03b60540fdbf6b6914146f7b21Sunny Goyal            // This way, we will try to create a table every time after crash, so the device
5866fb929e0038a9d03b60540fdbf6b6914146f7b21Sunny Goyal            // would eventually be able to recover.
587b5b55c8012c124182c1bd07bdd4be1e734d972d0Sunny Goyal            if (!tableExists(Favorites.TABLE_NAME) || !tableExists(WorkspaceScreens.TABLE_NAME)) {
5886fb929e0038a9d03b60540fdbf6b6914146f7b21Sunny Goyal                Log.e(TAG, "Tables are missing after onCreate has been called. Trying to recreate");
5896fb929e0038a9d03b60540fdbf6b6914146f7b21Sunny Goyal                // This operation is a no-op if the table already exists.
5906fb929e0038a9d03b60540fdbf6b6914146f7b21Sunny Goyal                addFavoritesTable(getWritableDatabase(), true);
5916fb929e0038a9d03b60540fdbf6b6914146f7b21Sunny Goyal                addWorkspacesTable(getWritableDatabase(), true);
5926fb929e0038a9d03b60540fdbf6b6914146f7b21Sunny Goyal            }
5936fb929e0038a9d03b60540fdbf6b6914146f7b21Sunny Goyal
5947eab3cc374aeaaa6e305a3f583901da9844dfe08Sunny Goyal            initIds();
59531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        }
59631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
597f076eae0cab10f035f7b187c72a680cd220acf1bSunny Goyal        /**
598c190dbfb784c0bca629b6966ce314d735cf98db3Sunny Goyal         * Constructor used in tests and for restore.
599f076eae0cab10f035f7b187c72a680cd220acf1bSunny Goyal         */
6007eab3cc374aeaaa6e305a3f583901da9844dfe08Sunny Goyal        public DatabaseHelper(
601b5b55c8012c124182c1bd07bdd4be1e734d972d0Sunny Goyal                Context context, Handler widgetHostResetHandler, String tableName) {
602bf67f3b1847c435bbaf19600ec7babbb9d4c7674Sunny Goyal            super(new NoLocaleSqliteContext(context), tableName, null, DATABASE_VERSION);
603f076eae0cab10f035f7b187c72a680cd220acf1bSunny Goyal            mContext = context;
604b5b55c8012c124182c1bd07bdd4be1e734d972d0Sunny Goyal            mWidgetHostResetHandler = widgetHostResetHandler;
6057eab3cc374aeaaa6e305a3f583901da9844dfe08Sunny Goyal        }
606f076eae0cab10f035f7b187c72a680cd220acf1bSunny Goyal
6077eab3cc374aeaaa6e305a3f583901da9844dfe08Sunny Goyal        protected void initIds() {
6087eab3cc374aeaaa6e305a3f583901da9844dfe08Sunny Goyal            // In the case where neither onCreate nor onUpgrade gets called, we read the maxId from
6097eab3cc374aeaaa6e305a3f583901da9844dfe08Sunny Goyal            // the DB here
6107eab3cc374aeaaa6e305a3f583901da9844dfe08Sunny Goyal            if (mMaxItemId == -1) {
6117eab3cc374aeaaa6e305a3f583901da9844dfe08Sunny Goyal                mMaxItemId = initializeMaxItemId(getWritableDatabase());
6127eab3cc374aeaaa6e305a3f583901da9844dfe08Sunny Goyal            }
6137eab3cc374aeaaa6e305a3f583901da9844dfe08Sunny Goyal            if (mMaxScreenId == -1) {
6147eab3cc374aeaaa6e305a3f583901da9844dfe08Sunny Goyal                mMaxScreenId = initializeMaxScreenId(getWritableDatabase());
6157eab3cc374aeaaa6e305a3f583901da9844dfe08Sunny Goyal            }
616f076eae0cab10f035f7b187c72a680cd220acf1bSunny Goyal        }
617f076eae0cab10f035f7b187c72a680cd220acf1bSunny Goyal
6186fb929e0038a9d03b60540fdbf6b6914146f7b21Sunny Goyal        private boolean tableExists(String tableName) {
6196fb929e0038a9d03b60540fdbf6b6914146f7b21Sunny Goyal            Cursor c = getReadableDatabase().query(
6206fb929e0038a9d03b60540fdbf6b6914146f7b21Sunny Goyal                    true, "sqlite_master", new String[] {"tbl_name"},
6216fb929e0038a9d03b60540fdbf6b6914146f7b21Sunny Goyal                    "tbl_name = ?", new String[] {tableName},
6226fb929e0038a9d03b60540fdbf6b6914146f7b21Sunny Goyal                    null, null, null, null, null);
6236fb929e0038a9d03b60540fdbf6b6914146f7b21Sunny Goyal            try {
6246fb929e0038a9d03b60540fdbf6b6914146f7b21Sunny Goyal                return c.getCount() > 0;
6256fb929e0038a9d03b60540fdbf6b6914146f7b21Sunny Goyal            } finally {
6266fb929e0038a9d03b60540fdbf6b6914146f7b21Sunny Goyal                c.close();
6276fb929e0038a9d03b60540fdbf6b6914146f7b21Sunny Goyal            }
6286fb929e0038a9d03b60540fdbf6b6914146f7b21Sunny Goyal        }
6296fb929e0038a9d03b60540fdbf6b6914146f7b21Sunny Goyal
63031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        @Override
63131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        public void onCreate(SQLiteDatabase db) {
632a30ce8e6b25e41f392a41fd4d0d3e0a424a84dadJoe Onorato            if (LOGD) Log.d(TAG, "creating new launcher database");
633a8c760d395e1d2a78522427738302fbca3a72453Michael Jurka
634dcd297f05a866e07090d6e2af8fb4b15f28cb555Adam Cohen            mMaxItemId = 1;
635dcd297f05a866e07090d6e2af8fb4b15f28cb555Adam Cohen            mMaxScreenId = 0;
636a8c760d395e1d2a78522427738302fbca3a72453Michael Jurka
6376fb929e0038a9d03b60540fdbf6b6914146f7b21Sunny Goyal            addFavoritesTable(db, false);
6386fb929e0038a9d03b60540fdbf6b6914146f7b21Sunny Goyal            addWorkspacesTable(db, false);
6396fb929e0038a9d03b60540fdbf6b6914146f7b21Sunny Goyal
6406fb929e0038a9d03b60540fdbf6b6914146f7b21Sunny Goyal            // Fresh and clean launcher DB.
6416fb929e0038a9d03b60540fdbf6b6914146f7b21Sunny Goyal            mMaxItemId = initializeMaxItemId(db);
642f076eae0cab10f035f7b187c72a680cd220acf1bSunny Goyal            onEmptyDbCreated();
643f076eae0cab10f035f7b187c72a680cd220acf1bSunny Goyal        }
644f076eae0cab10f035f7b187c72a680cd220acf1bSunny Goyal
645f076eae0cab10f035f7b187c72a680cd220acf1bSunny Goyal        /**
646f076eae0cab10f035f7b187c72a680cd220acf1bSunny Goyal         * Overriden in tests.
647f076eae0cab10f035f7b187c72a680cd220acf1bSunny Goyal         */
648f076eae0cab10f035f7b187c72a680cd220acf1bSunny Goyal        protected void onEmptyDbCreated() {
649b5b55c8012c124182c1bd07bdd4be1e734d972d0Sunny Goyal            // Database was just created, so wipe any previous widgets
650b5b55c8012c124182c1bd07bdd4be1e734d972d0Sunny Goyal            if (mWidgetHostResetHandler != null) {
651b5b55c8012c124182c1bd07bdd4be1e734d972d0Sunny Goyal                new AppWidgetHost(mContext, Launcher.APPWIDGET_HOST_ID).deleteHost();
652da4fe1a6244457f144e0a331cada3ada17157809Sunny Goyal                mWidgetHostResetHandler.sendMessage(Message.obtain(
653da4fe1a6244457f144e0a331cada3ada17157809Sunny Goyal                        mWidgetHostResetHandler,
654da4fe1a6244457f144e0a331cada3ada17157809Sunny Goyal                        ChangeListenerWrapper.MSG_APP_WIDGET_HOST_RESET,
655da4fe1a6244457f144e0a331cada3ada17157809Sunny Goyal                        mContext
656da4fe1a6244457f144e0a331cada3ada17157809Sunny Goyal                ));
657b5b55c8012c124182c1bd07bdd4be1e734d972d0Sunny Goyal            }
658b5b55c8012c124182c1bd07bdd4be1e734d972d0Sunny Goyal
659f076eae0cab10f035f7b187c72a680cd220acf1bSunny Goyal            // Set the flag for empty DB
660f076eae0cab10f035f7b187c72a680cd220acf1bSunny Goyal            Utilities.getPrefs(mContext).edit().putBoolean(EMPTY_DATABASE_CREATED, true).commit();
6616fb929e0038a9d03b60540fdbf6b6914146f7b21Sunny Goyal
6626fb929e0038a9d03b60540fdbf6b6914146f7b21Sunny Goyal            // When a new DB is created, remove all previously stored managed profile information.
663f076eae0cab10f035f7b187c72a680cd220acf1bSunny Goyal            ManagedProfileHeuristic.processAllUsers(Collections.<UserHandleCompat>emptyList(),
664f076eae0cab10f035f7b187c72a680cd220acf1bSunny Goyal                    mContext);
6656fb929e0038a9d03b60540fdbf6b6914146f7b21Sunny Goyal        }
6666fb929e0038a9d03b60540fdbf6b6914146f7b21Sunny Goyal
667e8f7d5a67ef71b1c6488f52145482a8876c1b708Sunny Goyal        public long getDefaultUserSerial() {
668f076eae0cab10f035f7b187c72a680cd220acf1bSunny Goyal            return UserManagerCompat.getInstance(mContext).getSerialNumberForUser(
669ed13187a745866483139e2878037e1f8427ce567Kenny Guy                    UserHandleCompat.myUserHandle());
670f076eae0cab10f035f7b187c72a680cd220acf1bSunny Goyal        }
671ed13187a745866483139e2878037e1f8427ce567Kenny Guy
672f076eae0cab10f035f7b187c72a680cd220acf1bSunny Goyal        private void addFavoritesTable(SQLiteDatabase db, boolean optional) {
673c190dbfb784c0bca629b6966ce314d735cf98db3Sunny Goyal            Favorites.addTableToDb(db, getDefaultUserSerial(), optional);
67431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        }
67531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
6766fb929e0038a9d03b60540fdbf6b6914146f7b21Sunny Goyal        private void addWorkspacesTable(SQLiteDatabase db, boolean optional) {
6776fb929e0038a9d03b60540fdbf6b6914146f7b21Sunny Goyal            String ifNotExists = optional ? " IF NOT EXISTS " : "";
678b5b55c8012c124182c1bd07bdd4be1e734d972d0Sunny Goyal            db.execSQL("CREATE TABLE " + ifNotExists + WorkspaceScreens.TABLE_NAME + " (" +
679d2f3819c34d8ecdfdb42bf973b6db3aac259bd62Sunny Goyal                    LauncherSettings.WorkspaceScreens._ID + " INTEGER PRIMARY KEY," +
6801ada10d5950f45aba998a7722bfde2e529201f48Chris Wren                    LauncherSettings.WorkspaceScreens.SCREEN_RANK + " INTEGER," +
6811ada10d5950f45aba998a7722bfde2e529201f48Chris Wren                    LauncherSettings.ChangeLogColumns.MODIFIED + " INTEGER NOT NULL DEFAULT 0" +
682dcd297f05a866e07090d6e2af8fb4b15f28cb555Adam Cohen                    ");");
683dcd297f05a866e07090d6e2af8fb4b15f28cb555Adam Cohen        }
684dcd297f05a866e07090d6e2af8fb4b15f28cb555Adam Cohen
685119285e755f8572632dc845d72320d9eed378f69Adam Cohen        private void removeOrphanedItems(SQLiteDatabase db) {
686f9c14de995d31a19123397c10e4f43bd0fe9eb56Adam Cohen            // Delete items directly on the workspace who's screen id doesn't exist
687f9c14de995d31a19123397c10e4f43bd0fe9eb56Adam Cohen            //  "DELETE FROM favorites WHERE screen NOT IN (SELECT _id FROM workspaceScreens)
688f9c14de995d31a19123397c10e4f43bd0fe9eb56Adam Cohen            //   AND container = -100"
689b5b55c8012c124182c1bd07bdd4be1e734d972d0Sunny Goyal            String removeOrphanedDesktopItems = "DELETE FROM " + Favorites.TABLE_NAME +
690f9c14de995d31a19123397c10e4f43bd0fe9eb56Adam Cohen                    " WHERE " +
691119285e755f8572632dc845d72320d9eed378f69Adam Cohen                    LauncherSettings.Favorites.SCREEN + " NOT IN (SELECT " +
692b5b55c8012c124182c1bd07bdd4be1e734d972d0Sunny Goyal                    LauncherSettings.WorkspaceScreens._ID + " FROM " + WorkspaceScreens.TABLE_NAME + ")" +
693f9c14de995d31a19123397c10e4f43bd0fe9eb56Adam Cohen                    " AND " +
694f9c14de995d31a19123397c10e4f43bd0fe9eb56Adam Cohen                    LauncherSettings.Favorites.CONTAINER + " = " +
695f9c14de995d31a19123397c10e4f43bd0fe9eb56Adam Cohen                    LauncherSettings.Favorites.CONTAINER_DESKTOP;
696f9c14de995d31a19123397c10e4f43bd0fe9eb56Adam Cohen            db.execSQL(removeOrphanedDesktopItems);
697f9c14de995d31a19123397c10e4f43bd0fe9eb56Adam Cohen
698f9c14de995d31a19123397c10e4f43bd0fe9eb56Adam Cohen            // Delete items contained in folders which no longer exist (after above statement)
699f9c14de995d31a19123397c10e4f43bd0fe9eb56Adam Cohen            //  "DELETE FROM favorites  WHERE container <> -100 AND container <> -101 AND container
700f9c14de995d31a19123397c10e4f43bd0fe9eb56Adam Cohen            //   NOT IN (SELECT _id FROM favorites WHERE itemType = 2)"
701b5b55c8012c124182c1bd07bdd4be1e734d972d0Sunny Goyal            String removeOrphanedFolderItems = "DELETE FROM " + Favorites.TABLE_NAME +
702f9c14de995d31a19123397c10e4f43bd0fe9eb56Adam Cohen                    " WHERE " +
703f9c14de995d31a19123397c10e4f43bd0fe9eb56Adam Cohen                    LauncherSettings.Favorites.CONTAINER + " <> " +
704f9c14de995d31a19123397c10e4f43bd0fe9eb56Adam Cohen                    LauncherSettings.Favorites.CONTAINER_DESKTOP +
705f9c14de995d31a19123397c10e4f43bd0fe9eb56Adam Cohen                    " AND "
706f9c14de995d31a19123397c10e4f43bd0fe9eb56Adam Cohen                    + LauncherSettings.Favorites.CONTAINER + " <> " +
707f9c14de995d31a19123397c10e4f43bd0fe9eb56Adam Cohen                    LauncherSettings.Favorites.CONTAINER_HOTSEAT +
708f9c14de995d31a19123397c10e4f43bd0fe9eb56Adam Cohen                    " AND "
709f9c14de995d31a19123397c10e4f43bd0fe9eb56Adam Cohen                    + LauncherSettings.Favorites.CONTAINER + " NOT IN (SELECT " +
710b5b55c8012c124182c1bd07bdd4be1e734d972d0Sunny Goyal                    LauncherSettings.Favorites._ID + " FROM " + Favorites.TABLE_NAME +
711f9c14de995d31a19123397c10e4f43bd0fe9eb56Adam Cohen                    " WHERE " + LauncherSettings.Favorites.ITEM_TYPE + " = " +
712f9c14de995d31a19123397c10e4f43bd0fe9eb56Adam Cohen                    LauncherSettings.Favorites.ITEM_TYPE_FOLDER + ")";
713f9c14de995d31a19123397c10e4f43bd0fe9eb56Adam Cohen            db.execSQL(removeOrphanedFolderItems);
714119285e755f8572632dc845d72320d9eed378f69Adam Cohen        }
715119285e755f8572632dc845d72320d9eed378f69Adam Cohen
71631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        @Override
71731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
718c763c4e4d28c256d1368be3fc1c4526c8b9bd232Winson Chung            if (LOGD) Log.d(TAG, "onUpgrade triggered: " + oldVersion);
719a2cc624e75e41f1439d738ea6cb69802dc9652beSunny Goyal            switch (oldVersion) {
720a2cc624e75e41f1439d738ea6cb69802dc9652beSunny Goyal                // The version cannot be lower that 12, as Launcher3 never supported a lower
721e87e6abc3639559f2746171999fee352cb5bd946Sunny Goyal                // version of the DB.
722a2cc624e75e41f1439d738ea6cb69802dc9652beSunny Goyal                case 12: {
723a2cc624e75e41f1439d738ea6cb69802dc9652beSunny Goyal                    // With the new shrink-wrapped and re-orderable workspaces, it makes sense
724a2cc624e75e41f1439d738ea6cb69802dc9652beSunny Goyal                    // to persist workspace screens and their relative order.
725a2cc624e75e41f1439d738ea6cb69802dc9652beSunny Goyal                    mMaxScreenId = 0;
7266fb929e0038a9d03b60540fdbf6b6914146f7b21Sunny Goyal                    addWorkspacesTable(db, false);
727d5e66bfca1dc4fdbab7513d88c6568bc4d17bca8Chris Wren                }
728a2cc624e75e41f1439d738ea6cb69802dc9652beSunny Goyal                case 13: {
729a2cc624e75e41f1439d738ea6cb69802dc9652beSunny Goyal                    db.beginTransaction();
730a2cc624e75e41f1439d738ea6cb69802dc9652beSunny Goyal                    try {
731a2cc624e75e41f1439d738ea6cb69802dc9652beSunny Goyal                        // Insert new column for holding widget provider name
732a2cc624e75e41f1439d738ea6cb69802dc9652beSunny Goyal                        db.execSQL("ALTER TABLE favorites " +
733a2cc624e75e41f1439d738ea6cb69802dc9652beSunny Goyal                                "ADD COLUMN appWidgetProvider TEXT;");
734a2cc624e75e41f1439d738ea6cb69802dc9652beSunny Goyal                        db.setTransactionSuccessful();
735a2cc624e75e41f1439d738ea6cb69802dc9652beSunny Goyal                    } catch (SQLException ex) {
736a2cc624e75e41f1439d738ea6cb69802dc9652beSunny Goyal                        Log.e(TAG, ex.getMessage(), ex);
737a2cc624e75e41f1439d738ea6cb69802dc9652beSunny Goyal                        // Old version remains, which means we wipe old data
738a2cc624e75e41f1439d738ea6cb69802dc9652beSunny Goyal                        break;
739a2cc624e75e41f1439d738ea6cb69802dc9652beSunny Goyal                    } finally {
740a2cc624e75e41f1439d738ea6cb69802dc9652beSunny Goyal                        db.endTransaction();
741a2cc624e75e41f1439d738ea6cb69802dc9652beSunny Goyal                    }
7421ada10d5950f45aba998a7722bfde2e529201f48Chris Wren                }
743a2cc624e75e41f1439d738ea6cb69802dc9652beSunny Goyal                case 14: {
744a2cc624e75e41f1439d738ea6cb69802dc9652beSunny Goyal                    db.beginTransaction();
745a2cc624e75e41f1439d738ea6cb69802dc9652beSunny Goyal                    try {
746a2cc624e75e41f1439d738ea6cb69802dc9652beSunny Goyal                        // Insert new column for holding update timestamp
747a2cc624e75e41f1439d738ea6cb69802dc9652beSunny Goyal                        db.execSQL("ALTER TABLE favorites " +
748a2cc624e75e41f1439d738ea6cb69802dc9652beSunny Goyal                                "ADD COLUMN modified INTEGER NOT NULL DEFAULT 0;");
749a2cc624e75e41f1439d738ea6cb69802dc9652beSunny Goyal                        db.execSQL("ALTER TABLE workspaceScreens " +
750a2cc624e75e41f1439d738ea6cb69802dc9652beSunny Goyal                                "ADD COLUMN modified INTEGER NOT NULL DEFAULT 0;");
751a2cc624e75e41f1439d738ea6cb69802dc9652beSunny Goyal                        db.setTransactionSuccessful();
752a2cc624e75e41f1439d738ea6cb69802dc9652beSunny Goyal                    } catch (SQLException ex) {
753a2cc624e75e41f1439d738ea6cb69802dc9652beSunny Goyal                        Log.e(TAG, ex.getMessage(), ex);
754a2cc624e75e41f1439d738ea6cb69802dc9652beSunny Goyal                        // Old version remains, which means we wipe old data
755a2cc624e75e41f1439d738ea6cb69802dc9652beSunny Goyal                        break;
756a2cc624e75e41f1439d738ea6cb69802dc9652beSunny Goyal                    } finally {
757a2cc624e75e41f1439d738ea6cb69802dc9652beSunny Goyal                        db.endTransaction();
758a2cc624e75e41f1439d738ea6cb69802dc9652beSunny Goyal                    }
759f4d081107f5984bfb92cb1a627667fea5bf1498cChris Wren                }
760a2cc624e75e41f1439d738ea6cb69802dc9652beSunny Goyal                case 15: {
7615d85c44fd873c740dc191b28424c2ee367d730a2Sunny Goyal                    if (!addIntegerColumn(db, Favorites.RESTORED, 0)) {
762a2cc624e75e41f1439d738ea6cb69802dc9652beSunny Goyal                        // Old version remains, which means we wipe old data
763a2cc624e75e41f1439d738ea6cb69802dc9652beSunny Goyal                        break;
764a2cc624e75e41f1439d738ea6cb69802dc9652beSunny Goyal                    }
765ed13187a745866483139e2878037e1f8427ce567Kenny Guy                }
766a2cc624e75e41f1439d738ea6cb69802dc9652beSunny Goyal                case 16: {
767a2cc624e75e41f1439d738ea6cb69802dc9652beSunny Goyal                    // We use the db version upgrade here to identify users who may not have seen
768a2cc624e75e41f1439d738ea6cb69802dc9652beSunny Goyal                    // clings yet (because they weren't available), but for whom the clings are now
769a2cc624e75e41f1439d738ea6cb69802dc9652beSunny Goyal                    // available (tablet users). Because one of the possible cling flows (migration)
770a2cc624e75e41f1439d738ea6cb69802dc9652beSunny Goyal                    // is very destructive (wipes out workspaces), we want to prevent this from showing
771a2cc624e75e41f1439d738ea6cb69802dc9652beSunny Goyal                    // until clear data. We do so by marking that the clings have been shown.
772d80439875557e8c94052e5b0f0327b656370b5fdSunny Goyal                    LauncherClings.markFirstRunClingDismissed(mContext);
773a2cc624e75e41f1439d738ea6cb69802dc9652beSunny Goyal                }
774a2cc624e75e41f1439d738ea6cb69802dc9652beSunny Goyal                case 17: {
775a2cc624e75e41f1439d738ea6cb69802dc9652beSunny Goyal                    // No-op
776a2cc624e75e41f1439d738ea6cb69802dc9652beSunny Goyal                }
777a2cc624e75e41f1439d738ea6cb69802dc9652beSunny Goyal                case 18: {
778a2cc624e75e41f1439d738ea6cb69802dc9652beSunny Goyal                    // Due to a data loss bug, some users may have items associated with screen ids
779a2cc624e75e41f1439d738ea6cb69802dc9652beSunny Goyal                    // which no longer exist. Since this can cause other problems, and since the user
780a2cc624e75e41f1439d738ea6cb69802dc9652beSunny Goyal                    // will never see these items anyway, we use database upgrade as an opportunity to
781a2cc624e75e41f1439d738ea6cb69802dc9652beSunny Goyal                    // clean things up.
782a2cc624e75e41f1439d738ea6cb69802dc9652beSunny Goyal                    removeOrphanedItems(db);
783a2cc624e75e41f1439d738ea6cb69802dc9652beSunny Goyal                }
784a2cc624e75e41f1439d738ea6cb69802dc9652beSunny Goyal                case 19: {
785a2cc624e75e41f1439d738ea6cb69802dc9652beSunny Goyal                    // Add userId column
786a2cc624e75e41f1439d738ea6cb69802dc9652beSunny Goyal                    if (!addProfileColumn(db)) {
787a2cc624e75e41f1439d738ea6cb69802dc9652beSunny Goyal                        // Old version remains, which means we wipe old data
788a2cc624e75e41f1439d738ea6cb69802dc9652beSunny Goyal                        break;
789a2cc624e75e41f1439d738ea6cb69802dc9652beSunny Goyal                    }
790a2cc624e75e41f1439d738ea6cb69802dc9652beSunny Goyal                }
791a2cc624e75e41f1439d738ea6cb69802dc9652beSunny Goyal                case 20:
792a2cc624e75e41f1439d738ea6cb69802dc9652beSunny Goyal                    if (!updateFolderItemsRank(db, true)) {
793a2cc624e75e41f1439d738ea6cb69802dc9652beSunny Goyal                        break;
794a2cc624e75e41f1439d738ea6cb69802dc9652beSunny Goyal                    }
795d2f3819c34d8ecdfdb42bf973b6db3aac259bd62Sunny Goyal                case 21:
796d2f3819c34d8ecdfdb42bf973b6db3aac259bd62Sunny Goyal                    // Recreate workspace table with screen id a primary key
797d2f3819c34d8ecdfdb42bf973b6db3aac259bd62Sunny Goyal                    if (!recreateWorkspaceTable(db)) {
798d2f3819c34d8ecdfdb42bf973b6db3aac259bd62Sunny Goyal                        break;
799d2f3819c34d8ecdfdb42bf973b6db3aac259bd62Sunny Goyal                    }
800d2f3819c34d8ecdfdb42bf973b6db3aac259bd62Sunny Goyal                case 22: {
8015d85c44fd873c740dc191b28424c2ee367d730a2Sunny Goyal                    if (!addIntegerColumn(db, Favorites.OPTIONS, 0)) {
8025d85c44fd873c740dc191b28424c2ee367d730a2Sunny Goyal                        // Old version remains, which means we wipe old data
8035d85c44fd873c740dc191b28424c2ee367d730a2Sunny Goyal                        break;
8045d85c44fd873c740dc191b28424c2ee367d730a2Sunny Goyal                    }
8055d85c44fd873c740dc191b28424c2ee367d730a2Sunny Goyal                }
8060b037789662d1c16627dc5b509d1ac55bc76971bSunny Goyal                case 23:
8075c97f51fd9443e461a360ff7852aa223c05d667fSunny Goyal                    // No-op
808e2fba6cba10d9b7996ebb86dfd67bed96cff0dedSunny Goyal                case 24:
809e2fba6cba10d9b7996ebb86dfd67bed96cff0dedSunny Goyal                    ManagedProfileHeuristic.markExistingUsersForNoFolderCreation(mContext);
8105c97f51fd9443e461a360ff7852aa223c05d667fSunny Goyal                case 25:
8115c97f51fd9443e461a360ff7852aa223c05d667fSunny Goyal                    convertShortcutsToLauncherActivities(db);
812a9e2f5abb3c21d9721939c625ffb0caabb34e8d9Sunny Goyal                case 26:
813a9e2f5abb3c21d9721939c625ffb0caabb34e8d9Sunny Goyal                    // QSB was moved to the grid. Clear the first row on screen 0.
814a9e2f5abb3c21d9721939c625ffb0caabb34e8d9Sunny Goyal                    if (FeatureFlags.QSB_ON_FIRST_SCREEN &&
815a9e2f5abb3c21d9721939c625ffb0caabb34e8d9Sunny Goyal                            !LauncherDbUtils.prepareScreenZeroToHostQsb(db)) {
816a9e2f5abb3c21d9721939c625ffb0caabb34e8d9Sunny Goyal                        break;
817a9e2f5abb3c21d9721939c625ffb0caabb34e8d9Sunny Goyal                    }
818a9e2f5abb3c21d9721939c625ffb0caabb34e8d9Sunny Goyal                case 27: {
819a2cc624e75e41f1439d738ea6cb69802dc9652beSunny Goyal                    // DB Upgraded successfully
820a2cc624e75e41f1439d738ea6cb69802dc9652beSunny Goyal                    return;
82108f7261d11a53ae4b330ad4fa897b8519de3d750Sunny Goyal                }
82208f7261d11a53ae4b330ad4fa897b8519de3d750Sunny Goyal            }
82308f7261d11a53ae4b330ad4fa897b8519de3d750Sunny Goyal
824a2cc624e75e41f1439d738ea6cb69802dc9652beSunny Goyal            // DB was not upgraded
825a2cc624e75e41f1439d738ea6cb69802dc9652beSunny Goyal            Log.w(TAG, "Destroying all old data.");
826a2cc624e75e41f1439d738ea6cb69802dc9652beSunny Goyal            createEmptyDB(db);
82731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        }
8287eb9e5e9257635cee5609ed633eb575fda93dd5aRomain Guy
8299b1d062b0f1c4714883444df874e1a078764caa1Adam Cohen        @Override
8309b1d062b0f1c4714883444df874e1a078764caa1Adam Cohen        public void onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion) {
8319b1d062b0f1c4714883444df874e1a078764caa1Adam Cohen            // This shouldn't happen -- throw our hands up in the air and start over.
8329b1d062b0f1c4714883444df874e1a078764caa1Adam Cohen            Log.w(TAG, "Database version downgrade from: " + oldVersion + " to " + newVersion +
8339b1d062b0f1c4714883444df874e1a078764caa1Adam Cohen                    ". Wiping databse.");
83442de82ff8d2aa182594709e63e608bbcd4610cfaSunny Goyal            createEmptyDB(db);
83542de82ff8d2aa182594709e63e608bbcd4610cfaSunny Goyal        }
83642de82ff8d2aa182594709e63e608bbcd4610cfaSunny Goyal
83742de82ff8d2aa182594709e63e608bbcd4610cfaSunny Goyal        /**
83842de82ff8d2aa182594709e63e608bbcd4610cfaSunny Goyal         * Clears all the data for a fresh start.
83942de82ff8d2aa182594709e63e608bbcd4610cfaSunny Goyal         */
84042de82ff8d2aa182594709e63e608bbcd4610cfaSunny Goyal        public void createEmptyDB(SQLiteDatabase db) {
841b5b55c8012c124182c1bd07bdd4be1e734d972d0Sunny Goyal            db.execSQL("DROP TABLE IF EXISTS " + Favorites.TABLE_NAME);
842b5b55c8012c124182c1bd07bdd4be1e734d972d0Sunny Goyal            db.execSQL("DROP TABLE IF EXISTS " + WorkspaceScreens.TABLE_NAME);
8439b1d062b0f1c4714883444df874e1a078764caa1Adam Cohen            onCreate(db);
8449b1d062b0f1c4714883444df874e1a078764caa1Adam Cohen        }
8459b1d062b0f1c4714883444df874e1a078764caa1Adam Cohen
846d2f3819c34d8ecdfdb42bf973b6db3aac259bd62Sunny Goyal        /**
8470b037789662d1c16627dc5b509d1ac55bc76971bSunny Goyal         * Replaces all shortcuts of type {@link Favorites#ITEM_TYPE_SHORTCUT} which have a valid
8480b037789662d1c16627dc5b509d1ac55bc76971bSunny Goyal         * launcher activity target with {@link Favorites#ITEM_TYPE_APPLICATION}.
8490b037789662d1c16627dc5b509d1ac55bc76971bSunny Goyal         */
850316490e636aad788fcfbfc2e04dd4f0e145bdd00Sunny Goyal        @Thunk void convertShortcutsToLauncherActivities(SQLiteDatabase db) {
8510b037789662d1c16627dc5b509d1ac55bc76971bSunny Goyal            db.beginTransaction();
8520b037789662d1c16627dc5b509d1ac55bc76971bSunny Goyal            Cursor c = null;
8530b037789662d1c16627dc5b509d1ac55bc76971bSunny Goyal            SQLiteStatement updateStmt = null;
8540b037789662d1c16627dc5b509d1ac55bc76971bSunny Goyal
8550b037789662d1c16627dc5b509d1ac55bc76971bSunny Goyal            try {
8560b037789662d1c16627dc5b509d1ac55bc76971bSunny Goyal                // Only consider the primary user as other users can't have a shortcut.
857b5b55c8012c124182c1bd07bdd4be1e734d972d0Sunny Goyal                long userSerial = getDefaultUserSerial();
858b5b55c8012c124182c1bd07bdd4be1e734d972d0Sunny Goyal                c = db.query(Favorites.TABLE_NAME, new String[] {
8590b037789662d1c16627dc5b509d1ac55bc76971bSunny Goyal                        Favorites._ID,
8600b037789662d1c16627dc5b509d1ac55bc76971bSunny Goyal                        Favorites.INTENT,
8610b037789662d1c16627dc5b509d1ac55bc76971bSunny Goyal                    }, "itemType=" + Favorites.ITEM_TYPE_SHORTCUT + " AND profileId=" + userSerial,
8620b037789662d1c16627dc5b509d1ac55bc76971bSunny Goyal                    null, null, null, null);
8630b037789662d1c16627dc5b509d1ac55bc76971bSunny Goyal
8640b037789662d1c16627dc5b509d1ac55bc76971bSunny Goyal                updateStmt = db.compileStatement("UPDATE favorites SET itemType="
8650b037789662d1c16627dc5b509d1ac55bc76971bSunny Goyal                        + Favorites.ITEM_TYPE_APPLICATION + " WHERE _id=?");
8660b037789662d1c16627dc5b509d1ac55bc76971bSunny Goyal
8670b037789662d1c16627dc5b509d1ac55bc76971bSunny Goyal                final int idIndex = c.getColumnIndexOrThrow(Favorites._ID);
8680b037789662d1c16627dc5b509d1ac55bc76971bSunny Goyal                final int intentIndex = c.getColumnIndexOrThrow(Favorites.INTENT);
8690b037789662d1c16627dc5b509d1ac55bc76971bSunny Goyal
8700b037789662d1c16627dc5b509d1ac55bc76971bSunny Goyal                while (c.moveToNext()) {
8710b037789662d1c16627dc5b509d1ac55bc76971bSunny Goyal                    String intentDescription = c.getString(intentIndex);
8720b037789662d1c16627dc5b509d1ac55bc76971bSunny Goyal                    Intent intent;
8730b037789662d1c16627dc5b509d1ac55bc76971bSunny Goyal                    try {
8740b037789662d1c16627dc5b509d1ac55bc76971bSunny Goyal                        intent = Intent.parseUri(intentDescription, 0);
8750b037789662d1c16627dc5b509d1ac55bc76971bSunny Goyal                    } catch (URISyntaxException e) {
8760b037789662d1c16627dc5b509d1ac55bc76971bSunny Goyal                        Log.e(TAG, "Unable to parse intent", e);
8770b037789662d1c16627dc5b509d1ac55bc76971bSunny Goyal                        continue;
8780b037789662d1c16627dc5b509d1ac55bc76971bSunny Goyal                    }
8790b037789662d1c16627dc5b509d1ac55bc76971bSunny Goyal
8804e5cc64eaf1f63d866d51ce0a6bbafb3d4085c21Sunny Goyal                    if (!Utilities.isLauncherAppTarget(intent)) {
8810b037789662d1c16627dc5b509d1ac55bc76971bSunny Goyal                        continue;
8820b037789662d1c16627dc5b509d1ac55bc76971bSunny Goyal                    }
8830b037789662d1c16627dc5b509d1ac55bc76971bSunny Goyal
8840b037789662d1c16627dc5b509d1ac55bc76971bSunny Goyal                    long id = c.getLong(idIndex);
8850b037789662d1c16627dc5b509d1ac55bc76971bSunny Goyal                    updateStmt.bindLong(1, id);
886c22841bfd1961781a84cfccb98d1c2937a9adee4Sunny Goyal                    updateStmt.executeUpdateDelete();
8870b037789662d1c16627dc5b509d1ac55bc76971bSunny Goyal                }
8880b037789662d1c16627dc5b509d1ac55bc76971bSunny Goyal                db.setTransactionSuccessful();
8890b037789662d1c16627dc5b509d1ac55bc76971bSunny Goyal            } catch (SQLException ex) {
8900b037789662d1c16627dc5b509d1ac55bc76971bSunny Goyal                Log.w(TAG, "Error deduping shortcuts", ex);
8910b037789662d1c16627dc5b509d1ac55bc76971bSunny Goyal            } finally {
8920b037789662d1c16627dc5b509d1ac55bc76971bSunny Goyal                db.endTransaction();
8930b037789662d1c16627dc5b509d1ac55bc76971bSunny Goyal                if (c != null) {
8940b037789662d1c16627dc5b509d1ac55bc76971bSunny Goyal                    c.close();
8950b037789662d1c16627dc5b509d1ac55bc76971bSunny Goyal                }
8960b037789662d1c16627dc5b509d1ac55bc76971bSunny Goyal                if (updateStmt != null) {
8970b037789662d1c16627dc5b509d1ac55bc76971bSunny Goyal                    updateStmt.close();
8980b037789662d1c16627dc5b509d1ac55bc76971bSunny Goyal                }
8990b037789662d1c16627dc5b509d1ac55bc76971bSunny Goyal            }
9000b037789662d1c16627dc5b509d1ac55bc76971bSunny Goyal        }
9010b037789662d1c16627dc5b509d1ac55bc76971bSunny Goyal
9020b037789662d1c16627dc5b509d1ac55bc76971bSunny Goyal        /**
903d2f3819c34d8ecdfdb42bf973b6db3aac259bd62Sunny Goyal         * Recreates workspace table and migrates data to the new table.
904d2f3819c34d8ecdfdb42bf973b6db3aac259bd62Sunny Goyal         */
905d2f3819c34d8ecdfdb42bf973b6db3aac259bd62Sunny Goyal        public boolean recreateWorkspaceTable(SQLiteDatabase db) {
906d2f3819c34d8ecdfdb42bf973b6db3aac259bd62Sunny Goyal            db.beginTransaction();
907d2f3819c34d8ecdfdb42bf973b6db3aac259bd62Sunny Goyal            try {
908b5b55c8012c124182c1bd07bdd4be1e734d972d0Sunny Goyal                Cursor c = db.query(WorkspaceScreens.TABLE_NAME,
909d2f3819c34d8ecdfdb42bf973b6db3aac259bd62Sunny Goyal                        new String[] {LauncherSettings.WorkspaceScreens._ID},
910d2f3819c34d8ecdfdb42bf973b6db3aac259bd62Sunny Goyal                        null, null, null, null,
911d2f3819c34d8ecdfdb42bf973b6db3aac259bd62Sunny Goyal                        LauncherSettings.WorkspaceScreens.SCREEN_RANK);
912d2f3819c34d8ecdfdb42bf973b6db3aac259bd62Sunny Goyal                ArrayList<Long> sortedIDs = new ArrayList<Long>();
913d2f3819c34d8ecdfdb42bf973b6db3aac259bd62Sunny Goyal                long maxId = 0;
914d2f3819c34d8ecdfdb42bf973b6db3aac259bd62Sunny Goyal                try {
915d2f3819c34d8ecdfdb42bf973b6db3aac259bd62Sunny Goyal                    while (c.moveToNext()) {
916d2f3819c34d8ecdfdb42bf973b6db3aac259bd62Sunny Goyal                        Long id = c.getLong(0);
917d2f3819c34d8ecdfdb42bf973b6db3aac259bd62Sunny Goyal                        if (!sortedIDs.contains(id)) {
918d2f3819c34d8ecdfdb42bf973b6db3aac259bd62Sunny Goyal                            sortedIDs.add(id);
919d2f3819c34d8ecdfdb42bf973b6db3aac259bd62Sunny Goyal                            maxId = Math.max(maxId, id);
920d2f3819c34d8ecdfdb42bf973b6db3aac259bd62Sunny Goyal                        }
921d2f3819c34d8ecdfdb42bf973b6db3aac259bd62Sunny Goyal                    }
922d2f3819c34d8ecdfdb42bf973b6db3aac259bd62Sunny Goyal                } finally {
923d2f3819c34d8ecdfdb42bf973b6db3aac259bd62Sunny Goyal                    c.close();
924d2f3819c34d8ecdfdb42bf973b6db3aac259bd62Sunny Goyal                }
925d2f3819c34d8ecdfdb42bf973b6db3aac259bd62Sunny Goyal
926b5b55c8012c124182c1bd07bdd4be1e734d972d0Sunny Goyal                db.execSQL("DROP TABLE IF EXISTS " + WorkspaceScreens.TABLE_NAME);
9276fb929e0038a9d03b60540fdbf6b6914146f7b21Sunny Goyal                addWorkspacesTable(db, false);
928d2f3819c34d8ecdfdb42bf973b6db3aac259bd62Sunny Goyal
929d2f3819c34d8ecdfdb42bf973b6db3aac259bd62Sunny Goyal                // Add all screen ids back
930d2f3819c34d8ecdfdb42bf973b6db3aac259bd62Sunny Goyal                int total = sortedIDs.size();
931d2f3819c34d8ecdfdb42bf973b6db3aac259bd62Sunny Goyal                for (int i = 0; i < total; i++) {
932d2f3819c34d8ecdfdb42bf973b6db3aac259bd62Sunny Goyal                    ContentValues values = new ContentValues();
933d2f3819c34d8ecdfdb42bf973b6db3aac259bd62Sunny Goyal                    values.put(LauncherSettings.WorkspaceScreens._ID, sortedIDs.get(i));
934d2f3819c34d8ecdfdb42bf973b6db3aac259bd62Sunny Goyal                    values.put(LauncherSettings.WorkspaceScreens.SCREEN_RANK, i);
935d2f3819c34d8ecdfdb42bf973b6db3aac259bd62Sunny Goyal                    addModifiedTime(values);
936b5b55c8012c124182c1bd07bdd4be1e734d972d0Sunny Goyal                    db.insertOrThrow(WorkspaceScreens.TABLE_NAME, null, values);
937d2f3819c34d8ecdfdb42bf973b6db3aac259bd62Sunny Goyal                }
938d2f3819c34d8ecdfdb42bf973b6db3aac259bd62Sunny Goyal                db.setTransactionSuccessful();
939d2f3819c34d8ecdfdb42bf973b6db3aac259bd62Sunny Goyal                mMaxScreenId = maxId;
940d2f3819c34d8ecdfdb42bf973b6db3aac259bd62Sunny Goyal            } catch (SQLException ex) {
941d2f3819c34d8ecdfdb42bf973b6db3aac259bd62Sunny Goyal                // Old version remains, which means we wipe old data
942d2f3819c34d8ecdfdb42bf973b6db3aac259bd62Sunny Goyal                Log.e(TAG, ex.getMessage(), ex);
943d2f3819c34d8ecdfdb42bf973b6db3aac259bd62Sunny Goyal                return false;
944d2f3819c34d8ecdfdb42bf973b6db3aac259bd62Sunny Goyal            } finally {
945d2f3819c34d8ecdfdb42bf973b6db3aac259bd62Sunny Goyal                db.endTransaction();
946d2f3819c34d8ecdfdb42bf973b6db3aac259bd62Sunny Goyal            }
947d2f3819c34d8ecdfdb42bf973b6db3aac259bd62Sunny Goyal            return true;
948d2f3819c34d8ecdfdb42bf973b6db3aac259bd62Sunny Goyal        }
949d2f3819c34d8ecdfdb42bf973b6db3aac259bd62Sunny Goyal
950091440a9cb9d4f42406631004aa484cbb79214caAdam Cohen        @Thunk boolean updateFolderItemsRank(SQLiteDatabase db, boolean addRankColumn) {
95108f7261d11a53ae4b330ad4fa897b8519de3d750Sunny Goyal            db.beginTransaction();
95208f7261d11a53ae4b330ad4fa897b8519de3d750Sunny Goyal            try {
95308f7261d11a53ae4b330ad4fa897b8519de3d750Sunny Goyal                if (addRankColumn) {
95408f7261d11a53ae4b330ad4fa897b8519de3d750Sunny Goyal                    // Insert new column for holding rank
95508f7261d11a53ae4b330ad4fa897b8519de3d750Sunny Goyal                    db.execSQL("ALTER TABLE favorites ADD COLUMN rank INTEGER NOT NULL DEFAULT 0;");
95608f7261d11a53ae4b330ad4fa897b8519de3d750Sunny Goyal                }
95708f7261d11a53ae4b330ad4fa897b8519de3d750Sunny Goyal
95808f7261d11a53ae4b330ad4fa897b8519de3d750Sunny Goyal                // Get a map for folder ID to folder width
95908f7261d11a53ae4b330ad4fa897b8519de3d750Sunny Goyal                Cursor c = db.rawQuery("SELECT container, MAX(cellX) FROM favorites"
96008f7261d11a53ae4b330ad4fa897b8519de3d750Sunny Goyal                        + " WHERE container IN (SELECT _id FROM favorites WHERE itemType = ?)"
96108f7261d11a53ae4b330ad4fa897b8519de3d750Sunny Goyal                        + " GROUP BY container;",
96208f7261d11a53ae4b330ad4fa897b8519de3d750Sunny Goyal                        new String[] {Integer.toString(LauncherSettings.Favorites.ITEM_TYPE_FOLDER)});
96308f7261d11a53ae4b330ad4fa897b8519de3d750Sunny Goyal
96408f7261d11a53ae4b330ad4fa897b8519de3d750Sunny Goyal                while (c.moveToNext()) {
965c87775d8e63c7b5722c23ef95c782f574b847d73Sunny Goyal                    db.execSQL("UPDATE favorites SET rank=cellX+(cellY*?) WHERE "
966c87775d8e63c7b5722c23ef95c782f574b847d73Sunny Goyal                            + "container=? AND cellX IS NOT NULL AND cellY IS NOT NULL;",
96708f7261d11a53ae4b330ad4fa897b8519de3d750Sunny Goyal                            new Object[] {c.getLong(1) + 1, c.getLong(0)});
96808f7261d11a53ae4b330ad4fa897b8519de3d750Sunny Goyal                }
96908f7261d11a53ae4b330ad4fa897b8519de3d750Sunny Goyal
97008f7261d11a53ae4b330ad4fa897b8519de3d750Sunny Goyal                c.close();
97108f7261d11a53ae4b330ad4fa897b8519de3d750Sunny Goyal                db.setTransactionSuccessful();
97208f7261d11a53ae4b330ad4fa897b8519de3d750Sunny Goyal            } catch (SQLException ex) {
97308f7261d11a53ae4b330ad4fa897b8519de3d750Sunny Goyal                // Old version remains, which means we wipe old data
97408f7261d11a53ae4b330ad4fa897b8519de3d750Sunny Goyal                Log.e(TAG, ex.getMessage(), ex);
97508f7261d11a53ae4b330ad4fa897b8519de3d750Sunny Goyal                return false;
97608f7261d11a53ae4b330ad4fa897b8519de3d750Sunny Goyal            } finally {
97708f7261d11a53ae4b330ad4fa897b8519de3d750Sunny Goyal                db.endTransaction();
97808f7261d11a53ae4b330ad4fa897b8519de3d750Sunny Goyal            }
97908f7261d11a53ae4b330ad4fa897b8519de3d750Sunny Goyal            return true;
98008f7261d11a53ae4b330ad4fa897b8519de3d750Sunny Goyal        }
98108f7261d11a53ae4b330ad4fa897b8519de3d750Sunny Goyal
982ed13187a745866483139e2878037e1f8427ce567Kenny Guy        private boolean addProfileColumn(SQLiteDatabase db) {
983b5b55c8012c124182c1bd07bdd4be1e734d972d0Sunny Goyal            return addIntegerColumn(db, Favorites.PROFILE_ID, getDefaultUserSerial());
9845d85c44fd873c740dc191b28424c2ee367d730a2Sunny Goyal        }
9855d85c44fd873c740dc191b28424c2ee367d730a2Sunny Goyal
9865d85c44fd873c740dc191b28424c2ee367d730a2Sunny Goyal        private boolean addIntegerColumn(SQLiteDatabase db, String columnName, long defaultValue) {
987ed13187a745866483139e2878037e1f8427ce567Kenny Guy            db.beginTransaction();
988ed13187a745866483139e2878037e1f8427ce567Kenny Guy            try {
9895d85c44fd873c740dc191b28424c2ee367d730a2Sunny Goyal                db.execSQL("ALTER TABLE favorites ADD COLUMN "
9905d85c44fd873c740dc191b28424c2ee367d730a2Sunny Goyal                        + columnName + " INTEGER NOT NULL DEFAULT " + defaultValue + ";");
991ed13187a745866483139e2878037e1f8427ce567Kenny Guy                db.setTransactionSuccessful();
992ed13187a745866483139e2878037e1f8427ce567Kenny Guy            } catch (SQLException ex) {
993ed13187a745866483139e2878037e1f8427ce567Kenny Guy                Log.e(TAG, ex.getMessage(), ex);
994ed13187a745866483139e2878037e1f8427ce567Kenny Guy                return false;
995ed13187a745866483139e2878037e1f8427ce567Kenny Guy            } finally {
996ed13187a745866483139e2878037e1f8427ce567Kenny Guy                db.endTransaction();
997ed13187a745866483139e2878037e1f8427ce567Kenny Guy            }
998ed13187a745866483139e2878037e1f8427ce567Kenny Guy            return true;
999ed13187a745866483139e2878037e1f8427ce567Kenny Guy        }
1000ed13187a745866483139e2878037e1f8427ce567Kenny Guy
1001a8c760d395e1d2a78522427738302fbca3a72453Michael Jurka        // Generates a new ID to use for an object in your database. This method should be only
1002a8c760d395e1d2a78522427738302fbca3a72453Michael Jurka        // called from the main UI thread. As an exception, we do call it when we call the
1003a8c760d395e1d2a78522427738302fbca3a72453Michael Jurka        // constructor from the worker thread; however, this doesn't extend until after the
1004a8c760d395e1d2a78522427738302fbca3a72453Michael Jurka        // constructor is called, and we only pass a reference to LauncherProvider to LauncherApp
1005a8c760d395e1d2a78522427738302fbca3a72453Michael Jurka        // after that point
10060fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal        @Override
1007dcd297f05a866e07090d6e2af8fb4b15f28cb555Adam Cohen        public long generateNewItemId() {
1008dcd297f05a866e07090d6e2af8fb4b15f28cb555Adam Cohen            if (mMaxItemId < 0) {
1009dcd297f05a866e07090d6e2af8fb4b15f28cb555Adam Cohen                throw new RuntimeException("Error: max item id was not initialized");
1010a8c760d395e1d2a78522427738302fbca3a72453Michael Jurka            }
1011dcd297f05a866e07090d6e2af8fb4b15f28cb555Adam Cohen            mMaxItemId += 1;
1012dcd297f05a866e07090d6e2af8fb4b15f28cb555Adam Cohen            return mMaxItemId;
1013a8c760d395e1d2a78522427738302fbca3a72453Michael Jurka        }
1014a8c760d395e1d2a78522427738302fbca3a72453Michael Jurka
10150fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal        @Override
10160fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal        public long insertAndCheck(SQLiteDatabase db, ContentValues values) {
1017b5b55c8012c124182c1bd07bdd4be1e734d972d0Sunny Goyal            return dbInsertAndCheck(this, db, Favorites.TABLE_NAME, null, values);
10180fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal        }
10190fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal
10205dee7aff5fa8774d37977d12df7ce8986752321dChris Wren        public void checkId(String table, ContentValues values) {
10215dee7aff5fa8774d37977d12df7ce8986752321dChris Wren            long id = values.getAsLong(LauncherSettings.BaseLauncherColumns._ID);
1022b5b55c8012c124182c1bd07bdd4be1e734d972d0Sunny Goyal            if (table == WorkspaceScreens.TABLE_NAME) {
10235dee7aff5fa8774d37977d12df7ce8986752321dChris Wren                mMaxScreenId = Math.max(id, mMaxScreenId);
10245dee7aff5fa8774d37977d12df7ce8986752321dChris Wren            }  else {
10255dee7aff5fa8774d37977d12df7ce8986752321dChris Wren                mMaxItemId = Math.max(id, mMaxItemId);
10265dee7aff5fa8774d37977d12df7ce8986752321dChris Wren            }
10275dee7aff5fa8774d37977d12df7ce8986752321dChris Wren        }
10285dee7aff5fa8774d37977d12df7ce8986752321dChris Wren
1029dcd297f05a866e07090d6e2af8fb4b15f28cb555Adam Cohen        private long initializeMaxItemId(SQLiteDatabase db) {
1030b5b55c8012c124182c1bd07bdd4be1e734d972d0Sunny Goyal            return getMaxId(db, Favorites.TABLE_NAME);
1031dcd297f05a866e07090d6e2af8fb4b15f28cb555Adam Cohen        }
1032dcd297f05a866e07090d6e2af8fb4b15f28cb555Adam Cohen
1033dcd297f05a866e07090d6e2af8fb4b15f28cb555Adam Cohen        // Generates a new ID to use for an workspace screen in your database. This method
1034dcd297f05a866e07090d6e2af8fb4b15f28cb555Adam Cohen        // should be only called from the main UI thread. As an exception, we do call it when we
1035dcd297f05a866e07090d6e2af8fb4b15f28cb555Adam Cohen        // call the constructor from the worker thread; however, this doesn't extend until after the
1036dcd297f05a866e07090d6e2af8fb4b15f28cb555Adam Cohen        // constructor is called, and we only pass a reference to LauncherProvider to LauncherApp
1037dcd297f05a866e07090d6e2af8fb4b15f28cb555Adam Cohen        // after that point
1038dcd297f05a866e07090d6e2af8fb4b15f28cb555Adam Cohen        public long generateNewScreenId() {
1039dcd297f05a866e07090d6e2af8fb4b15f28cb555Adam Cohen            if (mMaxScreenId < 0) {
1040dcd297f05a866e07090d6e2af8fb4b15f28cb555Adam Cohen                throw new RuntimeException("Error: max screen id was not initialized");
1041dcd297f05a866e07090d6e2af8fb4b15f28cb555Adam Cohen            }
1042dcd297f05a866e07090d6e2af8fb4b15f28cb555Adam Cohen            mMaxScreenId += 1;
1043dcd297f05a866e07090d6e2af8fb4b15f28cb555Adam Cohen            return mMaxScreenId;
1044dcd297f05a866e07090d6e2af8fb4b15f28cb555Adam Cohen        }
1045dcd297f05a866e07090d6e2af8fb4b15f28cb555Adam Cohen
1046dcd297f05a866e07090d6e2af8fb4b15f28cb555Adam Cohen        private long initializeMaxScreenId(SQLiteDatabase db) {
1047b5b55c8012c124182c1bd07bdd4be1e734d972d0Sunny Goyal            return getMaxId(db, WorkspaceScreens.TABLE_NAME);
10487ec3bbff8b6364dc609eb06ba85e6f3bb999c67eAdam Cohen        }
10497ec3bbff8b6364dc609eb06ba85e6f3bb999c67eAdam Cohen
1050091440a9cb9d4f42406631004aa484cbb79214caAdam Cohen        @Thunk int loadFavorites(SQLiteDatabase db, AutoInstallsLayout loader) {
105171483f417bb19f524cda41081f5ccac6084dc103Adam Cohen            ArrayList<Long> screenIds = new ArrayList<Long>();
10520fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal            // TODO: Use multiple loaders with fall-back and transaction.
10530fe505bf82a265e51c556d7204976651cde7f55cSunny Goyal            int count = loader.loadLayout(db, screenIds);
105471483f417bb19f524cda41081f5ccac6084dc103Adam Cohen
105571483f417bb19f524cda41081f5ccac6084dc103Adam Cohen            // Add the screens specified by the items above
105671483f417bb19f524cda41081f5ccac6084dc103Adam Cohen            Collections.sort(screenIds);
105771483f417bb19f524cda41081f5ccac6084dc103Adam Cohen            int rank = 0;
105871483f417bb19f524cda41081f5ccac6084dc103Adam Cohen            ContentValues values = new ContentValues();
105971483f417bb19f524cda41081f5ccac6084dc103Adam Cohen            for (Long id : screenIds) {
106071483f417bb19f524cda41081f5ccac6084dc103Adam Cohen                values.clear();
106171483f417bb19f524cda41081f5ccac6084dc103Adam Cohen                values.put(LauncherSettings.WorkspaceScreens._ID, id);
106271483f417bb19f524cda41081f5ccac6084dc103Adam Cohen                values.put(LauncherSettings.WorkspaceScreens.SCREEN_RANK, rank);
1063b5b55c8012c124182c1bd07bdd4be1e734d972d0Sunny Goyal                if (dbInsertAndCheck(this, db, WorkspaceScreens.TABLE_NAME, null, values) < 0) {
106471483f417bb19f524cda41081f5ccac6084dc103Adam Cohen                    throw new RuntimeException("Failed initialize screen table"
106571483f417bb19f524cda41081f5ccac6084dc103Adam Cohen                            + "from default layout");
106671483f417bb19f524cda41081f5ccac6084dc103Adam Cohen                }
106771483f417bb19f524cda41081f5ccac6084dc103Adam Cohen                rank++;
106871483f417bb19f524cda41081f5ccac6084dc103Adam Cohen            }
106971483f417bb19f524cda41081f5ccac6084dc103Adam Cohen
107071483f417bb19f524cda41081f5ccac6084dc103Adam Cohen            // Ensure that the max ids are initialized
107171483f417bb19f524cda41081f5ccac6084dc103Adam Cohen            mMaxItemId = initializeMaxItemId(db);
107271483f417bb19f524cda41081f5ccac6084dc103Adam Cohen            mMaxScreenId = initializeMaxScreenId(db);
10737ec3bbff8b6364dc609eb06ba85e6f3bb999c67eAdam Cohen
107471483f417bb19f524cda41081f5ccac6084dc103Adam Cohen            return count;
107571483f417bb19f524cda41081f5ccac6084dc103Adam Cohen        }
1076b87bd16c8e3f88a9ba78ed00c9f150b38dc39f33Mike Cleron    }
1077c3a804042844dc4733b4bd4b6ac03bf4b2b015b7Daniel Lehmann
1078fe4e4b91dc7db5d373690dd4bf2f3b5f1525b68aSunny Goyal    /**
1079fe4e4b91dc7db5d373690dd4bf2f3b5f1525b68aSunny Goyal     * @return the max _id in the provided table.
1080fe4e4b91dc7db5d373690dd4bf2f3b5f1525b68aSunny Goyal     */
1081091440a9cb9d4f42406631004aa484cbb79214caAdam Cohen    @Thunk static long getMaxId(SQLiteDatabase db, String table) {
1082fe4e4b91dc7db5d373690dd4bf2f3b5f1525b68aSunny Goyal        Cursor c = db.rawQuery("SELECT MAX(_id) FROM " + table, null);
1083fe4e4b91dc7db5d373690dd4bf2f3b5f1525b68aSunny Goyal        // get the result
1084fe4e4b91dc7db5d373690dd4bf2f3b5f1525b68aSunny Goyal        long id = -1;
1085fe4e4b91dc7db5d373690dd4bf2f3b5f1525b68aSunny Goyal        if (c != null && c.moveToNext()) {
1086fe4e4b91dc7db5d373690dd4bf2f3b5f1525b68aSunny Goyal            id = c.getLong(0);
1087fe4e4b91dc7db5d373690dd4bf2f3b5f1525b68aSunny Goyal        }
1088fe4e4b91dc7db5d373690dd4bf2f3b5f1525b68aSunny Goyal        if (c != null) {
1089fe4e4b91dc7db5d373690dd4bf2f3b5f1525b68aSunny Goyal            c.close();
1090fe4e4b91dc7db5d373690dd4bf2f3b5f1525b68aSunny Goyal        }
1091fe4e4b91dc7db5d373690dd4bf2f3b5f1525b68aSunny Goyal
1092fe4e4b91dc7db5d373690dd4bf2f3b5f1525b68aSunny Goyal        if (id == -1) {
1093fe4e4b91dc7db5d373690dd4bf2f3b5f1525b68aSunny Goyal            throw new RuntimeException("Error: could not query max id in " + table);
1094fe4e4b91dc7db5d373690dd4bf2f3b5f1525b68aSunny Goyal        }
1095fe4e4b91dc7db5d373690dd4bf2f3b5f1525b68aSunny Goyal
1096fe4e4b91dc7db5d373690dd4bf2f3b5f1525b68aSunny Goyal        return id;
1097fe4e4b91dc7db5d373690dd4bf2f3b5f1525b68aSunny Goyal    }
1098fe4e4b91dc7db5d373690dd4bf2f3b5f1525b68aSunny Goyal
109931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    static class SqlArguments {
110031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        public final String table;
110131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        public final String where;
110231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        public final String[] args;
110331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
110431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        SqlArguments(Uri url, String where, String[] args) {
110531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            if (url.getPathSegments().size() == 1) {
110631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                this.table = url.getPathSegments().get(0);
110731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                this.where = where;
110831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                this.args = args;
110931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            } else if (url.getPathSegments().size() != 2) {
111031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                throw new IllegalArgumentException("Invalid URI: " + url);
111131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            } else if (!TextUtils.isEmpty(where)) {
111231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                throw new UnsupportedOperationException("WHERE clause not supported: " + url);
111331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            } else {
111431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                this.table = url.getPathSegments().get(0);
1115c3a804042844dc4733b4bd4b6ac03bf4b2b015b7Daniel Lehmann                this.where = "_id=" + ContentUris.parseId(url);
111631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                this.args = null;
111731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            }
111831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        }
111931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project
112031dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        SqlArguments(Uri url) {
112131dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            if (url.getPathSegments().size() == 1) {
112231dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                table = url.getPathSegments().get(0);
112331dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                where = null;
112431dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                args = null;
112531dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            } else {
112631dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project                throw new IllegalArgumentException("Invalid URI: " + url);
112731dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project            }
112831dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project        }
112931dd503c6aa69018e694d91724d46db49ea09327The Android Open Source Project    }
1130b5b55c8012c124182c1bd07bdd4be1e734d972d0Sunny Goyal
1131b5b55c8012c124182c1bd07bdd4be1e734d972d0Sunny Goyal    private static class ChangeListenerWrapper implements Handler.Callback {
1132b5b55c8012c124182c1bd07bdd4be1e734d972d0Sunny Goyal
1133b5b55c8012c124182c1bd07bdd4be1e734d972d0Sunny Goyal        private static final int MSG_LAUNCHER_PROVIDER_CHANGED = 1;
1134b5b55c8012c124182c1bd07bdd4be1e734d972d0Sunny Goyal        private static final int MSG_EXTRACTED_COLORS_CHANGED = 2;
1135b5b55c8012c124182c1bd07bdd4be1e734d972d0Sunny Goyal        private static final int MSG_APP_WIDGET_HOST_RESET = 3;
1136b5b55c8012c124182c1bd07bdd4be1e734d972d0Sunny Goyal
1137b5b55c8012c124182c1bd07bdd4be1e734d972d0Sunny Goyal        private LauncherProviderChangeListener mListener;
1138b5b55c8012c124182c1bd07bdd4be1e734d972d0Sunny Goyal
1139b5b55c8012c124182c1bd07bdd4be1e734d972d0Sunny Goyal        @Override
1140b5b55c8012c124182c1bd07bdd4be1e734d972d0Sunny Goyal        public boolean handleMessage(Message msg) {
1141b5b55c8012c124182c1bd07bdd4be1e734d972d0Sunny Goyal            if (mListener != null) {
1142b5b55c8012c124182c1bd07bdd4be1e734d972d0Sunny Goyal                switch (msg.what) {
1143b5b55c8012c124182c1bd07bdd4be1e734d972d0Sunny Goyal                    case MSG_LAUNCHER_PROVIDER_CHANGED:
1144b5b55c8012c124182c1bd07bdd4be1e734d972d0Sunny Goyal                        mListener.onLauncherProviderChange();
1145b5b55c8012c124182c1bd07bdd4be1e734d972d0Sunny Goyal                        break;
1146b5b55c8012c124182c1bd07bdd4be1e734d972d0Sunny Goyal                    case MSG_EXTRACTED_COLORS_CHANGED:
1147b5b55c8012c124182c1bd07bdd4be1e734d972d0Sunny Goyal                        mListener.onExtractedColorsChanged();
1148b5b55c8012c124182c1bd07bdd4be1e734d972d0Sunny Goyal                        break;
1149b5b55c8012c124182c1bd07bdd4be1e734d972d0Sunny Goyal                    case MSG_APP_WIDGET_HOST_RESET:
1150da4fe1a6244457f144e0a331cada3ada17157809Sunny Goyal                        Context context = (Context) msg.obj;
1151da4fe1a6244457f144e0a331cada3ada17157809Sunny Goyal                        if (context != null) {
1152da4fe1a6244457f144e0a331cada3ada17157809Sunny Goyal                            context.sendBroadcast(new Intent(Launcher.ACTION_APPWIDGET_HOST_RESET)
1153da4fe1a6244457f144e0a331cada3ada17157809Sunny Goyal                                    .setPackage(context.getPackageName()));
1154da4fe1a6244457f144e0a331cada3ada17157809Sunny Goyal                        }
1155b5b55c8012c124182c1bd07bdd4be1e734d972d0Sunny Goyal                        break;
1156b5b55c8012c124182c1bd07bdd4be1e734d972d0Sunny Goyal                }
1157b5b55c8012c124182c1bd07bdd4be1e734d972d0Sunny Goyal            }
1158b5b55c8012c124182c1bd07bdd4be1e734d972d0Sunny Goyal            return true;
1159b5b55c8012c124182c1bd07bdd4be1e734d972d0Sunny Goyal        }
1160b5b55c8012c124182c1bd07bdd4be1e734d972d0Sunny Goyal    }
116172960975d8367e7ce789d8a5b6f0763fb0ec78bfAdam Cohen}
1162