Merge "Import translations. DO NOT MERGE" into ub-launcher3-master
diff --git a/AndroidManifest-common.xml b/AndroidManifest-common.xml
index 23ea195..86ae73e 100644
--- a/AndroidManifest-common.xml
+++ b/AndroidManifest-common.xml
@@ -76,6 +76,14 @@
android:process=":wallpaper_chooser">
</service>
+ <service android:name="com.android.launcher3.notification.NotificationListener"
+ android:enabled="@bool/notification_badging_enabled"
+ android:permission="android.permission.BIND_NOTIFICATION_LISTENER_SERVICE">
+ <intent-filter>
+ <action android:name="android.service.notification.NotificationListenerService" />
+ </intent-filter>
+ </service>
+
<meta-data android:name="android.nfc.disable_beam_default"
android:value="true" />
diff --git a/res/values-v25/bools.xml b/res/values-v25/bools.xml
new file mode 100644
index 0000000..6f7ecd9
--- /dev/null
+++ b/res/values-v25/bools.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/* Copyright 2017, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<resources>
+ <!-- TODO(b/35443111): remove from v25 and keep in v26 -->
+ <bool name="notification_badging_enabled">true</bool>
+</resources>
\ No newline at end of file
diff --git a/res/values-v26/bools.xml b/res/values-v26/bools.xml
new file mode 100644
index 0000000..1093f78
--- /dev/null
+++ b/res/values-v26/bools.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/* Copyright 2017, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<resources>
+ <bool name="notification_badging_enabled">true</bool>
+</resources>
\ No newline at end of file
diff --git a/res/values/bools.xml b/res/values/bools.xml
new file mode 100644
index 0000000..cc4a7ba
--- /dev/null
+++ b/res/values/bools.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/* Copyright 2017, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<resources>
+ <bool name="notification_badging_enabled">false</bool>
+</resources>
\ No newline at end of file
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index 70f5b32..f6bb3ad 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -89,6 +89,7 @@
<dimen name="widget_preview_key_shadow_distance">1dp</dimen>
<dimen name="widget_preview_corner_radius">2dp</dimen>
<dimen name="widget_preview_cell_divider_width">0.5dp</dimen>
+ <dimen name="widget_preview_shortcut_padding">8dp</dimen>
<dimen name="widget_section_height">56dp</dimen>
<dimen name="widget_section_icon_size">40dp</dimen>
diff --git a/src/com/android/launcher3/CellLayout.java b/src/com/android/launcher3/CellLayout.java
index a7c2026..e0fcbf0 100644
--- a/src/com/android/launcher3/CellLayout.java
+++ b/src/com/android/launcher3/CellLayout.java
@@ -2146,7 +2146,7 @@
info.spanY = lp.cellVSpan;
if (requiresDbUpdate) {
- LauncherModel.modifyItemInDatabase(getContext(), info, container, screenId,
+ mLauncher.getModelWriter().modifyItemInDatabase(info, container, screenId,
info.cellX, info.cellY, info.spanX, info.spanY);
}
}
diff --git a/src/com/android/launcher3/FolderInfo.java b/src/com/android/launcher3/FolderInfo.java
index 5fff2e7..2c69d0a 100644
--- a/src/com/android/launcher3/FolderInfo.java
+++ b/src/com/android/launcher3/FolderInfo.java
@@ -16,9 +16,9 @@
package com.android.launcher3;
-import android.content.Context;
import android.os.Process;
+import com.android.launcher3.model.ModelWriter;
import com.android.launcher3.util.ContentWriter;
import java.util.ArrayList;
@@ -128,17 +128,17 @@
/**
* @param option flag to set or clear
* @param isEnabled whether to set or clear the flag
- * @param context if not null, save changes to the db.
+ * @param writer if not null, save changes to the db.
*/
- public void setOption(int option, boolean isEnabled, Context context) {
+ public void setOption(int option, boolean isEnabled, ModelWriter writer) {
int oldOptions = options;
if (isEnabled) {
options |= option;
} else {
options &= ~option;
}
- if (context != null && oldOptions != options) {
- LauncherModel.updateItemInDatabase(context, this);
+ if (writer != null && oldOptions != options) {
+ writer.updateItemInDatabase(this);
}
}
}
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index a160c41..14b9c82 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -85,6 +85,7 @@
import com.android.launcher3.allapps.AllAppsTransitionController;
import com.android.launcher3.allapps.DefaultAppSearchController;
import com.android.launcher3.anim.AnimationLayerSet;
+import com.android.launcher3.model.ModelWriter;
import com.android.launcher3.notification.NotificationListener;
import com.android.launcher3.popup.PopupDataProvider;
import com.android.launcher3.compat.AppWidgetManagerCompat;
@@ -254,6 +255,7 @@
private ViewOnDrawExecutor mPendingExecutor;
private LauncherModel mModel;
+ private ModelWriter mModelWriter;
private IconCache mIconCache;
private ExtractedColors mExtractedColors;
private LauncherAccessibilityDelegate mAccessibilityDelegate;
@@ -371,6 +373,7 @@
mSharedPrefs = Utilities.getPrefs(this);
mIsSafeModeEnabled = getPackageManager().isSafeMode();
mModel = app.setLauncher(this);
+ mModelWriter = mModel.getWriter(mDeviceProfile.isVerticalBarLayout());
mIconCache = app.getIconCache();
mAccessibilityDelegate = new LauncherAccessibilityDelegate(this);
@@ -1498,7 +1501,7 @@
return;
}
- LauncherModel.addItemToDatabase(this, info, container, screenId, cellXY[0], cellXY[1]);
+ getModelWriter().addItemToDatabase(info, container, screenId, cellXY[0], cellXY[1]);
mWorkspace.addInScreen(view, info);
}
@@ -1526,7 +1529,7 @@
launcherInfo.minSpanY = itemInfo.minSpanY;
launcherInfo.user = appWidgetInfo.getUser();
- LauncherModel.addItemToDatabase(this, launcherInfo,
+ getModelWriter().addItemToDatabase(launcherInfo,
itemInfo.container, itemInfo.screenId, itemInfo.cellX, itemInfo.cellY);
if (hostView == null) {
@@ -1686,6 +1689,10 @@
return mModel;
}
+ public ModelWriter getModelWriter() {
+ return mModelWriter;
+ }
+
public SharedPreferences getSharedPrefs() {
return mSharedPrefs;
}
@@ -2127,8 +2134,7 @@
folderInfo.title = getText(R.string.folder_name);
// Update the model
- LauncherModel.addItemToDatabase(Launcher.this, folderInfo, container, screenId,
- cellX, cellY);
+ getModelWriter().addItemToDatabase(folderInfo, container, screenId, cellX, cellY);
// Create the view
FolderIcon newFolder = FolderIcon.fromXml(R.layout.folder_icon, this, layout, folderInfo);
@@ -2156,7 +2162,7 @@
mWorkspace.removeWorkspaceItem(v);
}
if (deleteFromDb) {
- LauncherModel.deleteItemFromDatabase(this, itemInfo);
+ getModelWriter().deleteItemFromDatabase(itemInfo);
}
} else if (itemInfo instanceof FolderInfo) {
final FolderInfo folderInfo = (FolderInfo) itemInfo;
@@ -2165,7 +2171,7 @@
}
mWorkspace.removeWorkspaceItem(v);
if (deleteFromDb) {
- LauncherModel.deleteFolderAndContentsFromDatabase(this, folderInfo);
+ getModelWriter().deleteFolderAndContentsFromDatabase(folderInfo);
}
} else if (itemInfo instanceof LauncherAppWidgetInfo) {
final LauncherAppWidgetInfo widgetInfo = (LauncherAppWidgetInfo) itemInfo;
@@ -2194,7 +2200,7 @@
}
}.executeOnExecutor(Utilities.THREAD_POOL_EXECUTOR);
}
- LauncherModel.deleteItemFromDatabase(this, widgetInfo);
+ getModelWriter().deleteItemFromDatabase(widgetInfo);
}
@Override
@@ -3373,7 +3379,7 @@
throw (new RuntimeException(desc));
} else {
Log.d(TAG, desc);
- LauncherModel.deleteItemFromDatabase(this, item);
+ getModelWriter().deleteItemFromDatabase(item);
continue;
}
}
@@ -3470,7 +3476,7 @@
+ " belongs to component " + item.providerName
+ ", as the provider is null");
}
- LauncherModel.deleteItemFromDatabase(this, item);
+ getModelWriter().deleteItemFromDatabase(item);
return;
}
@@ -3517,14 +3523,14 @@
: LauncherAppWidgetInfo.FLAG_UI_NOT_READY;
}
- LauncherModel.updateItemInDatabase(this, item);
+ getModelWriter().updateItemInDatabase(item);
}
} else if (item.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_UI_NOT_READY)
&& (appWidgetInfo.configure == null)) {
// The widget was marked as UI not ready, but there is no configure activity to
// update the UI.
item.restoreStatus = LauncherAppWidgetInfo.RESTORE_COMPLETED;
- LauncherModel.updateItemInDatabase(this, item);
+ getModelWriter().updateItemInDatabase(item);
}
}
@@ -3574,7 +3580,7 @@
info.restoreStatus = finalRestoreFlag;
mWorkspace.reinflateWidgetsIfNecessary();
- LauncherModel.updateItemInDatabase(this, info);
+ getModelWriter().updateItemInDatabase(info);
return info;
}
diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java
index 9ad8433..34d576d 100644
--- a/src/com/android/launcher3/LauncherModel.java
+++ b/src/com/android/launcher3/LauncherModel.java
@@ -57,6 +57,7 @@
import com.android.launcher3.model.ExtendedModelTask;
import com.android.launcher3.model.GridSizeMigrationTask;
import com.android.launcher3.model.LoaderCursor;
+import com.android.launcher3.model.ModelWriter;
import com.android.launcher3.model.PackageInstallStateChangedTask;
import com.android.launcher3.model.PackageItemInfo;
import com.android.launcher3.model.PackageUpdatedTask;
@@ -266,19 +267,8 @@
enqueueModelUpdateTask(new AddWorkspaceItemsTask(appsProvider));
}
- /**
- * Adds an item to the DB if it was not created previously, or move it to a new
- * <container, screen, cellX, cellY>
- */
- public static void addOrMoveItemInDatabase(Context context, ItemInfo item, long container,
- long screenId, int cellX, int cellY) {
- if (item.container == ItemInfo.NO_ID) {
- // From all apps
- addItemToDatabase(context, item, container, screenId, cellX, cellY);
- } else {
- // From somewhere else
- moveItemInDatabase(context, item, container, screenId, cellX, cellY);
- }
+ public ModelWriter getWriter(boolean hasVerticalHotseat) {
+ return new ModelWriter(mApp.getContext(), sBgDataModel, hasVerticalHotseat);
}
static void checkItemInfoLocked(
@@ -332,281 +322,6 @@
runOnWorkerThread(r);
}
- static void updateItemInDatabaseHelper(Context context, final ContentWriter writer,
- final ItemInfo item, final String callingFunction) {
- final long itemId = item.id;
- final Uri uri = LauncherSettings.Favorites.getContentUri(itemId);
- final ContentResolver cr = context.getContentResolver();
-
- final StackTraceElement[] stackTrace = new Throwable().getStackTrace();
- final Context appContext = context.getApplicationContext();
- Runnable r = new Runnable() {
- public void run() {
- cr.update(uri, writer.getValues(appContext), null, null);
- updateItemArrays(item, itemId, stackTrace);
- }
- };
- runOnWorkerThread(r);
- }
-
- static void updateItemsInDatabaseHelper(Context context, final ArrayList<ContentValues> valuesList,
- final ArrayList<ItemInfo> items, final String callingFunction) {
- final ContentResolver cr = context.getContentResolver();
-
- final StackTraceElement[] stackTrace = new Throwable().getStackTrace();
- Runnable r = new Runnable() {
- public void run() {
- ArrayList<ContentProviderOperation> ops =
- new ArrayList<ContentProviderOperation>();
- int count = items.size();
- for (int i = 0; i < count; i++) {
- ItemInfo item = items.get(i);
- final long itemId = item.id;
- final Uri uri = LauncherSettings.Favorites.getContentUri(itemId);
- ContentValues values = valuesList.get(i);
-
- ops.add(ContentProviderOperation.newUpdate(uri).withValues(values).build());
- updateItemArrays(item, itemId, stackTrace);
-
- }
- try {
- cr.applyBatch(LauncherProvider.AUTHORITY, ops);
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
- };
- runOnWorkerThread(r);
- }
-
- static void updateItemArrays(ItemInfo item, long itemId, StackTraceElement[] stackTrace) {
- // Lock on mBgLock *after* the db operation
- synchronized (sBgDataModel) {
- checkItemInfoLocked(itemId, item, stackTrace);
-
- if (item.container != LauncherSettings.Favorites.CONTAINER_DESKTOP &&
- item.container != LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
- // Item is in a folder, make sure this folder exists
- if (!sBgDataModel.folders.containsKey(item.container)) {
- // An items container is being set to a that of an item which is not in
- // the list of Folders.
- String msg = "item: " + item + " container being set to: " +
- item.container + ", not in the list of folders";
- Log.e(TAG, msg);
- }
- }
-
- // Items are added/removed from the corresponding FolderInfo elsewhere, such
- // as in Workspace.onDrop. Here, we just add/remove them from the list of items
- // that are on the desktop, as appropriate
- ItemInfo modelItem = sBgDataModel.itemsIdMap.get(itemId);
- if (modelItem != null &&
- (modelItem.container == LauncherSettings.Favorites.CONTAINER_DESKTOP ||
- modelItem.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT)) {
- switch (modelItem.itemType) {
- case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
- case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
- case LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT:
- case LauncherSettings.Favorites.ITEM_TYPE_FOLDER:
- if (!sBgDataModel.workspaceItems.contains(modelItem)) {
- sBgDataModel.workspaceItems.add(modelItem);
- }
- break;
- default:
- break;
- }
- } else {
- sBgDataModel.workspaceItems.remove(modelItem);
- }
- }
- }
-
- /**
- * Move an item in the DB to a new <container, screen, cellX, cellY>
- */
- public static void moveItemInDatabase(Context context, final ItemInfo item, final long container,
- final long screenId, final int cellX, final int cellY) {
- item.container = container;
- item.cellX = cellX;
- item.cellY = cellY;
-
- // We store hotseat items in canonical form which is this orientation invariant position
- // in the hotseat
- if (context instanceof Launcher && screenId < 0 &&
- container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
- item.screenId = Launcher.getLauncher(context).getHotseat()
- .getOrderInHotseat(cellX, cellY);
- } else {
- item.screenId = screenId;
- }
-
- final ContentWriter writer = new ContentWriter(context)
- .put(LauncherSettings.Favorites.CONTAINER, item.container)
- .put(LauncherSettings.Favorites.CELLX, item.cellX)
- .put(LauncherSettings.Favorites.CELLY, item.cellY)
- .put(LauncherSettings.Favorites.RANK, item.rank)
- .put(LauncherSettings.Favorites.SCREEN, item.screenId);
-
- updateItemInDatabaseHelper(context, writer, item, "moveItemInDatabase");
- }
-
- /**
- * Move items in the DB to a new <container, screen, cellX, cellY>. We assume that the
- * cellX, cellY have already been updated on the ItemInfos.
- */
- public static void moveItemsInDatabase(Context context, final ArrayList<ItemInfo> items,
- final long container, final int screen) {
-
- ArrayList<ContentValues> contentValues = new ArrayList<ContentValues>();
- int count = items.size();
-
- for (int i = 0; i < count; i++) {
- ItemInfo item = items.get(i);
- item.container = container;
-
- // We store hotseat items in canonical form which is this orientation invariant position
- // in the hotseat
- if (context instanceof Launcher && screen < 0 &&
- container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
- item.screenId = Launcher.getLauncher(context).getHotseat().getOrderInHotseat(item.cellX,
- item.cellY);
- } else {
- item.screenId = screen;
- }
-
- final ContentValues values = new ContentValues();
- values.put(LauncherSettings.Favorites.CONTAINER, item.container);
- values.put(LauncherSettings.Favorites.CELLX, item.cellX);
- values.put(LauncherSettings.Favorites.CELLY, item.cellY);
- values.put(LauncherSettings.Favorites.RANK, item.rank);
- values.put(LauncherSettings.Favorites.SCREEN, item.screenId);
-
- contentValues.add(values);
- }
- updateItemsInDatabaseHelper(context, contentValues, items, "moveItemInDatabase");
- }
-
- /**
- * Move and/or resize item in the DB to a new <container, screen, cellX, cellY, spanX, spanY>
- */
- static void modifyItemInDatabase(Context context, final ItemInfo item, final long container,
- final long screenId, final int cellX, final int cellY, final int spanX, final int spanY) {
- item.container = container;
- item.cellX = cellX;
- item.cellY = cellY;
- item.spanX = spanX;
- item.spanY = spanY;
-
- // We store hotseat items in canonical form which is this orientation invariant position
- // in the hotseat
- if (context instanceof Launcher && screenId < 0 &&
- container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
- item.screenId = Launcher.getLauncher(context).getHotseat()
- .getOrderInHotseat(cellX, cellY);
- } else {
- item.screenId = screenId;
- }
-
- final ContentWriter writer = new ContentWriter(context)
- .put(LauncherSettings.Favorites.CONTAINER, item.container)
- .put(LauncherSettings.Favorites.CELLX, item.cellX)
- .put(LauncherSettings.Favorites.CELLY, item.cellY)
- .put(LauncherSettings.Favorites.RANK, item.rank)
- .put(LauncherSettings.Favorites.SPANX, item.spanX)
- .put(LauncherSettings.Favorites.SPANY, item.spanY)
- .put(LauncherSettings.Favorites.SCREEN, item.screenId);
-
- updateItemInDatabaseHelper(context, writer, item, "modifyItemInDatabase");
- }
-
- /**
- * Update an item to the database in a specified container.
- */
- public static void updateItemInDatabase(Context context, final ItemInfo item) {
- ContentWriter writer = new ContentWriter(context);
- item.onAddToDatabase(writer);
- updateItemInDatabaseHelper(context, writer, item, "updateItemInDatabase");
- }
-
- /**
- * Add an item to the database in a specified container. Sets the container, screen, cellX and
- * cellY fields of the item. Also assigns an ID to the item.
- */
- public static void addItemToDatabase(Context context, final ItemInfo item, final long container,
- final long screenId, final int cellX, final int cellY) {
- item.container = container;
- item.cellX = cellX;
- item.cellY = cellY;
- // We store hotseat items in canonical form which is this orientation invariant position
- // in the hotseat
- if (context instanceof Launcher && screenId < 0 &&
- container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
- item.screenId = Launcher.getLauncher(context).getHotseat()
- .getOrderInHotseat(cellX, cellY);
- } else {
- item.screenId = screenId;
- }
-
- final ContentWriter writer = new ContentWriter(context);
- final ContentResolver cr = context.getContentResolver();
- item.onAddToDatabase(writer);
-
- item.id = LauncherSettings.Settings.call(cr, LauncherSettings.Settings.METHOD_NEW_ITEM_ID)
- .getLong(LauncherSettings.Settings.EXTRA_VALUE);
-
- writer.put(LauncherSettings.Favorites._ID, item.id);
-
- final StackTraceElement[] stackTrace = new Throwable().getStackTrace();
- final Context appContext = context.getApplicationContext();
- Runnable r = new Runnable() {
- public void run() {
- cr.insert(LauncherSettings.Favorites.CONTENT_URI, writer.getValues(appContext));
-
- synchronized (sBgDataModel) {
- checkItemInfoLocked(item.id, item, stackTrace);
- sBgDataModel.addItem(appContext, item, true);
- }
- }
- };
- runOnWorkerThread(r);
- }
-
- /**
- * Removes the specified item from the database
- */
- public static void deleteItemFromDatabase(Context context, final ItemInfo item) {
- ArrayList<ItemInfo> items = new ArrayList<>();
- items.add(item);
- deleteItemsFromDatabase(context, items);
- }
-
- /**
- * Removes all the items from the database matching {@param matcher}.
- */
- public static void deleteItemsFromDatabase(Context context, ItemInfoMatcher matcher) {
- deleteItemsFromDatabase(context, matcher.filterItemInfos(sBgDataModel.itemsIdMap));
- }
-
- /**
- * Removes the specified items from the database
- */
- public static void deleteItemsFromDatabase(Context context,
- final Iterable<? extends ItemInfo> items) {
- final ContentResolver cr = context.getContentResolver();
- final Context appContext = context.getApplicationContext();
- Runnable r = new Runnable() {
- public void run() {
- for (ItemInfo item : items) {
- final Uri uri = LauncherSettings.Favorites.getContentUri(item.id);
- cr.delete(uri, null, null);
-
- sBgDataModel.removeItem(appContext, item);
- }
- }
- };
- runOnWorkerThread(r);
- }
-
/**
* Update the order of the workspace screens in the database. The array list contains
* a list of screen ids in the order that they should appear.
@@ -656,27 +371,6 @@
}
/**
- * Remove the specified folder and all its contents from the database.
- */
- public static void deleteFolderAndContentsFromDatabase(Context context, final FolderInfo info) {
- final ContentResolver cr = context.getContentResolver();
- final Context appContext = context.getApplicationContext();
-
- Runnable r = new Runnable() {
- public void run() {
- cr.delete(LauncherSettings.Favorites.CONTENT_URI,
- LauncherSettings.Favorites.CONTAINER + "=" + info.id, null);
- sBgDataModel.removeItem(appContext, info.contents);
- info.contents.clear();
-
- cr.delete(LauncherSettings.Favorites.getContentUri(info.id), null, null);
- sBgDataModel.removeItem(appContext, info);
- }
- };
- runOnWorkerThread(r);
- }
-
- /**
* Set this as the current Launcher activity object for the loader.
*/
public void initialize(Callbacks callbacks) {
@@ -2193,6 +1887,11 @@
}
});
}
+
+ public ModelWriter getModelWriter() {
+ // Updates from model task, do not deal with icon position in hotseat.
+ return mModel.getWriter(false /* hasVerticalHotseat */);
+ }
}
public void updateAndBindShortcutInfo(final ShortcutInfo si, final ShortcutInfoCompat info) {
diff --git a/src/com/android/launcher3/WidgetPreviewLoader.java b/src/com/android/launcher3/WidgetPreviewLoader.java
index 3512210..689cc9b 100644
--- a/src/com/android/launcher3/WidgetPreviewLoader.java
+++ b/src/com/android/launcher3/WidgetPreviewLoader.java
@@ -14,22 +14,24 @@
import android.graphics.Bitmap.Config;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
-import android.graphics.ColorMatrix;
-import android.graphics.ColorMatrixColorFilter;
+import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
+import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.drawable.Drawable;
import android.os.AsyncTask;
import android.os.Handler;
import android.os.UserHandle;
+import android.support.v4.graphics.ColorUtils;
import android.util.Log;
import android.util.LongSparseArray;
import com.android.launcher3.compat.AppWidgetManagerCompat;
import com.android.launcher3.compat.ShortcutConfigActivityInfo;
import com.android.launcher3.compat.UserManagerCompat;
+import com.android.launcher3.graphics.LauncherIcons;
import com.android.launcher3.graphics.ShadowGenerator;
import com.android.launcher3.model.WidgetItem;
import com.android.launcher3.util.ComponentKey;
@@ -366,30 +368,13 @@
drawable.setBounds(x, 0, x + previewWidth, previewHeight);
drawable.draw(c);
} else {
- Resources res = mContext.getResources();
- float shadowBlur = res.getDimension(R.dimen.widget_preview_shadow_blur);
- float keyShadowDistance = res.getDimension(R.dimen.widget_preview_key_shadow_distance);
- float corner = res.getDimension(R.dimen.widget_preview_corner_radius);
-
- RectF boxRect = new RectF(shadowBlur, shadowBlur,
- previewWidth - shadowBlur, previewHeight - shadowBlur - keyShadowDistance);
-
final Paint p = new Paint(Paint.ANTI_ALIAS_FLAG);
- p.setColor(0xFFFFFFFF);
-
- // Key shadow
- p.setShadowLayer(shadowBlur, 0, keyShadowDistance,
- ShadowGenerator.KEY_SHADOW_ALPHA << 24);
- c.drawRoundRect(boxRect, corner, corner, p);
-
- // Ambient shadow
- p.setShadowLayer(shadowBlur, 0, 0, ShadowGenerator.AMBIENT_SHADOW_ALPHA << 24);
- c.drawRoundRect(boxRect, corner, corner, p);
+ RectF boxRect = drawBoxWithShadow(c, p, previewWidth, previewHeight);
// Draw horizontal and vertical lines to represent individual columns.
- p.clearShadowLayer();
p.setStyle(Paint.Style.STROKE);
- p.setStrokeWidth(res.getDimension(R.dimen.widget_preview_cell_divider_width));
+ p.setStrokeWidth(mContext.getResources()
+ .getDimension(R.dimen.widget_preview_cell_divider_width));
p.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
float t = boxRect.left;
@@ -426,47 +411,63 @@
return preview;
}
+ private RectF drawBoxWithShadow(Canvas c, Paint p, int width, int height) {
+ Resources res = mContext.getResources();
+ float shadowBlur = res.getDimension(R.dimen.widget_preview_shadow_blur);
+ float keyShadowDistance = res.getDimension(R.dimen.widget_preview_key_shadow_distance);
+ float corner = res.getDimension(R.dimen.widget_preview_corner_radius);
+
+ RectF bounds = new RectF(shadowBlur, shadowBlur,
+ width - shadowBlur, height - shadowBlur - keyShadowDistance);
+ p.setColor(Color.WHITE);
+
+ // Key shadow
+ p.setShadowLayer(shadowBlur, 0, keyShadowDistance,
+ ShadowGenerator.KEY_SHADOW_ALPHA << 24);
+ c.drawRoundRect(bounds, corner, corner, p);
+
+ // Ambient shadow
+ p.setShadowLayer(shadowBlur, 0, 0,
+ ColorUtils.setAlphaComponent(Color.BLACK, ShadowGenerator.AMBIENT_SHADOW_ALPHA));
+ c.drawRoundRect(bounds, corner, corner, p);
+
+ p.clearShadowLayer();
+ return bounds;
+ }
+
private Bitmap generateShortcutPreview(BaseActivity launcher, ShortcutConfigActivityInfo info,
int maxWidth, int maxHeight, Bitmap preview) {
+ int iconSize = launcher.getDeviceProfile().iconSizePx;
+ int padding = launcher.getResources()
+ .getDimensionPixelSize(R.dimen.widget_preview_shortcut_padding);
+
+ int size = iconSize + 2 * padding;
+ if (maxHeight < size || maxWidth < size) {
+ throw new RuntimeException("Max size is too small for preview");
+ }
final Canvas c = new Canvas();
- if (preview == null) {
- preview = Bitmap.createBitmap(maxWidth, maxHeight, Config.ARGB_8888);
+ if (preview == null || preview.getWidth() < size || preview.getHeight() < size) {
+ preview = Bitmap.createBitmap(size, size, Config.ARGB_8888);
c.setBitmap(preview);
- } else if (preview.getWidth() != maxWidth || preview.getHeight() != maxHeight) {
- throw new RuntimeException("Improperly sized bitmap passed as argument");
} else {
+ if (preview.getWidth() > size || preview.getHeight() > size) {
+ preview.reconfigure(size, size, preview.getConfig());
+ }
+
// Reusing bitmap. Clear it.
c.setBitmap(preview);
c.drawColor(0, PorterDuff.Mode.CLEAR);
}
+ Paint p = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG);
+ RectF boxRect = drawBoxWithShadow(c, p, size, size);
- Drawable icon = mutateOnMainThread(info.getFullResIcon(mIconCache));
- icon.setFilterBitmap(true);
+ Bitmap icon = LauncherIcons.createScaledBitmapWithoutShadow(
+ mutateOnMainThread(info.getFullResIcon(mIconCache)), mContext);
+ Rect src = new Rect(0, 0, icon.getWidth(), icon.getHeight());
- // Draw a desaturated/scaled version of the icon in the background as a watermark
- ColorMatrix colorMatrix = new ColorMatrix();
- colorMatrix.setSaturation(0);
- icon.setColorFilter(new ColorMatrixColorFilter(colorMatrix));
- icon.setAlpha((int) (255 * 0.06f));
-
- Resources res = mContext.getResources();
- int paddingTop = res.getDimensionPixelOffset(R.dimen.shortcut_preview_padding_top);
- int paddingLeft = res.getDimensionPixelOffset(R.dimen.shortcut_preview_padding_left);
- int paddingRight = res.getDimensionPixelOffset(R.dimen.shortcut_preview_padding_right);
- int scaledIconWidth = (maxWidth - paddingLeft - paddingRight);
- icon.setBounds(paddingLeft, paddingTop,
- paddingLeft + scaledIconWidth, paddingTop + scaledIconWidth);
- icon.draw(c);
-
- // Draw the final icon at top left corner.
- // TODO: use top right for RTL
- int appIconSize = launcher.getDeviceProfile().iconSizePx;
-
- icon.setAlpha(255);
- icon.setColorFilter(null);
- icon.setBounds(0, 0, appIconSize, appIconSize);
- icon.draw(c);
-
+ boxRect.set(0, 0, iconSize, iconSize);
+ boxRect.offset(padding, padding);
+ c.drawBitmap(icon, src, boxRect, p);
c.setBitmap(null);
return preview;
}
@@ -664,7 +665,6 @@
private static final class WidgetCacheKey extends ComponentKey {
- // TODO: remove dependency on size
@Thunk final String size;
public WidgetCacheKey(ComponentName componentName, UserHandle user, String size) {
diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java
index 3aa8825..56aa69e 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -2654,8 +2654,8 @@
}
}
- LauncherModel.modifyItemInDatabase(mLauncher, info, container, screenId, lp.cellX,
- lp.cellY, item.spanX, item.spanY);
+ mLauncher.getModelWriter().modifyItemInDatabase(info, container, screenId,
+ lp.cellX, lp.cellY, item.spanX, item.spanY);
} else {
if (!returnToOriginalCellToPreventShuffling) {
onNoCellFound(dropTargetLayout);
@@ -3387,7 +3387,7 @@
}
// Add the item to DB before adding to screen ensures that the container and other
// values of the info is properly updated.
- LauncherModel.addOrMoveItemInDatabase(mLauncher, info, container, screenId,
+ mLauncher.getModelWriter().addOrMoveItemInDatabase(info, container, screenId,
mTargetCell[0], mTargetCell[1]);
addInScreen(view, container, screenId, mTargetCell[0], mTargetCell[1],
@@ -4025,7 +4025,7 @@
HashSet<String> packages = new HashSet<>(1);
packages.add(packageName);
ItemInfoMatcher matcher = ItemInfoMatcher.ofPackages(packages, user);
- LauncherModel.deleteItemsFromDatabase(mLauncher, matcher);
+ mLauncher.getModelWriter().deleteItemsFromDatabase(matcher);
removeItemsByMatcher(matcher);
}
diff --git a/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java b/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java
index b77493b..a476650 100644
--- a/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java
+++ b/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java
@@ -168,7 +168,7 @@
public void run() {
if (item instanceof AppInfo) {
ShortcutInfo info = ((AppInfo) item).makeShortcut();
- LauncherModel.addItemToDatabase(mLauncher, info,
+ mLauncher.getModelWriter().addItemToDatabase(info,
LauncherSettings.Favorites.CONTAINER_DESKTOP,
screenId, coordinates[0], coordinates[1]);
@@ -194,7 +194,7 @@
final int[] coordinates = new int[2];
final long screenId = findSpaceOnWorkspace(item, coordinates);
- LauncherModel.moveItemInDatabase(mLauncher, info,
+ mLauncher.getModelWriter().moveItemInDatabase(info,
LauncherSettings.Favorites.CONTAINER_DESKTOP,
screenId, coordinates[0], coordinates[1]);
@@ -304,7 +304,7 @@
((LauncherAppWidgetHostView) host).updateAppWidgetSize(null,
sizeRange.left, sizeRange.top, sizeRange.right, sizeRange.bottom);
host.requestLayout();
- LauncherModel.updateItemInDatabase(mLauncher, info);
+ mLauncher.getModelWriter().updateItemInDatabase(info);
announceConfirmation(mLauncher.getString(R.string.widget_resized, info.spanX, info.spanY));
}
diff --git a/src/com/android/launcher3/accessibility/ShortcutMenuAccessibilityDelegate.java b/src/com/android/launcher3/accessibility/ShortcutMenuAccessibilityDelegate.java
index f7ca703..b784fe7 100644
--- a/src/com/android/launcher3/accessibility/ShortcutMenuAccessibilityDelegate.java
+++ b/src/com/android/launcher3/accessibility/ShortcutMenuAccessibilityDelegate.java
@@ -59,7 +59,7 @@
Runnable onComplete = new Runnable() {
@Override
public void run() {
- LauncherModel.addItemToDatabase(mLauncher, info,
+ mLauncher.getModelWriter().addItemToDatabase(info,
LauncherSettings.Favorites.CONTAINER_DESKTOP,
screenId, coordinates[0], coordinates[1]);
ArrayList<ItemInfo> itemList = new ArrayList<>();
diff --git a/src/com/android/launcher3/folder/Folder.java b/src/com/android/launcher3/folder/Folder.java
index eacea4a..3d28f22 100644
--- a/src/com/android/launcher3/folder/Folder.java
+++ b/src/com/android/launcher3/folder/Folder.java
@@ -355,7 +355,7 @@
// gets saved.
String newTitle = mFolderName.getText().toString();
mInfo.setTitle(newTitle);
- LauncherModel.updateItemInDatabase(mLauncher, mInfo);
+ mLauncher.getModelWriter().updateItemInDatabase(mInfo);
Utilities.sendCustomAccessibilityEvent(
this, AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED,
@@ -445,7 +445,7 @@
// TODO: Remove this, as with multi-page folders, there will never be any overflow
for (ShortcutInfo item: overflow) {
mInfo.remove(item, false);
- LauncherModel.deleteItemFromDatabase(mLauncher, item);
+ mLauncher.getModelWriter().deleteItemFromDatabase(item);
}
DragLayer.LayoutParams lp = (DragLayer.LayoutParams) getLayoutParams();
@@ -647,7 +647,8 @@
mPageIndicator.playEntryAnimation();
if (updateAnimationFlag) {
- mInfo.setOption(FolderInfo.FLAG_MULTI_PAGE_ANIMATION, true, mLauncher);
+ mInfo.setOption(FolderInfo.FLAG_MULTI_PAGE_ANIMATION, true,
+ mLauncher.getModelWriter());
}
}
});
@@ -973,7 +974,8 @@
// been refreshed yet.
if (getItemCount() <= mContent.itemsPerPage()) {
// Show the animation, next time something is added to the folder.
- mInfo.setOption(FolderInfo.FLAG_MULTI_PAGE_ANIMATION, false, mLauncher);
+ mInfo.setOption(FolderInfo.FLAG_MULTI_PAGE_ANIMATION, false,
+ mLauncher.getModelWriter());
}
if (!isFlingToDelete) {
@@ -1022,7 +1024,7 @@
items.add(info);
}
- LauncherModel.moveItemsInDatabase(mLauncher, items, mInfo.id, 0);
+ mLauncher.getModelWriter().moveItemsInDatabase(items, mInfo.id, 0);
}
public void notifyDrop() {
@@ -1189,8 +1191,8 @@
mInfo.screenId);
ShortcutInfo finalItem = mInfo.contents.remove(0);
newIcon = mLauncher.createShortcut(cellLayout, finalItem);
- LauncherModel.addOrMoveItemInDatabase(mLauncher, finalItem, mInfo.container,
- mInfo.screenId, mInfo.cellX, mInfo.cellY);
+ mLauncher.getModelWriter().addOrMoveItemInDatabase(finalItem,
+ mInfo.container, mInfo.screenId, mInfo.cellX, mInfo.cellY);
}
// Remove the folder
@@ -1301,8 +1303,8 @@
currentDragView = mContent.createAndAddViewForRank(si, mEmptyCellRank);
// Actually move the item in the database if it was an external drag. Call this
// before creating the view, so that ShortcutInfo is updated appropriately.
- LauncherModel.addOrMoveItemInDatabase(
- mLauncher, si, mInfo.id, 0, si.cellX, si.cellY);
+ mLauncher.getModelWriter().addOrMoveItemInDatabase(
+ si, mInfo.id, 0, si.cellX, si.cellY);
// We only need to update the locations if it doesn't get handled in #onDropCompleted.
if (d.dragSource != this) {
@@ -1342,7 +1344,7 @@
if (mContent.getPageCount() > 1) {
// The animation has already been shown while opening the folder.
- mInfo.setOption(FolderInfo.FLAG_MULTI_PAGE_ANIMATION, true, mLauncher);
+ mInfo.setOption(FolderInfo.FLAG_MULTI_PAGE_ANIMATION, true, mLauncher.getModelWriter());
}
if (d.stateAnnouncer != null) {
@@ -1366,8 +1368,8 @@
public void onAdd(ShortcutInfo item) {
mContent.createAndAddViewForRank(item, mContent.allocateRankForNewItem());
mItemsInvalidated = true;
- LauncherModel.addOrMoveItemInDatabase(
- mLauncher, item, mInfo.id, 0, item.cellX, item.cellY);
+ mLauncher.getModelWriter().addOrMoveItemInDatabase(
+ item, mInfo.id, 0, item.cellX, item.cellY);
}
public void onRemove(ShortcutInfo item) {
diff --git a/src/com/android/launcher3/folder/FolderPagedView.java b/src/com/android/launcher3/folder/FolderPagedView.java
index 4a2a735..9dfdfdc 100644
--- a/src/com/android/launcher3/folder/FolderPagedView.java
+++ b/src/com/android/launcher3/folder/FolderPagedView.java
@@ -336,7 +336,7 @@
info.cellY = newY;
info.rank = rank;
if (saveChanges) {
- LauncherModel.addOrMoveItemInDatabase(getContext(), info,
+ mFolder.mLauncher.getModelWriter().addOrMoveItemInDatabase(info,
mFolder.mInfo.id, 0, info.cellX, info.cellY);
}
}
diff --git a/src/com/android/launcher3/model/AddWorkspaceItemsTask.java b/src/com/android/launcher3/model/AddWorkspaceItemsTask.java
index c97b3b5..9696054 100644
--- a/src/com/android/launcher3/model/AddWorkspaceItemsTask.java
+++ b/src/com/android/launcher3/model/AddWorkspaceItemsTask.java
@@ -95,7 +95,9 @@
}
// Add the shortcut to the db
- addItemToDatabase(context, itemInfo, screenId, cordinates);
+ getModelWriter().addItemToDatabase(itemInfo,
+ LauncherSettings.Favorites.CONTAINER_DESKTOP, screenId,
+ cordinates[0], cordinates[1]);
// Save the ShortcutInfo for binding in the workspace
addedItemsFinal.add(itemInfo);
@@ -129,11 +131,6 @@
}
}
- protected void addItemToDatabase(Context context, ItemInfo item, long screenId, int[] pos) {
- LauncherModel.addItemToDatabase(context, item,
- LauncherSettings.Favorites.CONTAINER_DESKTOP, screenId, pos[0], pos[1]);
- }
-
protected void updateScreens(Context context, ArrayList<Long> workspaceScreens) {
LauncherModel.updateWorkspaceScreenOrder(context, workspaceScreens);
}
diff --git a/src/com/android/launcher3/model/ModelWriter.java b/src/com/android/launcher3/model/ModelWriter.java
new file mode 100644
index 0000000..4931dca
--- /dev/null
+++ b/src/com/android/launcher3/model/ModelWriter.java
@@ -0,0 +1,374 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.model;
+
+import android.content.ContentProviderOperation;
+import android.content.ContentResolver;
+import android.content.ContentValues;
+import android.content.Context;
+import android.net.Uri;
+import android.util.Log;
+
+import com.android.launcher3.FolderInfo;
+import com.android.launcher3.ItemInfo;
+import com.android.launcher3.LauncherAppState;
+import com.android.launcher3.LauncherModel;
+import com.android.launcher3.LauncherProvider;
+import com.android.launcher3.LauncherSettings;
+import com.android.launcher3.LauncherSettings.Favorites;
+import com.android.launcher3.LauncherSettings.Settings;
+import com.android.launcher3.ShortcutInfo;
+import com.android.launcher3.util.ContentWriter;
+import com.android.launcher3.util.ItemInfoMatcher;
+import com.android.launcher3.util.LooperExecuter;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.concurrent.Executor;
+
+/**
+ * Class for handling model updates.
+ */
+public class ModelWriter {
+
+ private static final String TAG = "ModelWriter";
+
+ private final Context mContext;
+ private final BgDataModel mBgDataModel;
+ private final Executor mWorkerExecutor;
+ private final boolean mHasVerticalHotseat;
+
+ public ModelWriter(Context context, BgDataModel dataModel, boolean hasVerticalHotseat) {
+ mContext = context;
+ mBgDataModel = dataModel;
+ mWorkerExecutor = new LooperExecuter(LauncherModel.getWorkerLooper());
+ mHasVerticalHotseat = hasVerticalHotseat;
+ }
+
+ private void updateItemInfoProps(
+ ItemInfo item, long container, long screenId, int cellX, int cellY) {
+ item.container = container;
+ item.cellX = cellX;
+ item.cellY = cellY;
+ // We store hotseat items in canonical form which is this orientation invariant position
+ // in the hotseat
+ if (container == Favorites.CONTAINER_HOTSEAT) {
+ item.screenId = mHasVerticalHotseat
+ ? LauncherAppState.getIDP(mContext).numHotseatIcons - cellY - 1 : cellX;
+ } else {
+ item.screenId = screenId;
+ }
+ }
+
+ /**
+ * Adds an item to the DB if it was not created previously, or move it to a new
+ * <container, screen, cellX, cellY>
+ */
+ public void addOrMoveItemInDatabase(ItemInfo item,
+ long container, long screenId, int cellX, int cellY) {
+ if (item.container == ItemInfo.NO_ID) {
+ // From all apps
+ addItemToDatabase(item, container, screenId, cellX, cellY);
+ } else {
+ // From somewhere else
+ moveItemInDatabase(item, container, screenId, cellX, cellY);
+ }
+ }
+
+ private void checkItemInfoLocked(long itemId, ItemInfo item, StackTraceElement[] stackTrace) {
+ ItemInfo modelItem = mBgDataModel.itemsIdMap.get(itemId);
+ if (modelItem != null && item != modelItem) {
+ // check all the data is consistent
+ if (modelItem instanceof ShortcutInfo && item instanceof ShortcutInfo) {
+ ShortcutInfo modelShortcut = (ShortcutInfo) modelItem;
+ ShortcutInfo shortcut = (ShortcutInfo) item;
+ if (modelShortcut.title.toString().equals(shortcut.title.toString()) &&
+ modelShortcut.intent.filterEquals(shortcut.intent) &&
+ modelShortcut.id == shortcut.id &&
+ modelShortcut.itemType == shortcut.itemType &&
+ modelShortcut.container == shortcut.container &&
+ modelShortcut.screenId == shortcut.screenId &&
+ modelShortcut.cellX == shortcut.cellX &&
+ modelShortcut.cellY == shortcut.cellY &&
+ modelShortcut.spanX == shortcut.spanX &&
+ modelShortcut.spanY == shortcut.spanY) {
+ // For all intents and purposes, this is the same object
+ return;
+ }
+ }
+
+ // the modelItem needs to match up perfectly with item if our model is
+ // to be consistent with the database-- for now, just require
+ // modelItem == item or the equality check above
+ String msg = "item: " + ((item != null) ? item.toString() : "null") +
+ "modelItem: " +
+ ((modelItem != null) ? modelItem.toString() : "null") +
+ "Error: ItemInfo passed to checkItemInfo doesn't match original";
+ RuntimeException e = new RuntimeException(msg);
+ if (stackTrace != null) {
+ e.setStackTrace(stackTrace);
+ }
+ throw e;
+ }
+ }
+
+ /**
+ * Move an item in the DB to a new <container, screen, cellX, cellY>
+ */
+ public void moveItemInDatabase(final ItemInfo item,
+ long container, long screenId, int cellX, int cellY) {
+ updateItemInfoProps(item, container, screenId, cellX, cellY);
+
+ final ContentWriter writer = new ContentWriter(mContext)
+ .put(Favorites.CONTAINER, item.container)
+ .put(Favorites.CELLX, item.cellX)
+ .put(Favorites.CELLY, item.cellY)
+ .put(Favorites.RANK, item.rank)
+ .put(Favorites.SCREEN, item.screenId);
+
+ mWorkerExecutor.execute(new UpdateItemRunnable(item, writer));
+ }
+
+ /**
+ * Move items in the DB to a new <container, screen, cellX, cellY>. We assume that the
+ * cellX, cellY have already been updated on the ItemInfos.
+ */
+ public void moveItemsInDatabase(final ArrayList<ItemInfo> items, long container, int screen) {
+ ArrayList<ContentValues> contentValues = new ArrayList<>();
+ int count = items.size();
+
+ for (int i = 0; i < count; i++) {
+ ItemInfo item = items.get(i);
+ updateItemInfoProps(item, container, screen, item.cellX, item.cellY);
+
+ final ContentValues values = new ContentValues();
+ values.put(Favorites.CONTAINER, item.container);
+ values.put(Favorites.CELLX, item.cellX);
+ values.put(Favorites.CELLY, item.cellY);
+ values.put(Favorites.RANK, item.rank);
+ values.put(Favorites.SCREEN, item.screenId);
+
+ contentValues.add(values);
+ }
+ mWorkerExecutor.execute(new UpdateItemsRunnable(items, contentValues));
+ }
+
+ /**
+ * Move and/or resize item in the DB to a new <container, screen, cellX, cellY, spanX, spanY>
+ */
+ public void modifyItemInDatabase(final ItemInfo item,
+ long container, long screenId, int cellX, int cellY, int spanX, int spanY) {
+ updateItemInfoProps(item, container, screenId, cellX, cellY);
+ item.spanX = spanX;
+ item.spanY = spanY;
+
+ final ContentWriter writer = new ContentWriter(mContext)
+ .put(Favorites.CONTAINER, item.container)
+ .put(Favorites.CELLX, item.cellX)
+ .put(Favorites.CELLY, item.cellY)
+ .put(Favorites.RANK, item.rank)
+ .put(Favorites.SPANX, item.spanX)
+ .put(Favorites.SPANY, item.spanY)
+ .put(Favorites.SCREEN, item.screenId);
+
+ mWorkerExecutor.execute(new UpdateItemRunnable(item, writer));
+ }
+
+ /**
+ * Update an item to the database in a specified container.
+ */
+ public void updateItemInDatabase(ItemInfo item) {
+ ContentWriter writer = new ContentWriter(mContext);
+ item.onAddToDatabase(writer);
+ mWorkerExecutor.execute(new UpdateItemRunnable(item, writer));
+ }
+
+ /**
+ * Add an item to the database in a specified container. Sets the container, screen, cellX and
+ * cellY fields of the item. Also assigns an ID to the item.
+ */
+ public void addItemToDatabase(final ItemInfo item,
+ long container, long screenId, int cellX, int cellY) {
+ updateItemInfoProps(item, container, screenId, cellX, cellY);
+
+ final ContentWriter writer = new ContentWriter(mContext);
+ final ContentResolver cr = mContext.getContentResolver();
+ item.onAddToDatabase(writer);
+
+ item.id = Settings.call(cr, Settings.METHOD_NEW_ITEM_ID).getLong(Settings.EXTRA_VALUE);
+ writer.put(Favorites._ID, item.id);
+
+ final StackTraceElement[] stackTrace = new Throwable().getStackTrace();
+ mWorkerExecutor.execute(new Runnable() {
+ public void run() {
+ cr.insert(Favorites.CONTENT_URI, writer.getValues(mContext));
+
+ synchronized (mBgDataModel) {
+ checkItemInfoLocked(item.id, item, stackTrace);
+ mBgDataModel.addItem(mContext, item, true);
+ }
+ }
+ });
+ }
+
+ /**
+ * Removes the specified item from the database
+ */
+ public void deleteItemFromDatabase(ItemInfo item) {
+ deleteItemsFromDatabase(Arrays.asList(item));
+ }
+
+ /**
+ * Removes all the items from the database matching {@param matcher}.
+ */
+ public void deleteItemsFromDatabase(ItemInfoMatcher matcher) {
+ deleteItemsFromDatabase(matcher.filterItemInfos(mBgDataModel.itemsIdMap));
+ }
+
+ /**
+ * Removes the specified items from the database
+ */
+ public void deleteItemsFromDatabase(final Iterable<? extends ItemInfo> items) {
+ mWorkerExecutor.execute(new Runnable() {
+ public void run() {
+ for (ItemInfo item : items) {
+ final Uri uri = Favorites.getContentUri(item.id);
+ mContext.getContentResolver().delete(uri, null, null);
+
+ mBgDataModel.removeItem(mContext, item);
+ }
+ }
+ });
+ }
+
+ /**
+ * Remove the specified folder and all its contents from the database.
+ */
+ public void deleteFolderAndContentsFromDatabase(final FolderInfo info) {
+ mWorkerExecutor.execute(new Runnable() {
+ public void run() {
+ ContentResolver cr = mContext.getContentResolver();
+ cr.delete(LauncherSettings.Favorites.CONTENT_URI,
+ LauncherSettings.Favorites.CONTAINER + "=" + info.id, null);
+ mBgDataModel.removeItem(mContext, info.contents);
+ info.contents.clear();
+
+ cr.delete(LauncherSettings.Favorites.getContentUri(info.id), null, null);
+ mBgDataModel.removeItem(mContext, info);
+ }
+ });
+ }
+
+ private class UpdateItemRunnable extends UpdateItemBaseRunnable {
+ private final ItemInfo mItem;
+ private final ContentWriter mWriter;
+ private final long mItemId;
+
+ UpdateItemRunnable(ItemInfo item, ContentWriter writer) {
+ mItem = item;
+ mWriter = writer;
+ mItemId = item.id;
+ }
+
+ @Override
+ public void run() {
+ Uri uri = Favorites.getContentUri(mItemId);
+ mContext.getContentResolver().update(uri, mWriter.getValues(mContext), null, null);
+ updateItemArrays(mItem, mItemId);
+ }
+ }
+
+ private class UpdateItemsRunnable extends UpdateItemBaseRunnable {
+ private final ArrayList<ContentValues> mValues;
+ private final ArrayList<ItemInfo> mItems;
+
+ UpdateItemsRunnable(ArrayList<ItemInfo> items, ArrayList<ContentValues> values) {
+ mValues = values;
+ mItems = items;
+ }
+
+ @Override
+ public void run() {
+ ArrayList<ContentProviderOperation> ops = new ArrayList<>();
+ int count = mItems.size();
+ for (int i = 0; i < count; i++) {
+ ItemInfo item = mItems.get(i);
+ final long itemId = item.id;
+ final Uri uri = Favorites.getContentUri(itemId);
+ ContentValues values = mValues.get(i);
+
+ ops.add(ContentProviderOperation.newUpdate(uri).withValues(values).build());
+ updateItemArrays(item, itemId);
+ }
+ try {
+ mContext.getContentResolver().applyBatch(LauncherProvider.AUTHORITY, ops);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+ }
+
+ private abstract class UpdateItemBaseRunnable implements Runnable {
+ private final StackTraceElement[] mStackTrace;
+
+ UpdateItemBaseRunnable() {
+ mStackTrace = new Throwable().getStackTrace();
+ }
+
+ protected void updateItemArrays(ItemInfo item, long itemId) {
+ // Lock on mBgLock *after* the db operation
+ synchronized (mBgDataModel) {
+ checkItemInfoLocked(itemId, item, mStackTrace);
+
+ if (item.container != Favorites.CONTAINER_DESKTOP &&
+ item.container != Favorites.CONTAINER_HOTSEAT) {
+ // Item is in a folder, make sure this folder exists
+ if (!mBgDataModel.folders.containsKey(item.container)) {
+ // An items container is being set to a that of an item which is not in
+ // the list of Folders.
+ String msg = "item: " + item + " container being set to: " +
+ item.container + ", not in the list of folders";
+ Log.e(TAG, msg);
+ }
+ }
+
+ // Items are added/removed from the corresponding FolderInfo elsewhere, such
+ // as in Workspace.onDrop. Here, we just add/remove them from the list of items
+ // that are on the desktop, as appropriate
+ ItemInfo modelItem = mBgDataModel.itemsIdMap.get(itemId);
+ if (modelItem != null &&
+ (modelItem.container == Favorites.CONTAINER_DESKTOP ||
+ modelItem.container == Favorites.CONTAINER_HOTSEAT)) {
+ switch (modelItem.itemType) {
+ case Favorites.ITEM_TYPE_APPLICATION:
+ case Favorites.ITEM_TYPE_SHORTCUT:
+ case Favorites.ITEM_TYPE_DEEP_SHORTCUT:
+ case Favorites.ITEM_TYPE_FOLDER:
+ if (!mBgDataModel.workspaceItems.contains(modelItem)) {
+ mBgDataModel.workspaceItems.add(modelItem);
+ }
+ break;
+ default:
+ break;
+ }
+ } else {
+ mBgDataModel.workspaceItems.remove(modelItem);
+ }
+ }
+ }
+ }
+}
diff --git a/src/com/android/launcher3/model/PackageUpdatedTask.java b/src/com/android/launcher3/model/PackageUpdatedTask.java
index 211b979..ee7186a 100644
--- a/src/com/android/launcher3/model/PackageUpdatedTask.java
+++ b/src/com/android/launcher3/model/PackageUpdatedTask.java
@@ -270,7 +270,7 @@
updatedShortcuts.add(si);
}
if (infoUpdated) {
- LauncherModel.updateItemInDatabase(context, si);
+ getModelWriter().updateItemInDatabase(si);
}
} else if (info instanceof LauncherAppWidgetInfo && mOp == OP_ADD) {
LauncherAppWidgetInfo widgetInfo = (LauncherAppWidgetInfo) info;
@@ -287,7 +287,7 @@
widgetInfo.restoreStatus |= LauncherAppWidgetInfo.FLAG_UI_NOT_READY;
widgets.add(widgetInfo);
- LauncherModel.updateItemInDatabase(context, widgetInfo);
+ getModelWriter().updateItemInDatabase(widgetInfo);
}
}
}
@@ -295,7 +295,7 @@
bindUpdatedShortcuts(updatedShortcuts, removedShortcuts, mUser);
if (!removedShortcuts.isEmpty()) {
- LauncherModel.deleteItemsFromDatabase(context, removedShortcuts);
+ getModelWriter().deleteItemsFromDatabase(removedShortcuts);
}
if (!widgets.isEmpty()) {
@@ -332,10 +332,10 @@
}
if (!removedPackages.isEmpty() || !removedComponents.isEmpty()) {
- LauncherModel.deleteItemsFromDatabase(
- context, ItemInfoMatcher.ofPackages(removedPackages, mUser));
- LauncherModel.deleteItemsFromDatabase(
- context, ItemInfoMatcher.ofComponents(removedComponents, mUser));
+ getModelWriter().deleteItemsFromDatabase(
+ ItemInfoMatcher.ofPackages(removedPackages, mUser));
+ getModelWriter().deleteItemsFromDatabase(
+ ItemInfoMatcher.ofComponents(removedComponents, mUser));
// Remove any queued items from the install queue
InstallShortcutReceiver.removeFromInstallQueue(context, removedPackages, mUser);
diff --git a/src/com/android/launcher3/model/ShortcutsChangedTask.java b/src/com/android/launcher3/model/ShortcutsChangedTask.java
index ba7112f..d8a429c 100644
--- a/src/com/android/launcher3/model/ShortcutsChangedTask.java
+++ b/src/com/android/launcher3/model/ShortcutsChangedTask.java
@@ -104,7 +104,7 @@
bindUpdatedShortcuts(updatedShortcutInfos, removedShortcutInfos, mUser);
if (!removedShortcutInfos.isEmpty()) {
- LauncherModel.deleteItemsFromDatabase(context, removedShortcutInfos);
+ getModelWriter().deleteItemsFromDatabase(removedShortcutInfos);
}
if (mUpdateIdMap) {
diff --git a/src/com/android/launcher3/model/UserLockStateChangedTask.java b/src/com/android/launcher3/model/UserLockStateChangedTask.java
index 25f2f9d..363f1ee 100644
--- a/src/com/android/launcher3/model/UserLockStateChangedTask.java
+++ b/src/com/android/launcher3/model/UserLockStateChangedTask.java
@@ -95,7 +95,7 @@
}
bindUpdatedShortcuts(updatedShortcutInfos, deletedShortcutInfos, mUser);
if (!deletedShortcutInfos.isEmpty()) {
- LauncherModel.deleteItemsFromDatabase(context, deletedShortcutInfos);
+ getModelWriter().deleteItemsFromDatabase(deletedShortcutInfos);
}
// Remove shortcut id map for that user
diff --git a/src/com/android/launcher3/notification/NotificationListener.java b/src/com/android/launcher3/notification/NotificationListener.java
index 206bb31..5c16176 100644
--- a/src/com/android/launcher3/notification/NotificationListener.java
+++ b/src/com/android/launcher3/notification/NotificationListener.java
@@ -80,9 +80,9 @@
switch (message.what) {
case MSG_NOTIFICATION_POSTED:
if (sNotificationsChangedListener != null) {
- Pair<PackageUserKey, String> pair
- = (Pair<PackageUserKey, String>) message.obj;
- sNotificationsChangedListener.onNotificationPosted(pair.first, pair.second);
+ NotificationPostedMsg msg = (NotificationPostedMsg) message.obj;
+ sNotificationsChangedListener.onNotificationPosted(msg.packageUserKey,
+ msg.notificationKey, msg.shouldBeFilteredOut);
}
break;
case MSG_NOTIFICATION_REMOVED:
@@ -149,23 +149,32 @@
@Override
public void onNotificationPosted(final StatusBarNotification sbn) {
super.onNotificationPosted(sbn);
- if (!shouldBeFilteredOut(sbn.getNotification())) {
- Pair<PackageUserKey, String> packageUserKeyAndNotificationKey
- = new Pair<>(PackageUserKey.fromNotification(sbn), sbn.getKey());
- mWorkerHandler.obtainMessage(MSG_NOTIFICATION_POSTED, packageUserKeyAndNotificationKey)
- .sendToTarget();
+ mWorkerHandler.obtainMessage(MSG_NOTIFICATION_POSTED, new NotificationPostedMsg(sbn))
+ .sendToTarget();
+ }
+
+ /**
+ * An object containing data to send to MSG_NOTIFICATION_POSTED targets.
+ */
+ private class NotificationPostedMsg {
+ PackageUserKey packageUserKey;
+ String notificationKey;
+ boolean shouldBeFilteredOut;
+
+ NotificationPostedMsg(StatusBarNotification sbn) {
+ packageUserKey = PackageUserKey.fromNotification(sbn);
+ notificationKey = sbn.getKey();
+ shouldBeFilteredOut = shouldBeFilteredOut(sbn.getNotification());
}
}
@Override
public void onNotificationRemoved(final StatusBarNotification sbn) {
super.onNotificationRemoved(sbn);
- if (!shouldBeFilteredOut(sbn.getNotification())) {
- Pair<PackageUserKey, String> packageUserKeyAndNotificationKey
- = new Pair<>(PackageUserKey.fromNotification(sbn), sbn.getKey());
- mWorkerHandler.obtainMessage(MSG_NOTIFICATION_REMOVED, packageUserKeyAndNotificationKey)
- .sendToTarget();
- }
+ Pair<PackageUserKey, String> packageUserKeyAndNotificationKey
+ = new Pair<>(PackageUserKey.fromNotification(sbn), sbn.getKey());
+ mWorkerHandler.obtainMessage(MSG_NOTIFICATION_REMOVED, packageUserKeyAndNotificationKey)
+ .sendToTarget();
}
/** This makes a potentially expensive binder call and should be run on a background thread. */
@@ -206,7 +215,8 @@
}
public interface NotificationsChangedListener {
- void onNotificationPosted(PackageUserKey postedPackageUserKey, String notificationKey);
+ void onNotificationPosted(PackageUserKey postedPackageUserKey, String notificationKey,
+ boolean shouldBeFilteredOut);
void onNotificationRemoved(PackageUserKey removedPackageUserKey, String notificationKey);
void onNotificationFullRefresh(List<StatusBarNotification> activeNotifications);
}
diff --git a/src/com/android/launcher3/popup/PopupDataProvider.java b/src/com/android/launcher3/popup/PopupDataProvider.java
index c754fda..e314b64 100644
--- a/src/com/android/launcher3/popup/PopupDataProvider.java
+++ b/src/com/android/launcher3/popup/PopupDataProvider.java
@@ -58,19 +58,26 @@
}
@Override
- public void onNotificationPosted(PackageUserKey postedPackageUserKey, String notificationKey) {
+ public void onNotificationPosted(PackageUserKey postedPackageUserKey, String notificationKey,
+ boolean shouldBeFilteredOut) {
BadgeInfo badgeInfo = mPackageUserToBadgeInfos.get(postedPackageUserKey);
- boolean notificationWasAdded; // As opposed to updated.
+ boolean notificationWasAddedOrRemoved; // As opposed to updated.
if (badgeInfo == null) {
- BadgeInfo newBadgeInfo = new BadgeInfo(postedPackageUserKey);
- newBadgeInfo.addNotificationKeyIfNotExists(notificationKey);
- mPackageUserToBadgeInfos.put(postedPackageUserKey, newBadgeInfo);
- notificationWasAdded = true;
+ if (!shouldBeFilteredOut) {
+ BadgeInfo newBadgeInfo = new BadgeInfo(postedPackageUserKey);
+ newBadgeInfo.addNotificationKeyIfNotExists(notificationKey);
+ mPackageUserToBadgeInfos.put(postedPackageUserKey, newBadgeInfo);
+ notificationWasAddedOrRemoved = true;
+ } else {
+ notificationWasAddedOrRemoved = false;
+ }
} else {
- notificationWasAdded = badgeInfo.addNotificationKeyIfNotExists(notificationKey);
+ notificationWasAddedOrRemoved = shouldBeFilteredOut
+ ? badgeInfo.removeNotificationKey(notificationKey)
+ : badgeInfo.addNotificationKeyIfNotExists(notificationKey);
}
updateLauncherIconBadges(Utilities.singletonHashSet(postedPackageUserKey),
- notificationWasAdded);
+ notificationWasAddedOrRemoved);
}
@Override
diff --git a/src/com/android/launcher3/util/ManagedProfileHeuristic.java b/src/com/android/launcher3/util/ManagedProfileHeuristic.java
index af61554..577d19f 100644
--- a/src/com/android/launcher3/util/ManagedProfileHeuristic.java
+++ b/src/com/android/launcher3/util/ManagedProfileHeuristic.java
@@ -203,7 +203,7 @@
long workFolderId, int startingRank, ArrayList<ShortcutInfo> workFolderApps) {
for (ItemInfo info : workFolderApps) {
info.rank = startingRank++;
- LauncherModel.addItemToDatabase(mContext, info, workFolderId, 0, 0, 0);
+ mModel.getWriter(false).addItemToDatabase(info, workFolderId, 0, 0, 0);
}
}
diff --git a/src/com/android/launcher3/widget/PendingItemDragHelper.java b/src/com/android/launcher3/widget/PendingItemDragHelper.java
index c723f9e..6f4c286 100644
--- a/src/com/android/launcher3/widget/PendingItemDragHelper.java
+++ b/src/com/android/launcher3/widget/PendingItemDragHelper.java
@@ -29,6 +29,7 @@
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.PendingAddItemInfo;
+import com.android.launcher3.R;
import com.android.launcher3.Workspace;
import com.android.launcher3.dragndrop.DragOptions;
import com.android.launcher3.graphics.DragPreviewProvider;
@@ -112,6 +113,11 @@
DeviceProfile dp = launcher.getDeviceProfile();
int iconSize = dp.iconSizePx;
+ int padding = launcher.getResources()
+ .getDimensionPixelSize(R.dimen.widget_preview_shortcut_padding);
+ previewBounds.left += padding;
+ previewBounds.top += padding;
+
dragRegion = new Rect();
dragRegion.left = (size[0] - iconSize) / 2;
dragRegion.right = dragRegion.left + iconSize;
diff --git a/src/com/android/launcher3/widget/WidgetImageView.java b/src/com/android/launcher3/widget/WidgetImageView.java
index 1211c08..df2bcff 100644
--- a/src/com/android/launcher3/widget/WidgetImageView.java
+++ b/src/com/android/launcher3/widget/WidgetImageView.java
@@ -89,16 +89,25 @@
}
private void updateDstRectF() {
- if (mBitmap.getWidth() > getWidth()) {
- float scale = ((float) getWidth()) / mBitmap.getWidth();
- mDstRectF.set(0, 0, getWidth(), scale * mBitmap.getHeight());
+ float myWidth = getWidth();
+ float myHeight = getHeight();
+ float bitmapWidth = mBitmap.getWidth();
+
+ final float scale = bitmapWidth > myWidth ? myWidth / bitmapWidth : 1;
+ float scaledWidth = bitmapWidth * scale;
+ float scaledHeight = mBitmap.getHeight() * scale;
+
+ mDstRectF.left = (myWidth - scaledWidth) / 2;
+ mDstRectF.right = (myWidth + scaledWidth) / 2;
+
+ if (scaledHeight > myHeight) {
+ mDstRectF.top = 0;
+ mDstRectF.bottom = scaledHeight;
} else {
- mDstRectF.set(
- (getWidth() - mBitmap.getWidth()) * 0.5f,
- 0,
- (getWidth() + mBitmap.getWidth()) * 0.5f,
- mBitmap.getHeight());
+ mDstRectF.top = (myHeight - scaledHeight) / 2;
+ mDstRectF.bottom = (myHeight + scaledHeight) / 2;
}
+
if (mBadge != null) {
Rect bounds = mBadge.getBounds();
int left = Utilities.boundToRange(
diff --git a/tests/src/com/android/launcher3/model/AddWorkspaceItemsTaskTest.java b/tests/src/com/android/launcher3/model/AddWorkspaceItemsTaskTest.java
index 2071389..d0ba907 100644
--- a/tests/src/com/android/launcher3/model/AddWorkspaceItemsTaskTest.java
+++ b/tests/src/com/android/launcher3/model/AddWorkspaceItemsTaskTest.java
@@ -53,14 +53,6 @@
return new AddWorkspaceItemsTask(Provider.of(Arrays.asList(items))) {
@Override
- protected void addItemToDatabase(Context context, ItemInfo item,
- long screenId, int[] pos) {
- item.screenId = screenId;
- item.cellX = pos[0];
- item.cellY = pos[1];
- }
-
- @Override
protected void updateScreens(Context context, ArrayList<Long> workspaceScreens) { }
};
}
diff --git a/tests/src/com/android/launcher3/model/BaseModelUpdateTaskTestCase.java b/tests/src/com/android/launcher3/model/BaseModelUpdateTaskTestCase.java
index 9b7fc6e..b9944db 100644
--- a/tests/src/com/android/launcher3/model/BaseModelUpdateTaskTestCase.java
+++ b/tests/src/com/android/launcher3/model/BaseModelUpdateTaskTestCase.java
@@ -37,6 +37,7 @@
import java.util.HashMap;
import java.util.List;
+import static org.mockito.Matchers.anyBoolean;
import static org.mockito.Mockito.atLeast;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
@@ -54,6 +55,8 @@
public InvariantDeviceProfile idp;
public LauncherAppState appState;
+ public LauncherModel model;
+ public ModelWriter modelWriter;
public MyIconCache iconCache;
public BgDataModel bgDataModel;
@@ -70,6 +73,11 @@
callbacks = mock(Callbacks.class);
appState = mock(LauncherAppState.class);
+ model = mock(LauncherModel.class);
+ modelWriter = mock(ModelWriter.class);
+ when(appState.getModel()).thenReturn(model);
+ when(model.getWriter(anyBoolean())).thenReturn(modelWriter);
+
myUser = Process.myUserHandle();
bgDataModel = new BgDataModel();