• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2016 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.launcher3.provider;
18 
19 import android.content.ContentValues;
20 import android.content.Context;
21 import android.database.Cursor;
22 import android.database.DatabaseUtils;
23 import android.database.sqlite.SQLiteDatabase;
24 import android.util.Log;
25 
26 import com.android.launcher3.LauncherAppState;
27 import com.android.launcher3.LauncherSettings.Favorites;
28 import com.android.launcher3.LauncherSettings.WorkspaceScreens;
29 
30 import java.util.ArrayList;
31 import java.util.Collection;
32 
33 /**
34  * A set of utility methods for Launcher DB used for DB updates and migration.
35  */
36 public class LauncherDbUtils {
37 
38     private static final String TAG = "LauncherDbUtils";
39 
40     /**
41      * Makes the first screen as screen 0 (if screen 0 already exists,
42      * renames it to some other number).
43      * If the first row of screen 0 is non empty, runs a 'lossy' GridMigrationTask to clear
44      * the first row. The items in the first screen are moved and resized but the carry-forward
45      * items are simply deleted.
46      */
prepareScreenZeroToHostQsb(Context context, SQLiteDatabase db)47     public static boolean prepareScreenZeroToHostQsb(Context context, SQLiteDatabase db) {
48         try (SQLiteTransaction t = new SQLiteTransaction(db)) {
49             // Get the existing screens
50             ArrayList<Long> screenIds = getScreenIdsFromCursor(db.query(WorkspaceScreens.TABLE_NAME,
51                     null, null, null, null, null, WorkspaceScreens.SCREEN_RANK));
52 
53             if (screenIds.isEmpty()) {
54                 // No update needed
55                 t.commit();
56                 return true;
57             }
58             if (screenIds.get(0) != 0) {
59                 // First screen is not 0, we need to rename screens
60                 if (screenIds.indexOf(0L) > -1) {
61                     // There is already a screen 0. First rename it to a different screen.
62                     long newScreenId = 1;
63                     while (screenIds.indexOf(newScreenId) > -1) newScreenId++;
64                     renameScreen(db, 0, newScreenId);
65                 }
66 
67                 // Rename the first screen to 0.
68                 renameScreen(db, screenIds.get(0), 0);
69             }
70 
71             // Check if the first row is empty
72             if (DatabaseUtils.queryNumEntries(db, Favorites.TABLE_NAME,
73                     "container = -100 and screen = 0 and cellY = 0") == 0) {
74                 // First row is empty, no need to migrate.
75                 t.commit();
76                 return true;
77             }
78 
79             new LossyScreenMigrationTask(context, LauncherAppState.getIDP(context), db)
80                     .migrateScreen0();
81             t.commit();
82             return true;
83         } catch (Exception e) {
84             Log.e(TAG, "Failed to update workspace size", e);
85             return false;
86         }
87     }
88 
renameScreen(SQLiteDatabase db, long oldScreen, long newScreen)89     private static void renameScreen(SQLiteDatabase db, long oldScreen, long newScreen) {
90         String[] whereParams = new String[] { Long.toString(oldScreen) };
91 
92         ContentValues values = new ContentValues();
93         values.put(WorkspaceScreens._ID, newScreen);
94         db.update(WorkspaceScreens.TABLE_NAME, values, "_id = ?", whereParams);
95 
96         values.clear();
97         values.put(Favorites.SCREEN, newScreen);
98         db.update(Favorites.TABLE_NAME, values, "container = -100 and screen = ?", whereParams);
99     }
100 
101     /**
102      * Parses the cursor containing workspace screens table and returns the list of screen IDs
103      */
getScreenIdsFromCursor(Cursor sc)104     public static ArrayList<Long> getScreenIdsFromCursor(Cursor sc) {
105         try {
106             return iterateCursor(sc,
107                     sc.getColumnIndexOrThrow(WorkspaceScreens._ID),
108                     new ArrayList<Long>());
109         } finally {
110             sc.close();
111         }
112     }
113 
iterateCursor(Cursor c, int columnIndex, T out)114     public static <T extends Collection<Long>> T iterateCursor(Cursor c, int columnIndex, T out) {
115         while (c.moveToNext()) {
116             out.add(c.getLong(columnIndex));
117         }
118         return out;
119     }
120 
121     /**
122      * Utility class to simplify managing sqlite transactions
123      */
124     public static class SQLiteTransaction implements AutoCloseable {
125         private final SQLiteDatabase mDb;
126 
SQLiteTransaction(SQLiteDatabase db)127         public SQLiteTransaction(SQLiteDatabase db) {
128             mDb = db;
129             db.beginTransaction();
130         }
131 
commit()132         public void commit() {
133             mDb.setTransactionSuccessful();
134         }
135 
136         @Override
close()137         public void close() {
138             mDb.endTransaction();
139         }
140     }
141 }
142