Merge "Preventing overdraw. Drawing background directly at the window, instead of the rootview." into ub-launcher3-burnaby
diff --git a/proguard.flags b/proguard.flags
index 6eb5948..5e2c384 100644
--- a/proguard.flags
+++ b/proguard.flags
@@ -1,19 +1,3 @@
--keep class com.android.launcher3.Launcher {
- public void previousScreen(android.view.View);
- public void nextScreen(android.view.View);
- public void launchHotSeat(android.view.View);
- public void onClickSearchButton(android.view.View);
- public void onClickVoiceButton(android.view.View);
- public void onClickConfigureButton(android.view.View);
- public void onClickAllAppsButton(android.view.View);
- public void dismissFirstRunCling(android.view.View);
- public void dismissMigrationClingCopyApps(android.view.View);
- public void dismissMigrationClingUseDefault(android.view.View);
- public void dismissMigrationWorkspaceCling(android.view.View);
- public void dismissWorkspaceCling(android.view.View);
- public void dismissAllAppsCling(android.view.View);
-}
-
-keep class com.android.launcher3.CellLayout {
public float getBackgroundAlpha();
public void setBackgroundAlpha(float);
@@ -44,8 +28,6 @@
-keep class com.android.launcher3.Workspace {
public float getBackgroundAlpha();
public void setBackgroundAlpha(float);
- public float getChildrenOutlineAlpha();
- public void setChildrenOutlineAlpha(float);
}
-keep class com.android.launcher3.MemoryDumpActivity {
diff --git a/res/drawable-hdpi/ic_launcher_info_active.png b/res/drawable-hdpi/ic_launcher_info_active.png
deleted file mode 100644
index f7a3b68..0000000
--- a/res/drawable-hdpi/ic_launcher_info_active.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi/ic_launcher_remove_active.png b/res/drawable-hdpi/ic_launcher_remove_active.png
deleted file mode 100644
index e53de0d..0000000
--- a/res/drawable-hdpi/ic_launcher_remove_active.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi/ic_launcher_uninstall_active.png b/res/drawable-hdpi/ic_launcher_uninstall_active.png
deleted file mode 100644
index 22b97ee..0000000
--- a/res/drawable-hdpi/ic_launcher_uninstall_active.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi/ic_launcher_info_active.png b/res/drawable-mdpi/ic_launcher_info_active.png
deleted file mode 100644
index ea71272..0000000
--- a/res/drawable-mdpi/ic_launcher_info_active.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi/ic_launcher_remove_active.png b/res/drawable-mdpi/ic_launcher_remove_active.png
deleted file mode 100644
index f36cfdd..0000000
--- a/res/drawable-mdpi/ic_launcher_remove_active.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi/ic_launcher_uninstall_active.png b/res/drawable-mdpi/ic_launcher_uninstall_active.png
deleted file mode 100644
index e4ee911..0000000
--- a/res/drawable-mdpi/ic_launcher_uninstall_active.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xhdpi/ic_launcher_info_active.png b/res/drawable-xhdpi/ic_launcher_info_active.png
deleted file mode 100644
index b438f9e..0000000
--- a/res/drawable-xhdpi/ic_launcher_info_active.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xhdpi/ic_launcher_remove_active.png b/res/drawable-xhdpi/ic_launcher_remove_active.png
deleted file mode 100644
index 14ac79d..0000000
--- a/res/drawable-xhdpi/ic_launcher_remove_active.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xhdpi/ic_launcher_uninstall_active.png b/res/drawable-xhdpi/ic_launcher_uninstall_active.png
deleted file mode 100644
index 2c19b32..0000000
--- a/res/drawable-xhdpi/ic_launcher_uninstall_active.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_launcher_info_active.png b/res/drawable-xxhdpi/ic_launcher_info_active.png
deleted file mode 100644
index d354dd3..0000000
--- a/res/drawable-xxhdpi/ic_launcher_info_active.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_launcher_remove_active.png b/res/drawable-xxhdpi/ic_launcher_remove_active.png
deleted file mode 100644
index 9df4404..0000000
--- a/res/drawable-xxhdpi/ic_launcher_remove_active.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_launcher_uninstall_active.png b/res/drawable-xxhdpi/ic_launcher_uninstall_active.png
deleted file mode 100644
index db7d339..0000000
--- a/res/drawable-xxhdpi/ic_launcher_uninstall_active.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxxhdpi/ic_launcher_info_active.png b/res/drawable-xxxhdpi/ic_launcher_info_active.png
deleted file mode 100644
index 162e23d..0000000
--- a/res/drawable-xxxhdpi/ic_launcher_info_active.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxxhdpi/ic_launcher_remove_active.png b/res/drawable-xxxhdpi/ic_launcher_remove_active.png
deleted file mode 100644
index c0b8ea2..0000000
--- a/res/drawable-xxxhdpi/ic_launcher_remove_active.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxxhdpi/ic_launcher_uninstall_active.png b/res/drawable-xxxhdpi/ic_launcher_uninstall_active.png
deleted file mode 100644
index 75896f3..0000000
--- a/res/drawable-xxxhdpi/ic_launcher_uninstall_active.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable/info_target_selector.xml b/res/drawable/info_target_selector.xml
deleted file mode 100644
index 51caece..0000000
--- a/res/drawable/info_target_selector.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-**
-** Copyright 2011, 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.
-*/
--->
-
-<transition xmlns:android="http://schemas.android.com/apk/res/android">
- <item android:drawable="@drawable/ic_launcher_info_normal" />
- <item android:drawable="@drawable/ic_launcher_info_active" />
-</transition>
diff --git a/res/drawable/remove_target_selector.xml b/res/drawable/remove_target_selector.xml
deleted file mode 100644
index 9025e8a..0000000
--- a/res/drawable/remove_target_selector.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-**
-** Copyright 2007, 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.
-*/
--->
-
-<transition xmlns:android="http://schemas.android.com/apk/res/android">
- <item android:drawable="@drawable/ic_launcher_remove_normal" />
- <item android:drawable="@drawable/ic_launcher_remove_active" />
-</transition>
diff --git a/res/drawable/uninstall_target_selector.xml b/res/drawable/uninstall_target_selector.xml
deleted file mode 100644
index 175cc20..0000000
--- a/res/drawable/uninstall_target_selector.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-**
-** Copyright 2007, 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.
-*/
--->
-
-<transition xmlns:android="http://schemas.android.com/apk/res/android">
- <item android:drawable="@drawable/ic_launcher_uninstall_normal" />
- <item android:drawable="@drawable/ic_launcher_uninstall_active" />
-</transition>
diff --git a/res/layout/apps_list_view.xml b/res/layout/apps_list_view.xml
index 6f9be05..0404983 100644
--- a/res/layout/apps_list_view.xml
+++ b/res/layout/apps_list_view.xml
@@ -39,6 +39,8 @@
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/apps_search_bar_height"
android:orientation="horizontal"
+ android:descendantFocusability="afterDescendants"
+ android:focusable="true"
android:visibility="invisible" >
</LinearLayout>
diff --git a/res/layout/search_drop_target_bar.xml b/res/layout/search_drop_target_bar.xml
index b0435aa..4737ee1 100644
--- a/res/layout/search_drop_target_bar.xml
+++ b/res/layout/search_drop_target_bar.xml
@@ -36,8 +36,6 @@
<com.android.launcher3.DeleteDropTarget
android:id="@+id/delete_target_text"
style="@style/DropTargetButton"
- android:drawableLeft="@drawable/remove_target_selector"
- android:drawableStart="@drawable/remove_target_selector"
android:text="@string/delete_target_label" />
</FrameLayout>
@@ -50,8 +48,6 @@
<com.android.launcher3.InfoDropTarget
android:id="@+id/info_target_text"
style="@style/DropTargetButton"
- android:drawableLeft="@drawable/info_target_selector"
- android:drawableStart="@drawable/info_target_selector"
android:text="@string/info_target_label" />
</FrameLayout>
@@ -64,8 +60,6 @@
<com.android.launcher3.UninstallDropTarget
android:id="@+id/uninstall_target_text"
style="@style/DropTargetButton"
- android:drawableLeft="@drawable/uninstall_target_selector"
- android:drawableStart="@drawable/uninstall_target_selector"
android:text="@string/delete_target_uninstall_label" />
</FrameLayout>
</LinearLayout>
diff --git a/res/layout/widgets_list_row_view.xml b/res/layout/widgets_list_row_view.xml
index dc1bcce..67b4acb 100644
--- a/res/layout/widgets_list_row_view.xml
+++ b/res/layout/widgets_list_row_view.xml
@@ -19,6 +19,7 @@
android:id="@+id/widgets_cell_list_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
+ android:background="@color/widgets_cell_color"
android:orientation="vertical"
android:focusable="true"
android:descendantFocusability="afterDescendants">
@@ -52,7 +53,7 @@
android:id="@+id/widgets_scroll_container"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:scrollbars="none" >
+ android:scrollbars="none">
<LinearLayout
android:id="@+id/widgets_cell_list"
android:layout_width="wrap_content"
@@ -61,7 +62,6 @@
android:layout_marginLeft="@dimen/widget_row_padding"
android:orientation="horizontal"
android:divider="@drawable/widgets_row_divider"
- android:showDividers="middle"
- android:background="@color/widgets_cell_color"/>
+ android:showDividers="middle"/>
</HorizontalScrollView>
</LinearLayout>
diff --git a/res/layout/widgets_view.xml b/res/layout/widgets_view.xml
index 1857595..9d9fa10 100644
--- a/res/layout/widgets_view.xml
+++ b/res/layout/widgets_view.xml
@@ -34,19 +34,12 @@
android:focusable="false"
android:visibility="invisible" />
- <LinearLayout
- android:id="@+id/widgets_content"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:clipChildren="false"
- android:elevation="15dp"
- android:visibility="gone"
- android:orientation="vertical" >
-
<com.android.launcher3.widget.WidgetsContainerRecyclerView
- android:id="@+id/widgets_list_view"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:background="@drawable/quantum_panel_dark"/>
- </LinearLayout>
+ android:id="@+id/widgets_list_view"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:background="@drawable/quantum_panel_dark"
+ android:elevation="15dp"
+ android:visibility="gone" />
+
</com.android.launcher3.widget.WidgetsContainerView>
\ No newline at end of file
diff --git a/res/values/colors.xml b/res/values/colors.xml
index 1e89615..a5db2fc 100644
--- a/res/values/colors.xml
+++ b/res/values/colors.xml
@@ -20,9 +20,9 @@
<resources>
<!-- The color tints to apply to the text and drag view when hovering
over the delete target or the info target -->
- <color name="delete_target_hover_tint">#DAC1C1C1</color>
- <color name="uninstall_target_hover_tint">#DAF0592B</color>
- <color name="info_target_hover_tint">#DA009688</color>
+ <color name="delete_target_hover_tint">#FFC1C1C1</color>
+ <color name="uninstall_target_hover_tint">#FFF0592B</color>
+ <color name="info_target_hover_tint">#FF009688</color>
<color name="cling_scrim_background">#80000000</color>
<color name="focused_background">#80c6c5c5</color>
diff --git a/src/com/android/launcher3/AlphabeticalAppsList.java b/src/com/android/launcher3/AlphabeticalAppsList.java
index 82aaeb9..5e05d11 100644
--- a/src/com/android/launcher3/AlphabeticalAppsList.java
+++ b/src/com/android/launcher3/AlphabeticalAppsList.java
@@ -5,14 +5,10 @@
import android.support.v7.widget.RecyclerView;
import android.util.Log;
import com.android.launcher3.compat.AlphabeticIndexCompat;
-import com.android.launcher3.compat.UserHandleCompat;
-import com.android.launcher3.compat.UserManagerCompat;
import com.android.launcher3.model.AppNameComparator;
-import java.text.Collator;
import java.util.ArrayList;
import java.util.Collections;
-import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
@@ -121,10 +117,10 @@
}
/**
- * A callback to notify of changes to the filter.
+ * Callback to notify when the set of adapter items have changed.
*/
- public interface FilterChangedCallback {
- void onFilterChanged();
+ public interface AdapterChangedCallback {
+ void onAdapterItemsChanged();
}
/**
@@ -178,12 +174,20 @@
private static final int MAX_NUM_MERGES_PHONE = 2;
private Context mContext;
+
+ // The set of apps from the system not including predictions
private List<AppInfo> mApps = new ArrayList<>();
+ // The set of filtered apps with the current filter
private List<AppInfo> mFilteredApps = new ArrayList<>();
- private List<AdapterItem> mSectionedFilteredApps = new ArrayList<>();
+ // The current set of adapter items
+ private List<AdapterItem> mAdapterItems = new ArrayList<>();
+ // The set of sections for the apps with the current filter
private List<SectionInfo> mSections = new ArrayList<>();
+ // The set of sections that we allow fast-scrolling to (includes non-merged sections)
private List<FastScrollSectionInfo> mFastScrollerSections = new ArrayList<>();
+ // The set of predicted app component names
private List<ComponentName> mPredictedAppComponents = new ArrayList<>();
+ // The set of predicted apps resolved from the component names and the current set of apps
private List<AppInfo> mPredictedApps = new ArrayList<>();
private HashMap<CharSequence, String> mCachedSectionNames = new HashMap<>();
private RecyclerView.Adapter mAdapter;
@@ -191,16 +195,16 @@
private AlphabeticIndexCompat mIndexer;
private AppNameComparator mAppNameComparator;
private MergeAlgorithm mMergeAlgorithm;
- private FilterChangedCallback mFilterChangedCallback;
+ private AdapterChangedCallback mAdapterChangedCallback;
private int mNumAppsPerRow;
private int mNumPredictedAppsPerRow;
- public AlphabeticalAppsList(Context context, FilterChangedCallback cb, int numAppsPerRow,
+ public AlphabeticalAppsList(Context context, AdapterChangedCallback auCb, int numAppsPerRow,
int numPredictedAppsPerRow) {
mContext = context;
mIndexer = new AlphabeticIndexCompat(context);
mAppNameComparator = new AppNameComparator(context);
- mFilterChangedCallback = cb;
+ mAdapterChangedCallback = auCb;
setNumAppsPerRow(numAppsPerRow, numPredictedAppsPerRow);
}
@@ -248,7 +252,7 @@
* Returns the current filtered list of applications broken down into their sections.
*/
public List<AdapterItem> getAdapterItems() {
- return mSectionedFilteredApps;
+ return mAdapterItems;
}
/**
@@ -278,11 +282,7 @@
public void setFilter(Filter f) {
if (mFilter != f) {
mFilter = f;
- onAppsUpdated();
- mAdapter.notifyDataSetChanged();
- if (mFilterChangedCallback != null){
- mFilterChangedCallback.onFilterChanged();
- }
+ updateAdapterItems();
}
}
@@ -294,7 +294,6 @@
mPredictedAppComponents.clear();
mPredictedAppComponents.addAll(apps);
onAppsUpdated();
- mAdapter.notifyDataSetChanged();
}
/**
@@ -311,7 +310,6 @@
mApps.clear();
mApps.addAll(apps);
onAppsUpdated();
- mAdapter.notifyDataSetChanged();
}
/**
@@ -320,10 +318,9 @@
public void addApps(List<AppInfo> apps) {
// We add it in place, in alphabetical order
for (AppInfo info : apps) {
- addApp(info);
+ mApps.add(info);
}
onAppsUpdated();
- mAdapter.notifyDataSetChanged();
}
/**
@@ -335,11 +332,10 @@
if (index != -1) {
mApps.set(index, info);
} else {
- addApp(info);
+ mApps.add(info);
}
}
onAppsUpdated();
- mAdapter.notifyDataSetChanged();
}
/**
@@ -353,7 +349,6 @@
}
}
onAppsUpdated();
- mAdapter.notifyDataSetChanged();
}
/**
@@ -373,34 +368,68 @@
}
/**
- * Implementation to actually add an app to the alphabetic list, but does not notify.
- */
- private void addApp(AppInfo info) {
- int index = Collections.binarySearch(mApps, info, mAppNameComparator.getAppInfoComparator());
- if (index < 0) {
- mApps.add(-(index + 1), info);
- }
- }
-
- /**
* Updates internals when the set of apps are updated.
*/
private void onAppsUpdated() {
// Sort the list of apps
Collections.sort(mApps, mAppNameComparator.getAppInfoComparator());
- // Prepare to update the list of sections, filtered apps, etc.
- mFilteredApps.clear();
- mSections.clear();
- mSectionedFilteredApps.clear();
- mFastScrollerSections.clear();
+ // As a special case for some languages (currently only Simplified Chinese), we may need to
+ // coalesce sections
+ Locale curLocale = mContext.getResources().getConfiguration().locale;
+ TreeMap<String, ArrayList<AppInfo>> sectionMap = null;
+ boolean localeRequiresSectionSorting = curLocale.equals(Locale.SIMPLIFIED_CHINESE);
+ if (localeRequiresSectionSorting) {
+ // Compute the section headers. We use a TreeMap with the section name comparator to
+ // ensure that the sections are ordered when we iterate over it later
+ sectionMap = new TreeMap<>(mAppNameComparator.getSectionNameComparator());
+ for (AppInfo info : mApps) {
+ // Add the section to the cache
+ String sectionName = getAndUpdateCachedSectionName(info.title);
+
+ // Add it to the mapping
+ ArrayList<AppInfo> sectionApps = sectionMap.get(sectionName);
+ if (sectionApps == null) {
+ sectionApps = new ArrayList<>();
+ sectionMap.put(sectionName, sectionApps);
+ }
+ sectionApps.add(info);
+ }
+
+ // Add each of the section apps to the list in order
+ List<AppInfo> allApps = new ArrayList<>(mApps.size());
+ for (Map.Entry<String, ArrayList<AppInfo>> entry : sectionMap.entrySet()) {
+ allApps.addAll(entry.getValue());
+ }
+ mApps = allApps;
+ } else {
+ // Just compute the section headers for use below
+ for (AppInfo info : mApps) {
+ // Add the section to the cache
+ getAndUpdateCachedSectionName(info.title);
+ }
+ }
+
+ // Recompose the set of adapter items from the current set of apps
+ updateAdapterItems();
+ }
+
+ /**
+ * Updates the set of filtered apps with the current filter. At this point, we expect
+ * mCachedSectionNames to have been calculated for the set of all apps in mApps.
+ */
+ private void updateAdapterItems() {
SectionInfo lastSectionInfo = null;
String lastSectionName = null;
FastScrollSectionInfo lastFastScrollerSectionInfo = null;
int position = 0;
int appIndex = 0;
- List<AppInfo> allApps = new ArrayList<>();
+ // Prepare to update the list of sections, filtered apps, etc.
+ mFilteredApps.clear();
+ mFastScrollerSections.clear();
+ mAdapterItems.clear();
+ mSections.clear();
// Process the predicted app components
mPredictedApps.clear();
@@ -421,61 +450,16 @@
if (!mPredictedApps.isEmpty()) {
// Create a new spacer for the prediction bar
AdapterItem sectionItem = AdapterItem.asPredictionBarSpacer(position++);
- mSectionedFilteredApps.add(sectionItem);
+ mAdapterItems.add(sectionItem);
}
}
- // As a special case for some languages (currently only Simplified Chinese), we may need to
- // coalesce sections
- Locale curLocale = mContext.getResources().getConfiguration().locale;
- TreeMap<String, ArrayList<AppInfo>> sectionMap = null;
- boolean localeRequiresSectionSorting = curLocale.equals(Locale.SIMPLIFIED_CHINESE);
- if (localeRequiresSectionSorting) {
- // Compute the section headers. We use a TreeMap with the section name comparator to
- // ensure that the sections are ordered when we iterate over it later
- sectionMap = new TreeMap<>(mAppNameComparator.getSectionNameComparator());
- for (AppInfo info : mApps) {
- // Add the section to the cache
- String sectionName = mCachedSectionNames.get(info.title);
- if (sectionName == null) {
- sectionName = mIndexer.computeSectionName(info.title);
- mCachedSectionNames.put(info.title, sectionName);
- }
-
- // Add it to the mapping
- ArrayList<AppInfo> sectionApps = sectionMap.get(sectionName);
- if (sectionApps == null) {
- sectionApps = new ArrayList<>();
- sectionMap.put(sectionName, sectionApps);
- }
- sectionApps.add(info);
- }
-
- // Add it to the list
- for (Map.Entry<String, ArrayList<AppInfo>> entry : sectionMap.entrySet()) {
- allApps.addAll(entry.getValue());
- }
- } else {
- // Just compute the section headers for use below
- for (AppInfo info : mApps) {
- // Add the section to the cache
- String sectionName = mCachedSectionNames.get(info.title);
- if (sectionName == null) {
- sectionName = mIndexer.computeSectionName(info.title);
- mCachedSectionNames.put(info.title, sectionName);
- }
- }
- // Add it to the list
- allApps.addAll(mApps);
- }
-
// Recreate the filtered and sectioned apps (for convenience for the grid layout) from the
// ordered set of sections
- int numApps = allApps.size();
+ int numApps = mApps.size();
for (int i = 0; i < numApps; i++) {
- AppInfo info = allApps.get(i);
- // The section name was computed above so this should be find
- String sectionName = mCachedSectionNames.get(info.title);
+ AppInfo info = mApps.get(i);
+ String sectionName = getAndUpdateCachedSectionName(info.title);
// Check if we want to retain this app
if (mFilter != null && !mFilter.retainApp(info, sectionName)) {
@@ -494,7 +478,7 @@
// Create a new section item to break the flow of items in the list
if (!hasFilter()) {
AdapterItem sectionItem = AdapterItem.asSectionBreak(position++, lastSectionInfo);
- mSectionedFilteredApps.add(sectionItem);
+ mAdapterItems.add(sectionItem);
}
}
@@ -505,12 +489,21 @@
lastSectionInfo.firstAppItem = appItem;
lastFastScrollerSectionInfo.appItem = appItem;
}
- mSectionedFilteredApps.add(appItem);
+ mAdapterItems.add(appItem);
mFilteredApps.add(info);
}
// Merge multiple sections together as requested by the merge strategy for this device
mergeSections();
+
+ // Refresh the recycler view
+ if (mAdapter != null) {
+ mAdapter.notifyDataSetChanged();
+ }
+
+ if (mAdapterChangedCallback != null) {
+ mAdapterChangedCallback.onAdapterItemsChanged();
+ }
}
/**
@@ -531,20 +524,20 @@
SectionInfo nextSection = mSections.remove(i + 1);
// Remove the next section break
- mSectionedFilteredApps.remove(nextSection.sectionBreakItem);
- int pos = mSectionedFilteredApps.indexOf(section.firstAppItem);
+ mAdapterItems.remove(nextSection.sectionBreakItem);
+ int pos = mAdapterItems.indexOf(section.firstAppItem);
// Point the section for these new apps to the merged section
int nextPos = pos + section.numApps;
for (int j = nextPos; j < (nextPos + nextSection.numApps); j++) {
- AdapterItem item = mSectionedFilteredApps.get(j);
+ AdapterItem item = mAdapterItems.get(j);
item.sectionInfo = section;
item.sectionAppIndex += section.numApps;
}
// Update the following adapter items of the removed section item
- pos = mSectionedFilteredApps.indexOf(nextSection.firstAppItem);
- for (int j = pos; j < mSectionedFilteredApps.size(); j++) {
- AdapterItem item = mSectionedFilteredApps.get(j);
+ pos = mAdapterItems.indexOf(nextSection.firstAppItem);
+ for (int j = pos; j < mAdapterItems.size(); j++) {
+ AdapterItem item = mAdapterItems.get(j);
item.position--;
}
section.numApps += nextSection.numApps;
@@ -560,4 +553,17 @@
}
}
}
+
+ /**
+ * Returns the cached section name for the given title, recomputing and updating the cache if
+ * the title has no cached section name.
+ */
+ private String getAndUpdateCachedSectionName(CharSequence title) {
+ String sectionName = mCachedSectionNames.get(title);
+ if (sectionName == null) {
+ sectionName = mIndexer.computeSectionName(title);
+ mCachedSectionNames.put(title, sectionName);
+ }
+ return sectionName;
+ }
}
diff --git a/src/com/android/launcher3/AppsContainerView.java b/src/com/android/launcher3/AppsContainerView.java
index 5ccb6c6..612c19c 100644
--- a/src/com/android/launcher3/AppsContainerView.java
+++ b/src/com/android/launcher3/AppsContainerView.java
@@ -136,7 +136,7 @@
*/
public class AppsContainerView extends BaseContainerView implements DragSource, Insettable,
TextWatcher, TextView.OnEditorActionListener, LauncherTransitionable,
- AlphabeticalAppsList.FilterChangedCallback, AppsGridAdapter.PredictionBarSpacerCallbacks,
+ AlphabeticalAppsList.AdapterChangedCallback, AppsGridAdapter.PredictionBarSpacerCallbacks,
View.OnTouchListener, View.OnClickListener, View.OnLongClickListener,
ViewTreeObserver.OnPreDrawListener {
@@ -183,6 +183,7 @@
private int mContainerInset;
private int mPredictionBarHeight;
private int mLastRecyclerViewScrollPos = -1;
+ private boolean mFocusPredictionBarOnFirstBind;
private CheckLongPressHelper mPredictionIconCheckForLongPress;
private View mPredictionIconUnderTouch;
@@ -298,7 +299,17 @@
@Override
public void onFocusChange(View v, boolean hasFocus) {
if (v == mContentView && hasFocus) {
- mAppsRecyclerView.requestFocus();
+ if (!mApps.getPredictedApps().isEmpty()) {
+ // If the prediction bar is going to be bound, then defer focusing until
+ // it is first bound
+ if (mPredictionBarView.getChildCount() == 0) {
+ mFocusPredictionBarOnFirstBind = true;
+ } else {
+ mPredictionBarView.requestFocus();
+ }
+ } else {
+ mAppsRecyclerView.requestFocus();
+ }
}
}
});
@@ -387,6 +398,11 @@
icon.setVisibility(View.INVISIBLE);
}
}
+
+ if (mFocusPredictionBarOnFirstBind) {
+ mFocusPredictionBarOnFirstBind = false;
+ mPredictionBarView.requestFocus();
+ }
}
@Override
@@ -670,7 +686,7 @@
}
@Override
- public void onFilterChanged() {
+ public void onAdapterItemsChanged() {
updatePredictionBarVisibility();
}
diff --git a/src/com/android/launcher3/BubbleTextView.java b/src/com/android/launcher3/BubbleTextView.java
index 3b3b9bf..d530009 100644
--- a/src/com/android/launcher3/BubbleTextView.java
+++ b/src/com/android/launcher3/BubbleTextView.java
@@ -38,7 +38,7 @@
import android.widget.TextView;
import com.android.launcher3.IconCache.IconLoadRequest;
-import com.android.launcher3.widget.PackageItemInfo;
+import com.android.launcher3.model.PackageItemInfo;
/**
* TextView that draws a bubble behind the text. We cannot use a LineBackgroundSpan
diff --git a/src/com/android/launcher3/ButtonDropTarget.java b/src/com/android/launcher3/ButtonDropTarget.java
index b8214d1..4cd28c0 100644
--- a/src/com/android/launcher3/ButtonDropTarget.java
+++ b/src/com/android/launcher3/ButtonDropTarget.java
@@ -16,15 +16,20 @@
package com.android.launcher3;
+import android.animation.AnimatorSet;
+import android.animation.FloatArrayEvaluator;
import android.animation.ObjectAnimator;
+import android.animation.ValueAnimator;
+import android.animation.ValueAnimator.AnimatorUpdateListener;
import android.annotation.TargetApi;
import android.content.Context;
import android.content.res.ColorStateList;
import android.content.res.Configuration;
+import android.graphics.ColorMatrix;
+import android.graphics.ColorMatrixColorFilter;
import android.graphics.PointF;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
-import android.graphics.drawable.TransitionDrawable;
import android.os.Build;
import android.util.AttributeSet;
import android.view.View;
@@ -56,9 +61,11 @@
protected int mHoverColor = 0;
protected ColorStateList mOriginalTextColor;
- protected TransitionDrawable mDrawable;
+ protected Drawable mDrawable;
- private ObjectAnimator mCurrentColorAnim;
+ private AnimatorSet mCurrentColorAnim;
+ private ColorMatrix mSrcFilter, mDstFilter, mCurrentFilter;
+
public ButtonDropTarget(Context context, AttributeSet attrs) {
this(context, attrs, 0);
@@ -85,21 +92,14 @@
@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
protected void setDrawable(int resId) {
- // Get the hover color
- mDrawable = (TransitionDrawable) getCurrentDrawable();
+ // We do not set the drawable in the xml as that inflates two drawables corresponding to
+ // drawableLeft and drawableStart.
+ mDrawable = getResources().getDrawable(resId);
- if (mDrawable == null) {
- // TODO: investigate why this is ever happening. Presently only on one known device.
- mDrawable = (TransitionDrawable) getResources().getDrawable(resId);
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
- setCompoundDrawablesRelativeWithIntrinsicBounds(mDrawable, null, null, null);
- } else {
- setCompoundDrawablesWithIntrinsicBounds(mDrawable, null, null, null);
- }
- }
-
- if (null != mDrawable) {
- mDrawable.setCrossFadeEnabled(true);
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
+ setCompoundDrawablesRelativeWithIntrinsicBounds(mDrawable, null, null, null);
+ } else {
+ setCompoundDrawablesWithIntrinsicBounds(mDrawable, null, null, null);
}
}
@@ -111,16 +111,6 @@
mSearchDropTargetBar = searchDropTargetBar;
}
- protected Drawable getCurrentDrawable() {
- Drawable[] drawables = getCompoundDrawables();
- for (int i = 0; i < drawables.length; ++i) {
- if (drawables[i] != null) {
- return drawables[i];
- }
- }
- return null;
- }
-
@Override
public void onFlingToDelete(DragObject d, PointF vec) { }
@@ -128,10 +118,13 @@
public final void onDragEnter(DragObject d) {
d.dragView.setColor(mHoverColor);
if (Utilities.isLmpOrAbove()) {
- mDrawable.startTransition(DragView.COLOR_CHANGE_DURATION);
animateTextColor(mHoverColor);
} else {
- mDrawable.startTransition(0);
+ if (mCurrentFilter == null) {
+ mCurrentFilter = new ColorMatrix();
+ }
+ DragView.setColorScale(mHoverColor, mCurrentFilter);
+ mDrawable.setColorFilter(new ColorMatrixColorFilter(mCurrentFilter));
setTextColor(mHoverColor);
}
}
@@ -141,12 +134,11 @@
// Do nothing
}
- protected void resetHoverColor() {
+ protected void resetHoverColor() {
if (Utilities.isLmpOrAbove()) {
- mDrawable.reverseTransition(DragView.COLOR_CHANGE_DURATION);
animateTextColor(mOriginalTextColor.getDefaultColor());
} else {
- mDrawable.resetTransition();
+ mDrawable.setColorFilter(null);
setTextColor(mOriginalTextColor);
}
}
@@ -156,8 +148,32 @@
if (mCurrentColorAnim != null) {
mCurrentColorAnim.cancel();
}
- mCurrentColorAnim = ObjectAnimator.ofArgb(this, "textColor", targetColor);
+
+ mCurrentColorAnim = new AnimatorSet();
mCurrentColorAnim.setDuration(DragView.COLOR_CHANGE_DURATION);
+
+ if (mSrcFilter == null) {
+ mSrcFilter = new ColorMatrix();
+ mDstFilter = new ColorMatrix();
+ mCurrentFilter = new ColorMatrix();
+ }
+
+ DragView.setColorScale(getTextColor(), mSrcFilter);
+ DragView.setColorScale(targetColor, mDstFilter);
+ ValueAnimator anim1 = ValueAnimator.ofObject(
+ new FloatArrayEvaluator(mCurrentFilter.getArray()),
+ mSrcFilter.getArray(), mDstFilter.getArray());
+ anim1.addUpdateListener(new AnimatorUpdateListener() {
+
+ @Override
+ public void onAnimationUpdate(ValueAnimator animation) {
+ mDrawable.setColorFilter(new ColorMatrixColorFilter(mCurrentFilter));
+ invalidate();
+ }
+ });
+
+ mCurrentColorAnim.play(anim1);
+ mCurrentColorAnim.play(ObjectAnimator.ofArgb(this, "textColor", targetColor));
mCurrentColorAnim.start();
}
@@ -172,10 +188,10 @@
}
}
- @Override
+ @Override
public final void onDragStart(DragSource source, Object info, int dragAction) {
mActive = supportsDrop(source, info);
- mDrawable.resetTransition();
+ mDrawable.setColorFilter(null);
if (mCurrentColorAnim != null) {
mCurrentColorAnim.cancel();
mCurrentColorAnim = null;
diff --git a/src/com/android/launcher3/CellLayout.java b/src/com/android/launcher3/CellLayout.java
index edcdc11..1d211bf 100644
--- a/src/com/android/launcher3/CellLayout.java
+++ b/src/com/android/launcher3/CellLayout.java
@@ -103,8 +103,6 @@
private float FOREGROUND_ALPHA_DAMPER = 0.65f;
private int mForegroundAlpha = 0;
private float mBackgroundAlpha;
- private float mBackgroundAlphaMultiplier = 1.0f;
- private boolean mDrawBackground = true;
private Drawable mNormalBackground;
private Drawable mActiveGlowBackground;
@@ -423,10 +421,6 @@
}
}
- void disableBackground() {
- mDrawBackground = false;
- }
-
void disableDragTarget() {
mIsDragTarget = false;
}
@@ -448,12 +442,16 @@
@Override
protected void onDraw(Canvas canvas) {
+ if (!mIsDragTarget) {
+ return;
+ }
+
// When we're large, we are either drawn in a "hover" state (ie when dragging an item to
// a neighboring page) or with just a normal background (if backgroundAlpha > 0.0f)
// When we're small, we are either drawn normally or in the "accepts drops" state (during
// a drag). However, we also drag the mini hover background *over* one of those two
// backgrounds
- if (mDrawBackground && mBackgroundAlpha > 0.0f) {
+ if (mBackgroundAlpha > 0.0f) {
Drawable bg;
if (mIsDragOverlapping) {
@@ -463,7 +461,7 @@
bg = mNormalBackground;
}
- bg.setAlpha((int) (mBackgroundAlpha * mBackgroundAlphaMultiplier * 255));
+ bg.setAlpha((int) (mBackgroundAlpha * 255));
bg.setBounds(mBackgroundRect);
bg.draw(canvas);
}
@@ -951,17 +949,6 @@
return mBackgroundAlpha;
}
- public void setBackgroundAlphaMultiplier(float multiplier) {
- if (mBackgroundAlphaMultiplier != multiplier) {
- mBackgroundAlphaMultiplier = multiplier;
- invalidate();
- }
- }
-
- public float getBackgroundAlphaMultiplier() {
- return mBackgroundAlphaMultiplier;
- }
-
public void setBackgroundAlpha(float alpha) {
if (mBackgroundAlpha != alpha) {
mBackgroundAlpha = alpha;
diff --git a/src/com/android/launcher3/DeleteDropTarget.java b/src/com/android/launcher3/DeleteDropTarget.java
index 08186f5..fa6e74f 100644
--- a/src/com/android/launcher3/DeleteDropTarget.java
+++ b/src/com/android/launcher3/DeleteDropTarget.java
@@ -43,7 +43,7 @@
// Get the hover color
mHoverColor = getResources().getColor(R.color.delete_target_hover_tint);
- setDrawable(R.drawable.remove_target_selector);
+ setDrawable(R.drawable.ic_launcher_remove_normal);
}
public static boolean supportsDrop(Object info) {
diff --git a/src/com/android/launcher3/DragView.java b/src/com/android/launcher3/DragView.java
index 120299e..b332338 100644
--- a/src/com/android/launcher3/DragView.java
+++ b/src/com/android/launcher3/DragView.java
@@ -38,7 +38,7 @@
import java.util.Arrays;
public class DragView extends View {
- public static int COLOR_CHANGE_DURATION = 200;
+ public static int COLOR_CHANGE_DURATION = 120;
@Thunk static float sDragAlpha = 1f;
@@ -249,8 +249,7 @@
m1.setSaturation(0);
ColorMatrix m2 = new ColorMatrix();
- m2.setScale(Color.red(color) / 255f, Color.green(color) / 255f,
- Color.blue(color) / 255f, Color.alpha(color) / 255f);
+ setColorScale(color, m2);
m1.postConcat(m2);
if (Utilities.isLmpOrAbove()) {
@@ -355,4 +354,9 @@
mDragLayer.removeView(DragView.this);
}
}
+
+ public static void setColorScale(int color, ColorMatrix target) {
+ target.setScale(Color.red(color) / 255f, Color.green(color) / 255f,
+ Color.blue(color) / 255f, Color.alpha(color) / 255f);
+ }
}
diff --git a/src/com/android/launcher3/FocusHelper.java b/src/com/android/launcher3/FocusHelper.java
index c77d416..678ed0f 100644
--- a/src/com/android/launcher3/FocusHelper.java
+++ b/src/com/android/launcher3/FocusHelper.java
@@ -314,12 +314,14 @@
// with the hotseat.
if (keyCode == KeyEvent.KEYCODE_DPAD_DOWN && !profile.isVerticalBarLayout()) {
matrix = FocusLogic.createSparseMatrix(iconLayout, hotseatLayout, true /* horizontal */,
- hotseat.getAllAppsButtonRank(), false /* all apps icon is ignored */);
+ hotseat.getAllAppsButtonRank(),
+ !hotseat.hasIcons() /* ignore all apps icon, unless there are no other icons */);
countY = countY + 1;
} else if (keyCode == KeyEvent.KEYCODE_DPAD_RIGHT &&
profile.isVerticalBarLayout()) {
matrix = FocusLogic.createSparseMatrix(iconLayout, hotseatLayout, false /* horizontal */,
- hotseat.getAllAppsButtonRank(), false /* all apps icon is ignored */);
+ hotseat.getAllAppsButtonRank(),
+ !hotseat.hasIcons() /* ignore all apps icon, unless there are no other icons */);
countX = countX + 1;
} else if (keyCode == KeyEvent.KEYCODE_DEL || keyCode == KeyEvent.KEYCODE_FORWARD_DEL) {
workspace.removeWorkspaceItem(v);
diff --git a/src/com/android/launcher3/FolderInfo.java b/src/com/android/launcher3/FolderInfo.java
index 930f911..aea21c9 100644
--- a/src/com/android/launcher3/FolderInfo.java
+++ b/src/com/android/launcher3/FolderInfo.java
@@ -92,7 +92,7 @@
}
public void setTitle(CharSequence title) {
- this.title = Utilities.trim(title);
+ this.title = title;
for (int i = 0; i < listeners.size(); i++) {
listeners.get(i).onTitleChanged(title);
}
diff --git a/src/com/android/launcher3/FolderPagedView.java b/src/com/android/launcher3/FolderPagedView.java
index de30b60..06ed588 100644
--- a/src/com/android/launcher3/FolderPagedView.java
+++ b/src/com/android/launcher3/FolderPagedView.java
@@ -241,6 +241,11 @@
return page;
}
+ @Override
+ protected int getChildGap() {
+ return getPaddingLeft() + getPaddingRight();
+ }
+
public void setFixedSize(int width, int height) {
width -= (getPaddingLeft() + getPaddingRight());
height -= (getPaddingTop() + getPaddingBottom());
diff --git a/src/com/android/launcher3/Hotseat.java b/src/com/android/launcher3/Hotseat.java
index b614bc6..b8337b6 100644
--- a/src/com/android/launcher3/Hotseat.java
+++ b/src/com/android/launcher3/Hotseat.java
@@ -65,6 +65,13 @@
}
/**
+ * Returns whether there are other icons than the all apps button in the hotseat.
+ */
+ public boolean hasIcons() {
+ return mContent.getShortcutsAndWidgets().getChildCount() > 1;
+ }
+
+ /**
* Registers the specified listener on the cell layout of the hotseat.
*/
@Override
@@ -98,25 +105,6 @@
return rank == mAllAppsButtonRank;
}
- /** This returns the coordinates of an app in a given cell, relative to the DragLayer */
- Rect getCellCoordinates(int cellX, int cellY) {
- Rect coords = new Rect();
- mContent.cellToRect(cellX, cellY, 1, 1, coords);
- int[] hotseatInParent = new int[2];
- Utilities.getDescendantCoordRelativeToParent(this, mLauncher.getDragLayer(),
- hotseatInParent, false);
- coords.offset(hotseatInParent[0], hotseatInParent[1]);
-
- // Center the icon
- int cWidth = mContent.getShortcutsAndWidgets().getCellContentWidth();
- int cHeight = mContent.getShortcutsAndWidgets().getCellContentHeight();
- int cellPaddingX = (int) Math.max(0, ((coords.width() - cWidth) / 2f));
- int cellPaddingY = (int) Math.max(0, ((coords.height() - cHeight) / 2f));
- coords.offset(cellPaddingX, cellPaddingY);
-
- return coords;
- }
-
@Override
protected void onFinishInflate() {
super.onFinishInflate();
diff --git a/src/com/android/launcher3/IconCache.java b/src/com/android/launcher3/IconCache.java
index fff07c6..8b5f747 100644
--- a/src/com/android/launcher3/IconCache.java
+++ b/src/com/android/launcher3/IconCache.java
@@ -35,6 +35,7 @@
import android.graphics.Canvas;
import android.graphics.drawable.Drawable;
import android.os.Handler;
+import android.os.SystemClock;
import android.text.TextUtils;
import android.util.Log;
@@ -42,9 +43,9 @@
import com.android.launcher3.compat.LauncherAppsCompat;
import com.android.launcher3.compat.UserHandleCompat;
import com.android.launcher3.compat.UserManagerCompat;
+import com.android.launcher3.model.PackageItemInfo;
import com.android.launcher3.util.ComponentKey;
import com.android.launcher3.util.Thunk;
-import com.android.launcher3.widget.PackageItemInfo;
import java.util.HashMap;
import java.util.HashSet;
@@ -52,6 +53,7 @@
import java.util.List;
import java.util.Locale;
import java.util.Map.Entry;
+import java.util.Stack;
/**
* Cache of application icons. Icons can be made from any thread.
@@ -69,6 +71,8 @@
private static final int LOW_RES_SCALE_FACTOR = 8;
+ private static final Object ICON_UPDATE_TOKEN = new Object();
+
@Thunk static class CacheEntry {
public Bitmap icon;
public CharSequence title;
@@ -221,13 +225,32 @@
new String[] {packageName + "/%", Long.toString(userSerial)});
}
+ public void updateDbIcons() {
+ // Remove all active icon update tasks.
+ mWorkerHandler.removeCallbacksAndMessages(ICON_UPDATE_TOKEN);
+
+ mIconDb.updateSystemStateString(mContext);
+ for (UserHandleCompat user : mUserManager.getUserProfiles()) {
+ // Query for the set of apps
+ final List<LauncherActivityInfoCompat> apps = mLauncherApps.getActivityList(null, user);
+ // Fail if we don't have any apps
+ // TODO: Fix this. Only fail for the current user.
+ if (apps == null || apps.isEmpty()) {
+ return;
+ }
+
+ // Update icon cache. This happens in segments and {@link #onPackageIconsUpdated}
+ // is called by the icon cache when the job is complete.
+ updateDBIcons(user, apps);
+ }
+ }
+
/**
* Updates the persistent DB, such that only entries corresponding to {@param apps} remain in
* the DB and are updated.
* @return The set of packages for which icons have updated.
*/
- public HashSet<String> updateDBIcons(UserHandleCompat user, List<LauncherActivityInfoCompat> apps) {
- mIconDb.updateSystemStateString(mContext);
+ private void updateDBIcons(UserHandleCompat user, List<LauncherActivityInfoCompat> apps) {
long userSerial = mUserManager.getSerialNumberForUser(user);
PackageManager pm = mContext.getPackageManager();
HashMap<String, PackageInfo> pkgInfoMap = new HashMap<String, PackageInfo>();
@@ -255,7 +278,7 @@
final int systemStateIndex = c.getColumnIndex(IconDB.COLUMN_SYSTEM_STATE);
HashSet<Integer> itemsToRemove = new HashSet<Integer>();
- HashSet<String> updatedPackages = new HashSet<String>();
+ Stack<LauncherActivityInfoCompat> appsToUpdate = new Stack<>();
while (c.moveToNext()) {
String cn = c.getString(indexComponent);
@@ -279,14 +302,9 @@
}
if (app == null) {
itemsToRemove.add(c.getInt(rowIndex));
- continue;
+ } else {
+ appsToUpdate.add(app);
}
- ContentValues values = updateCacheAndGetContentValues(app);
- mIconDb.getWritableDatabase().update(IconDB.TABLE_NAME, values,
- IconDB.COLUMN_COMPONENT + " = ? AND " + IconDB.COLUMN_USER + " = ?",
- new String[] {cn, Long.toString(userSerial)});
-
- updatedPackages.add(component.getPackageName());
}
c.close();
if (!itemsToRemove.isEmpty()) {
@@ -296,21 +314,20 @@
}
// Insert remaining apps.
- for (LauncherActivityInfoCompat app : componentMap.values()) {
- PackageInfo info = pkgInfoMap.get(app.getComponentName().getPackageName());
- if (info == null) {
- continue;
- }
- addIconToDBAndMemCache(app, info, userSerial);
+ if (!componentMap.isEmpty() || !appsToUpdate.isEmpty()) {
+ Stack<LauncherActivityInfoCompat> appsToAdd = new Stack<>();
+ appsToAdd.addAll(componentMap.values());
+ new SerializedIconUpdateTask(userSerial, pkgInfoMap,
+ appsToAdd, appsToUpdate).scheduleNext();
}
- return updatedPackages;
}
private void addIconToDBAndMemCache(LauncherActivityInfoCompat app, PackageInfo info,
long userSerial) {
- ContentValues values = updateCacheAndGetContentValues(app);
+ // Reuse the existing entry if it already exists in the DB. This ensures that we do not
+ // create bitmap if it was already created during loader.
+ ContentValues values = updateCacheAndGetContentValues(app, false);
addIconToDB(values, app.getComponentName(), info, userSerial);
- values.put(IconDB.COLUMN_COMPONENT, app.getComponentName().flattenToString());
}
/**
@@ -327,9 +344,21 @@
SQLiteDatabase.CONFLICT_REPLACE);
}
- private ContentValues updateCacheAndGetContentValues(LauncherActivityInfoCompat app) {
- CacheEntry entry = new CacheEntry();
- entry.icon = Utilities.createIconBitmap(app.getBadgedIcon(mIconDpi), mContext);
+ private ContentValues updateCacheAndGetContentValues(LauncherActivityInfoCompat app,
+ boolean replaceExisting) {
+ final ComponentKey key = new ComponentKey(app.getComponentName(), app.getUser());
+ CacheEntry entry = null;
+ if (!replaceExisting) {
+ entry = mCache.get(key);
+ // We can't reuse the entry if the high-res icon is not present.
+ if (entry == null || entry.isLowResIcon || entry.icon == null) {
+ entry = null;
+ }
+ }
+ if (entry == null) {
+ entry = new CacheEntry();
+ entry.icon = Utilities.createIconBitmap(app.getBadgedIcon(mIconDpi), mContext);
+ }
entry.title = app.getLabel();
entry.contentDescription = mUserManager.getBadgedLabelForUser(entry.title, app.getUser());
mCache.put(new ComponentKey(app.getComponentName(), app.getUser()), entry);
@@ -671,6 +700,66 @@
}
}
+ /**
+ * A runnable that updates invalid icons and adds missing icons in the DB for the provided
+ * LauncherActivityInfoCompat list. Items are updated/added one at a time, so that the
+ * worker thread doesn't get blocked.
+ */
+ private class SerializedIconUpdateTask implements Runnable {
+ private final long mUserSerial;
+ private final HashMap<String, PackageInfo> mPkgInfoMap;
+ private final Stack<LauncherActivityInfoCompat> mAppsToAdd;
+ private final Stack<LauncherActivityInfoCompat> mAppsToUpdate;
+ private final HashSet<String> mUpdatedPackages = new HashSet<String>();
+
+ private SerializedIconUpdateTask(long userSerial, HashMap<String, PackageInfo> pkgInfoMap,
+ Stack<LauncherActivityInfoCompat> appsToAdd,
+ Stack<LauncherActivityInfoCompat> appsToUpdate) {
+ mUserSerial = userSerial;
+ mPkgInfoMap = pkgInfoMap;
+ mAppsToAdd = appsToAdd;
+ mAppsToUpdate = appsToUpdate;
+ }
+
+ @Override
+ public void run() {
+ if (!mAppsToUpdate.isEmpty()) {
+ LauncherActivityInfoCompat app = mAppsToUpdate.pop();
+ String cn = app.getComponentName().flattenToString();
+ ContentValues values = updateCacheAndGetContentValues(app, true);
+ mIconDb.getWritableDatabase().update(IconDB.TABLE_NAME, values,
+ IconDB.COLUMN_COMPONENT + " = ? AND " + IconDB.COLUMN_USER + " = ?",
+ new String[] {cn, Long.toString(mUserSerial)});
+ mUpdatedPackages.add(app.getComponentName().getPackageName());
+
+ if (mAppsToUpdate.isEmpty() && !mUpdatedPackages.isEmpty()) {
+ // No more app to update. Notify model.
+ LauncherAppState.getInstance().getModel().onPackageIconsUpdated(
+ mUpdatedPackages, mUserManager.getUserForSerialNumber(mUserSerial));
+ }
+
+ // Let it run one more time.
+ scheduleNext();
+ } else if (!mAppsToAdd.isEmpty()) {
+ LauncherActivityInfoCompat app = mAppsToAdd.pop();
+ PackageInfo info = mPkgInfoMap.get(app.getComponentName().getPackageName());
+ if (info != null) {
+ synchronized (IconCache.this) {
+ addIconToDBAndMemCache(app, info, mUserSerial);
+ }
+ }
+
+ if (!mAppsToAdd.isEmpty()) {
+ scheduleNext();
+ }
+ }
+ }
+
+ public void scheduleNext() {
+ mWorkerHandler.postAtTime(this, ICON_UPDATE_TOKEN, SystemClock.uptimeMillis() + 1);
+ }
+ }
+
private static final class IconDB extends SQLiteOpenHelper {
private final static int DB_VERSION = 4;
diff --git a/src/com/android/launcher3/InfoDropTarget.java b/src/com/android/launcher3/InfoDropTarget.java
index f3383cc..0f139fa 100644
--- a/src/com/android/launcher3/InfoDropTarget.java
+++ b/src/com/android/launcher3/InfoDropTarget.java
@@ -41,7 +41,7 @@
// Get the hover color
mHoverColor = getResources().getColor(R.color.info_target_hover_tint);
- setDrawable(R.drawable.info_target_selector);
+ setDrawable(R.drawable.ic_launcher_info_normal);
}
public static void startDetailsActivityForInfo(Object info, Launcher launcher) {
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index d7282eb..e5539c0 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -103,6 +103,7 @@
import com.android.launcher3.compat.LauncherAppsCompat;
import com.android.launcher3.compat.UserHandleCompat;
import com.android.launcher3.compat.UserManagerCompat;
+import com.android.launcher3.model.WidgetsModel;
import com.android.launcher3.util.LongArrayMap;
import com.android.launcher3.util.Thunk;
import com.android.launcher3.widget.PendingAddWidgetInfo;
@@ -273,8 +274,9 @@
// Main container view for the all apps screen.
@Thunk AppsContainerView mAppsView;
- // Main container view for the widget tray screen.
- private WidgetsContainerView mWidgetsView;
+ // Main container view and the model for the widget tray screen.
+ @Thunk WidgetsContainerView mWidgetsView;
+ @Thunk WidgetsModel mWidgetsModel;
private boolean mAutoAdvanceRunning = false;
private AppWidgetHostView mQsb;
@@ -4379,23 +4381,22 @@
}
}
- @Thunk ArrayList<Object> mWidgetsAndShortcuts;
private Runnable mBindPackagesUpdatedRunnable = new Runnable() {
public void run() {
- bindAllPackages(mWidgetsAndShortcuts);
+ bindAllPackages(mWidgetsModel);
}
};
@Override
- public void bindAllPackages(final ArrayList<Object> widgetsAndShortcuts) {
+ public void bindAllPackages(final WidgetsModel model) {
if (waitUntilResume(mBindPackagesUpdatedRunnable, true)) {
- mWidgetsAndShortcuts = widgetsAndShortcuts;
+ mWidgetsModel = model;
return;
}
- if (mWidgetsView != null && widgetsAndShortcuts != null) {
- mWidgetsView.addWidgets(widgetsAndShortcuts, getPackageManager());
- mWidgetsAndShortcuts = null;
+ if (mWidgetsView != null && model != null) {
+ mWidgetsView.addWidgets(model);
+ mWidgetsModel = null;
}
}
diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java
index 3e05f57..d994dc7 100644
--- a/src/com/android/launcher3/LauncherModel.java
+++ b/src/com/android/launcher3/LauncherModel.java
@@ -58,6 +58,7 @@
import com.android.launcher3.compat.PackageInstallerCompat.PackageInstallInfo;
import com.android.launcher3.compat.UserHandleCompat;
import com.android.launcher3.compat.UserManagerCompat;
+import com.android.launcher3.model.WidgetsModel;
import com.android.launcher3.util.ComponentKey;
import com.android.launcher3.util.LongArrayMap;
import com.android.launcher3.util.ManagedProfileHeuristic;
@@ -203,7 +204,7 @@
public void bindRestoreItemsChange(HashSet<ItemInfo> updates);
public void bindComponentsRemoved(ArrayList<String> packageNames,
ArrayList<AppInfo> appInfos, UserHandleCompat user, int reason);
- public void bindAllPackages(ArrayList<Object> widgetsAndShortcuts);
+ public void bindAllPackages(WidgetsModel model);
public void bindSearchablesChanged();
public boolean isAllAppsButtonRank(int rank);
public void onPageBoundSynchronously(int page);
@@ -968,7 +969,8 @@
break;
}
- folderInfo.title = Utilities.trim(c.getString(titleIndex));
+ // Do not trim the folder label, as is was set by the user.
+ folderInfo.title = c.getString(titleIndex);
folderInfo.id = id;
folderInfo.container = c.getInt(containerIndex);
folderInfo.screenId = c.getInt(screenIndex);
@@ -2110,7 +2112,8 @@
id = c.getLong(idIndex);
FolderInfo folderInfo = findOrMakeFolder(sBgFolders, id);
- folderInfo.title = Utilities.trim(c.getString(titleIndex));
+ // Do not trim the folder label, as is was set by the user.
+ folderInfo.title = c.getString(titleIndex);
folderInfo.id = id;
container = c.getInt(containerIndex);
folderInfo.container = container;
@@ -2754,7 +2757,7 @@
return;
}
}
- updateAllAppsIconsCache();
+ mIconCache.updateDbIcons();
synchronized (LoaderTask.this) {
if (mStopped) {
return;
@@ -2880,68 +2883,6 @@
}
}
- private void updateAllAppsIconsCache() {
- final ArrayList<AppInfo> updatedApps = new ArrayList<>();
-
- for (UserHandleCompat user : mUserManager.getUserProfiles()) {
- // Query for the set of apps
- final List<LauncherActivityInfoCompat> apps = mLauncherApps.getActivityList(null, user);
- // Fail if we don't have any apps
- // TODO: Fix this. Only fail for the current user.
- if (apps == null || apps.isEmpty()) {
- return;
- }
-
- // Update icon cache
- HashSet<String> updatedPackages = mIconCache.updateDBIcons(user, apps);
-
- // If any package icon has changed (app was updated while launcher was dead),
- // update the corresponding shortcuts.
- if (!updatedPackages.isEmpty()) {
- final ArrayList<ShortcutInfo> updatedShortcuts = new ArrayList<>();
- synchronized (sBgLock) {
- for (ItemInfo info : sBgItemsIdMap) {
- if (info instanceof ShortcutInfo && user.equals(info.user)
- && info.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION) {
- ShortcutInfo si = (ShortcutInfo) info;
- ComponentName cn = si.getTargetComponent();
- if (cn != null && updatedPackages.contains(cn.getPackageName())) {
- si.updateIcon(mIconCache);
- updatedShortcuts.add(si);
- }
- }
- }
- mBgAllAppsList.updateIconsAndLabels(updatedPackages, user, updatedApps);
- }
-
- if (!updatedShortcuts.isEmpty()) {
- final UserHandleCompat userFinal = user;
- mHandler.post(new Runnable() {
-
- public void run() {
- Callbacks cb = getCallback();
- if (cb != null) {
- cb.bindShortcutsChanged(updatedShortcuts,
- new ArrayList<ShortcutInfo>(), userFinal);
- }
- }
- });
- }
- }
- }
- if (!updatedApps.isEmpty()) {
- mHandler.post(new Runnable() {
-
- public void run() {
- Callbacks cb = getCallback();
- if (cb != null) {
- cb.bindAppsUpdated(updatedApps);
- }
- }
- });
- }
- }
-
public void dumpState() {
synchronized (sBgLock) {
Log.d(TAG, "mLoaderTask.mContext=" + mContext);
@@ -2952,6 +2893,58 @@
}
}
+ /**
+ * Called when the icons for packages have been updated in the icon cache.
+ */
+ public void onPackageIconsUpdated(HashSet<String> updatedPackages, UserHandleCompat user) {
+ final Callbacks callbacks = getCallback();
+ final ArrayList<AppInfo> updatedApps = new ArrayList<>();
+ final ArrayList<ShortcutInfo> updatedShortcuts = new ArrayList<>();
+
+ // If any package icon has changed (app was updated while launcher was dead),
+ // update the corresponding shortcuts.
+ synchronized (sBgLock) {
+ for (ItemInfo info : sBgItemsIdMap) {
+ if (info instanceof ShortcutInfo && user.equals(info.user)
+ && info.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION) {
+ ShortcutInfo si = (ShortcutInfo) info;
+ ComponentName cn = si.getTargetComponent();
+ if (cn != null && updatedPackages.contains(cn.getPackageName())) {
+ si.updateIcon(mIconCache);
+ updatedShortcuts.add(si);
+ }
+ }
+ }
+ mBgAllAppsList.updateIconsAndLabels(updatedPackages, user, updatedApps);
+ }
+
+ if (!updatedShortcuts.isEmpty()) {
+ final UserHandleCompat userFinal = user;
+ mHandler.post(new Runnable() {
+
+ public void run() {
+ Callbacks cb = getCallback();
+ if (cb != null && callbacks == cb) {
+ cb.bindShortcutsChanged(updatedShortcuts,
+ new ArrayList<ShortcutInfo>(), userFinal);
+ }
+ }
+ });
+ }
+
+ if (!updatedApps.isEmpty()) {
+ mHandler.post(new Runnable() {
+
+ public void run() {
+ Callbacks cb = getCallback();
+ if (cb != null && callbacks == cb) {
+ cb.bindAppsUpdated(updatedApps);
+ }
+ }
+ });
+ }
+ }
+
void enqueuePackageUpdated(PackageUpdatedTask task) {
sWorker.post(task);
}
@@ -3344,18 +3337,19 @@
runOnWorkerThread(new Runnable(){
@Override
public void run() {
- final ArrayList<Object> list = getWidgetsAndShortcuts(context, refresh);
+ final WidgetsModel model = createWidgetsModel(context, refresh);
mHandler.post(new Runnable() {
@Override
public void run() {
Callbacks cb = getCallback();
if (callbacks == cb && cb != null) {
- callbacks.bindAllPackages(list);
+ callbacks.bindAllPackages(model);
}
}
});
// update the Widget entries inside DB on the worker thread.
- LauncherAppState.getInstance().getWidgetCache().removeObsoletePreviews(list);
+ LauncherAppState.getInstance().getWidgetCache().removeObsoletePreviews(
+ model.getRawList());
}
});
}
@@ -3365,13 +3359,15 @@
*
* @see #loadAndBindWidgetsAndShortcuts
*/
- private ArrayList<Object> getWidgetsAndShortcuts(Context context, boolean refresh) {
+ private WidgetsModel createWidgetsModel(Context context, boolean refresh) {
PackageManager packageManager = context.getPackageManager();
final ArrayList<Object> widgetsAndShortcuts = new ArrayList<Object>();
widgetsAndShortcuts.addAll(getWidgetProviders(context, refresh));
Intent shortcutsIntent = new Intent(Intent.ACTION_CREATE_SHORTCUT);
widgetsAndShortcuts.addAll(packageManager.queryIntentActivities(shortcutsIntent, 0));
- return widgetsAndShortcuts;
+ WidgetsModel model = new WidgetsModel(context);
+ model.addWidgetsAndShortcuts(widgetsAndShortcuts);
+ return model;
}
@Thunk static boolean isPackageDisabled(Context context, String packageName,
diff --git a/src/com/android/launcher3/PagedView.java b/src/com/android/launcher3/PagedView.java
index 62f1bc9..dda9a16 100644
--- a/src/com/android/launcher3/PagedView.java
+++ b/src/com/android/launcher3/PagedView.java
@@ -633,9 +633,6 @@
if (mCurrentPage != getNextPage()) {
AccessibilityEvent ev =
AccessibilityEvent.obtain(AccessibilityEvent.TYPE_VIEW_SCROLLED);
- ev.setItemCount(getChildCount());
- ev.setFromIndex(getNextPage());
- ev.setToIndex(getNextPage());
sendAccessibilityEventUnchecked(ev);
}
@@ -912,8 +909,7 @@
pageGap = getPaddingRight();
}
- childLeft += childWidth + pageGap
- + (lp.isFullScreenPage ? 0 : (getPaddingLeft() + getPaddingRight()));
+ childLeft += childWidth + pageGap + getChildGap();
}
}
@@ -961,6 +957,10 @@
}
}
+ protected int getChildGap() {
+ return 0;
+ }
+
private void updateMaxScrollX() {
int childCount = getChildCount();
if (childCount > 0) {
diff --git a/src/com/android/launcher3/UninstallDropTarget.java b/src/com/android/launcher3/UninstallDropTarget.java
index 0fc8f32..4199390 100644
--- a/src/com/android/launcher3/UninstallDropTarget.java
+++ b/src/com/android/launcher3/UninstallDropTarget.java
@@ -28,7 +28,7 @@
// Get the hover color
mHoverColor = getResources().getColor(R.color.uninstall_target_hover_tint);
- setDrawable(R.drawable.uninstall_target_selector);
+ setDrawable(R.drawable.ic_launcher_uninstall_normal);
}
@Override
diff --git a/src/com/android/launcher3/Utilities.java b/src/com/android/launcher3/Utilities.java
index 6734fdc..1f8a6f2 100644
--- a/src/com/android/launcher3/Utilities.java
+++ b/src/com/android/launcher3/Utilities.java
@@ -44,6 +44,7 @@
import android.graphics.drawable.Drawable;
import android.graphics.drawable.PaintDrawable;
import android.os.Build;
+import android.os.Process;
import android.util.Log;
import android.util.Pair;
import android.util.SparseArray;
@@ -57,6 +58,8 @@
import java.util.regex.Matcher;
import java.util.regex.Pattern;
+import junit.framework.Assert;
+
/**
* Various utilities shared amongst the Launcher's classes.
*/
@@ -641,4 +644,10 @@
return (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) &&
(res.getConfiguration().getLayoutDirection() == View.LAYOUT_DIRECTION_RTL);
}
+
+ public static void assertWorkerThread() {
+ if (LauncherAppState.isDogfoodBuild()) {
+ Assert.assertTrue(LauncherModel.sWorkerThread.getThreadId() == Process.myTid());
+ }
+ }
}
diff --git a/src/com/android/launcher3/WidgetPreviewLoader.java b/src/com/android/launcher3/WidgetPreviewLoader.java
index 8459673..5ee1f26 100644
--- a/src/com/android/launcher3/WidgetPreviewLoader.java
+++ b/src/com/android/launcher3/WidgetPreviewLoader.java
@@ -24,17 +24,16 @@
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.os.AsyncTask;
+import android.os.Handler;
import android.os.Process;
import android.util.Log;
import android.util.LongSparseArray;
-
import com.android.launcher3.compat.AppWidgetManagerCompat;
import com.android.launcher3.compat.UserHandleCompat;
import com.android.launcher3.compat.UserManagerCompat;
import com.android.launcher3.util.ComponentKey;
import com.android.launcher3.util.Thunk;
import com.android.launcher3.widget.WidgetCell;
-
import junit.framework.Assert;
import java.util.ArrayList;
@@ -69,6 +68,7 @@
private final CacheDb mDb;
private final MainThreadExecutor mMainThreadExecutor = new MainThreadExecutor();
+ private final Handler mWorkerHandler;
public WidgetPreviewLoader(Context context, IconCache iconCache) {
mContext = context;
@@ -76,6 +76,7 @@
mManager = AppWidgetManagerCompat.getInstance(context);
mUserManager = UserManagerCompat.getInstance(context);
mDb = new CacheDb(context);
+ mWorkerHandler = new Handler(LauncherModel.getWorkerLooper());
}
/**
@@ -83,8 +84,6 @@
* called on UI thread
*
* @param o either {@link LauncherAppWidgetProviderInfo} or {@link ResolveInfo}
- * @param immediateResult A bitmap array of size 1. If the result is already cached, it is
- * set to the final result.
* @return a request id which can be used to cancel the request.
*/
public PreviewLoadRequest getPreview(final Object o, int previewWidth, int previewHeight,
@@ -207,8 +206,7 @@
* This ensures that we remove entries for packages which changed while the launcher was dead.
*/
public void removeObsoletePreviews(ArrayList<Object> list) {
- // This method should always be called from the worker thread.
- Assert.assertTrue(LauncherModel.sWorkerThread.getThreadId() == Process.myTid());
+ Utilities.assertWorkerThread();
LongSparseArray<UserHandleCompat> userIdCache = new LongSparseArray<>();
LongSparseArray<HashSet<String>> validPackages = new LongSparseArray<>();
@@ -289,7 +287,10 @@
}
}
- private Bitmap readFromDb(WidgetCacheKey key, Bitmap recycle) {
+ /**
+ * Reads the preview bitmap from the DB or null if the preview is not in the DB.
+ */
+ private Bitmap readFromDb(WidgetCacheKey key, Bitmap recycle, PreviewLoadTask loadTask) {
Cursor cursor = null;
try {
cursor = mDb.getReadableDatabase().query(
@@ -302,12 +303,18 @@
key.size
},
null, null, null);
+ // If cancelled, skip getting the blob and decoding it into a bitmap
+ if (loadTask.isCancelled()) {
+ return null;
+ }
if (cursor.moveToNext()) {
byte[] blob = cursor.getBlob(0);
BitmapFactory.Options opts = new BitmapFactory.Options();
opts.inBitmap = recycle;
try {
- return BitmapFactory.decodeByteArray(blob, 0, blob.length, opts);
+ if (!loadTask.isCancelled()) {
+ return BitmapFactory.decodeByteArray(blob, 0, blob.length, opts);
+ }
} catch (Exception e) {
return null;
}
@@ -545,15 +552,17 @@
mTask.cancel(true);
}
- if (mTask.mBitmap == null) {
- return;
+ // This only handles the case where the PreviewLoadTask is cancelled after the task has
+ // successfully completed (including having written to disk when necessary). In the
+ // other cases where it is cancelled while the task is running, it will be cleaned up
+ // in the tasks's onCancelled() call, and if cancelled while the task is writing to
+ // disk, it will be cancelled in the task's onPostExecute() call.
+ if (mTask.mBitmapToRecycle != null) {
+ synchronized (mUnusedBitmaps) {
+ mUnusedBitmaps.add(mTask.mBitmapToRecycle);
+ }
+ mTask.mBitmapToRecycle = null;
}
-
- // The preview is no longer bound to any view, move it to {@link WeakReference} list.
- synchronized(mUnusedBitmaps) {
- mUnusedBitmaps.add(mTask.mBitmap);
- }
- mTask.mBitmap = null;
}
}
@@ -564,7 +573,8 @@
private final int mPreviewHeight;
private final int mPreviewWidth;
private final WidgetCell mCaller;
- private Bitmap mBitmap;
+ private long[] mVersions;
+ private Bitmap mBitmapToRecycle;
PreviewLoadTask(WidgetCacheKey key, Object info, int previewWidth,
int previewHeight, WidgetCell caller) {
@@ -584,6 +594,10 @@
Bitmap unusedBitmap = null;
synchronized (mUnusedBitmaps) {
+ // If already cancelled before this gets to run in the background, then return early
+ if (isCancelled()) {
+ return null;
+ }
// Check if we can use a bitmap
for (Bitmap candidate : mUnusedBitmaps) {
if (candidate != null && candidate.isMutable() &&
@@ -600,31 +614,64 @@
mUnusedBitmaps.remove(unusedBitmap);
}
}
+ // If cancelled now, don't bother reading the preview from the DB
if (isCancelled()) {
- return null;
+ return unusedBitmap;
}
- Bitmap preview = readFromDb(mKey, unusedBitmap);
+ Bitmap preview = readFromDb(mKey, unusedBitmap, this);
+ // Only consider generating the preview if we have not cancelled the task already
if (!isCancelled() && preview == null) {
// Fetch the version info before we generate the preview, so that, in-case the
// app was updated while we are generating the preview, we use the old version info,
// which would gets re-written next time.
- long[] versions = getPackageVersion(mKey.componentName.getPackageName());
+ mVersions = getPackageVersion(mKey.componentName.getPackageName());
// it's not in the db... we need to generate it
preview = generatePreview(mInfo, unusedBitmap, mPreviewWidth, mPreviewHeight);
-
- if (!isCancelled()) {
- writeToDb(mKey, versions, preview);
- }
}
-
return preview;
}
@Override
- protected void onPostExecute(Bitmap result) {
- mBitmap = result;
- mCaller.applyPreview(result);
+ protected void onPostExecute(final Bitmap preview) {
+ mCaller.applyPreview(preview);
+
+ // Write the generated preview to the DB in the worker thread
+ if (mVersions != null) {
+ mWorkerHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ if (!isCancelled()) {
+ // If we are still using this preview, then write it to the DB and then
+ // let the normal clear mechanism recycle the bitmap
+ writeToDb(mKey, mVersions, preview);
+ mBitmapToRecycle = preview;
+ } else {
+ // If we've already cancelled, then skip writing the bitmap to the DB
+ // and manually add the bitmap back to the recycled set
+ synchronized (mUnusedBitmaps) {
+ mUnusedBitmaps.add(preview);
+ }
+ }
+ }
+ });
+ } else {
+ // If we don't need to write to disk, then ensure the preview gets recycled by
+ // the normal clear mechanism
+ mBitmapToRecycle = preview;
+ }
+ }
+
+ @Override
+ protected void onCancelled(Bitmap preview) {
+ // If we've cancelled while the task is running, then can return the bitmap to the
+ // recycled set immediately. Otherwise, it will be recycled after the preview is written
+ // to disk.
+ if (preview != null) {
+ synchronized (mUnusedBitmaps) {
+ mUnusedBitmaps.add(preview);
+ }
+ }
}
}
diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java
index 8c1c7d6..55742f6 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -89,10 +89,6 @@
Insettable, UninstallSource, AccessibilityDragSource {
private static final String TAG = "Launcher.Workspace";
- private static final int CHILDREN_OUTLINE_FADE_OUT_DELAY = 0;
- private static final int CHILDREN_OUTLINE_FADE_OUT_DURATION = 375;
- private static final int CHILDREN_OUTLINE_FADE_IN_DURATION = 100;
-
protected static final int SNAP_OFF_EMPTY_SCREEN_DURATION = 400;
protected static final int FADE_EMPTY_SCREEN_DURATION = 150;
@@ -101,11 +97,6 @@
static final boolean MAP_NO_RECURSE = false;
static final boolean MAP_RECURSE = true;
- // These animators are used to fade the children's outlines
- private ObjectAnimator mChildrenOutlineFadeInAnimation;
- private ObjectAnimator mChildrenOutlineFadeOutAnimation;
- private float mChildrenOutlineAlpha = 0;
-
private static final long CUSTOM_CONTENT_GESTURE_DELAY = 200;
private long mTouchDownTime = -1;
private long mCustomContentShowTime = -1;
@@ -388,7 +379,6 @@
updateChildrenLayersEnabled(false);
mLauncher.lockScreenOrientation();
mLauncher.onInteractionBegin();
- setChildrenBackgroundAlphaMultipliers(1f);
// Prevent any Un/InstallShortcutReceivers from updating the db while we are dragging
InstallShortcutReceiver.enableInstallQueue();
post(new Runnable() {
@@ -578,7 +568,6 @@
public void createCustomContentContainer() {
CellLayout customScreen = (CellLayout)
mLauncher.getLayoutInflater().inflate(R.layout.workspace_screen, this, false);
- customScreen.disableBackground();
customScreen.disableDragTarget();
mWorkspaceScreens.put(CUSTOM_CONTENT_SCREEN_ID, customScreen);
@@ -1533,45 +1522,12 @@
}
}
- void showOutlines() {
- if (!workspaceInModalState() && !mIsSwitchingState) {
- if (mChildrenOutlineFadeOutAnimation != null) mChildrenOutlineFadeOutAnimation.cancel();
- if (mChildrenOutlineFadeInAnimation != null) mChildrenOutlineFadeInAnimation.cancel();
- mChildrenOutlineFadeInAnimation = LauncherAnimUtils.ofFloat(this, "childrenOutlineAlpha", 1.0f);
- mChildrenOutlineFadeInAnimation.setDuration(CHILDREN_OUTLINE_FADE_IN_DURATION);
- mChildrenOutlineFadeInAnimation.start();
- }
- }
-
- void hideOutlines() {
- if (!workspaceInModalState() && !mIsSwitchingState) {
- if (mChildrenOutlineFadeInAnimation != null) mChildrenOutlineFadeInAnimation.cancel();
- if (mChildrenOutlineFadeOutAnimation != null) mChildrenOutlineFadeOutAnimation.cancel();
- mChildrenOutlineFadeOutAnimation = LauncherAnimUtils.ofFloat(this, "childrenOutlineAlpha", 0.0f);
- mChildrenOutlineFadeOutAnimation.setDuration(CHILDREN_OUTLINE_FADE_OUT_DURATION);
- mChildrenOutlineFadeOutAnimation.setStartDelay(CHILDREN_OUTLINE_FADE_OUT_DELAY);
- mChildrenOutlineFadeOutAnimation.start();
- }
- }
-
public void showOutlinesTemporarily() {
if (!mIsPageMoving && !isTouchActive()) {
snapToPage(mCurrentPage);
}
}
- public void setChildrenOutlineAlpha(float alpha) {
- mChildrenOutlineAlpha = alpha;
- for (int i = 0; i < getChildCount(); i++) {
- CellLayout cl = (CellLayout) getChildAt(i);
- cl.setBackgroundAlpha(alpha);
- }
- }
-
- public float getChildrenOutlineAlpha() {
- return mChildrenOutlineAlpha;
- }
-
float backgroundAlphaInterpolator(float r) {
float pivotA = 0.1f;
float pivotB = 0.4f;
@@ -1601,13 +1557,6 @@
}
}
- private void setChildrenBackgroundAlphaMultipliers(float a) {
- for (int i = 0; i < getChildCount(); i++) {
- CellLayout child = (CellLayout) getChildAt(i);
- child.setBackgroundAlphaMultiplier(a);
- }
- }
-
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
@Override
public void enableAccessibleDrag(boolean enable) {
@@ -2004,7 +1953,6 @@
public void onStartReordering() {
super.onStartReordering();
- showOutlines();
// Reordering handles its own animations, disable the automatic ones.
disableLayoutTransitions();
}
@@ -2017,7 +1965,6 @@
return;
}
- hideOutlines();
mScreenOrder.clear();
int count = getChildCount();
for (int i = 0; i < count; i++) {
@@ -2969,9 +2916,6 @@
mSpringLoadedDragController.cancel();
- if (!mIsPageMoving) {
- hideOutlines();
- }
mLauncher.getDragLayer().hidePageHints();
}
diff --git a/src/com/android/launcher3/widget/PackageItemInfo.java b/src/com/android/launcher3/model/PackageItemInfo.java
similarity index 97%
rename from src/com/android/launcher3/widget/PackageItemInfo.java
rename to src/com/android/launcher3/model/PackageItemInfo.java
index 8f45a77..0f0134a 100644
--- a/src/com/android/launcher3/widget/PackageItemInfo.java
+++ b/src/com/android/launcher3/model/PackageItemInfo.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.launcher3.widget;
+package com.android.launcher3.model;
import android.content.ComponentName;
import android.graphics.Bitmap;
diff --git a/src/com/android/launcher3/model/WidgetsAndShortcutNameComparator.java b/src/com/android/launcher3/model/WidgetsAndShortcutNameComparator.java
index 7c4e806..61e8952 100644
--- a/src/com/android/launcher3/model/WidgetsAndShortcutNameComparator.java
+++ b/src/com/android/launcher3/model/WidgetsAndShortcutNameComparator.java
@@ -1,6 +1,5 @@
package com.android.launcher3.model;
-import android.appwidget.AppWidgetProviderInfo;
import android.content.Context;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
@@ -8,6 +7,7 @@
import com.android.launcher3.LauncherAppWidgetProviderInfo;
import com.android.launcher3.Utilities;
import com.android.launcher3.compat.AppWidgetManagerCompat;
+import com.android.launcher3.compat.UserHandleCompat;
import java.text.Collator;
import java.util.Comparator;
@@ -18,12 +18,14 @@
private final PackageManager mPackageManager;
private final HashMap<Object, String> mLabelCache;
private final Collator mCollator;
+ private final UserHandleCompat mMainHandle;
public WidgetsAndShortcutNameComparator(Context context) {
mManager = AppWidgetManagerCompat.getInstance(context);
mPackageManager = context.getPackageManager();
mLabelCache = new HashMap<Object, String>();
mCollator = Collator.getInstance();
+ mMainHandle = UserHandleCompat.myUserHandle();
}
@Override
@@ -45,19 +47,22 @@
: Utilities.trim(((ResolveInfo) b).loadLabel(mPackageManager));
mLabelCache.put(b, labelB);
}
- int result = mCollator.compare(labelA, labelB);
- if (result == 0 && a instanceof AppWidgetProviderInfo &&
- b instanceof AppWidgetProviderInfo) {
- AppWidgetProviderInfo aInfo = (AppWidgetProviderInfo) a;
- AppWidgetProviderInfo bInfo = (AppWidgetProviderInfo) b;
- // prioritize main user's widgets against work profile widgets.
- if (aInfo.getProfile().equals(android.os.Process.myUserHandle())) {
- return -1;
- } else if (bInfo.getProfile().equals(android.os.Process.myUserHandle())) {
- return 1;
- }
+ // Currently, there is no work profile shortcuts, hence only considering the widget cases.
+
+ boolean aWorkProfile = (a instanceof LauncherAppWidgetProviderInfo) &&
+ !mMainHandle.equals(mManager.getUser((LauncherAppWidgetProviderInfo) a));
+ boolean bWorkProfile = (b instanceof LauncherAppWidgetProviderInfo) &&
+ !mMainHandle.equals(mManager.getUser((LauncherAppWidgetProviderInfo) b));
+
+ // Independent of how the labels compare, if only one of the two widget info belongs to
+ // work profile, put that one in the back.
+ if (aWorkProfile && !bWorkProfile) {
+ return 1;
}
- return result;
+ if (!aWorkProfile && bWorkProfile) {
+ return -1;
+ }
+ return mCollator.compare(labelA, labelB);
}
};
diff --git a/src/com/android/launcher3/widget/WidgetsModel.java b/src/com/android/launcher3/model/WidgetsModel.java
similarity index 81%
rename from src/com/android/launcher3/widget/WidgetsModel.java
rename to src/com/android/launcher3/model/WidgetsModel.java
index 5a920e8..b72b981 100644
--- a/src/com/android/launcher3/widget/WidgetsModel.java
+++ b/src/com/android/launcher3/model/WidgetsModel.java
@@ -1,18 +1,19 @@
-package com.android.launcher3.widget;
+package com.android.launcher3.model;
import android.content.Context;
-import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
-import android.support.v7.widget.RecyclerView;
+import android.os.Handler;
+import android.os.Process;
import android.util.Log;
import com.android.launcher3.IconCache;
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.LauncherAppWidgetProviderInfo;
+
+import com.android.launcher3.LauncherModel;
+import com.android.launcher3.Utilities;
import com.android.launcher3.compat.UserHandleCompat;
-import com.android.launcher3.model.AppNameComparator;
-import com.android.launcher3.model.WidgetsAndShortcutNameComparator;
import java.util.ArrayList;
import java.util.Collections;
@@ -37,19 +38,19 @@
/* Map of widgets and shortcuts that are tracked per package. */
private Map<PackageItemInfo, ArrayList<Object>> mWidgetsList = new HashMap<>();
- /* Notifies the adapter when data changes. */
- private RecyclerView.Adapter mAdapter;
+ private ArrayList<Object> mRawList;
- private Comparator mWidgetAndShortcutNameComparator;
- private Comparator mAppNameComparator;
+ private final Comparator mWidgetAndShortcutNameComparator;
+ private final Comparator mAppNameComparator;
- private IconCache mIconCache;
+ private final IconCache mIconCache;
+ private final Handler mWorkerHandler;
- public WidgetsModel(Context context, RecyclerView.Adapter adapter) {
- mAdapter = adapter;
+ public WidgetsModel(Context context) {
mWidgetAndShortcutNameComparator = new WidgetsAndShortcutNameComparator(context);
mAppNameComparator = (new AppNameComparator(context)).getAppInfoComparator();
mIconCache = LauncherAppState.getInstance().getIconCache();
+ mWorkerHandler = new Handler(LauncherModel.getWorkerLooper());
}
// Access methods that may be deleted if the private fields are made package-private.
@@ -66,9 +67,15 @@
return mWidgetsList.get(mPackageItemInfos.get(pos));
}
- public void addWidgetsAndShortcuts(ArrayList<Object> widgetsShortcuts, PackageManager pm) {
+ public ArrayList<Object> getRawList() {
+ return mRawList;
+ }
+
+ public void addWidgetsAndShortcuts(ArrayList<Object> rawWidgetsShortcuts) {
+ Utilities.assertWorkerThread();
+ mRawList = rawWidgetsShortcuts;
if (DEBUG) {
- Log.d(TAG, "addWidgetsAndShortcuts, widgetsShortcuts#=" + widgetsShortcuts.size());
+ Log.d(TAG, "addWidgetsAndShortcuts, widgetsShortcuts#=" + rawWidgetsShortcuts.size());
}
// Temporary list for {@link PackageItemInfos} to avoid having to go through
@@ -80,7 +87,7 @@
mPackageItemInfos.clear();
// add and update.
- for (Object o: widgetsShortcuts) {
+ for (Object o: rawWidgetsShortcuts) {
String packageName = "";
if (o instanceof LauncherAppWidgetProviderInfo) {
LauncherAppWidgetProviderInfo widgetInfo = (LauncherAppWidgetProviderInfo) o;
@@ -100,10 +107,9 @@
} else {
widgetsShortcutsList = new ArrayList<Object>();
widgetsShortcutsList.add(o);
-
pInfo = new PackageItemInfo(packageName);
mIconCache.getTitleAndIconForApp(packageName, UserHandleCompat.myUserHandle(),
- true /* useLowResIcon */, pInfo);
+ true /* userLowResIcon */, pInfo);
mWidgetsList.put(pInfo, widgetsShortcutsList);
tmpPackageItemInfos.put(packageName, pInfo);
mPackageItemInfos.add(pInfo);
@@ -115,8 +121,5 @@
for (PackageItemInfo p: mPackageItemInfos) {
Collections.sort(mWidgetsList.get(p), mWidgetAndShortcutNameComparator);
}
-
- // notify.
- mAdapter.notifyDataSetChanged();
}
}
\ No newline at end of file
diff --git a/src/com/android/launcher3/widget/WidgetsContainerView.java b/src/com/android/launcher3/widget/WidgetsContainerView.java
index 181c08a..5a879fa 100644
--- a/src/com/android/launcher3/widget/WidgetsContainerView.java
+++ b/src/com/android/launcher3/widget/WidgetsContainerView.java
@@ -17,7 +17,6 @@
package com.android.launcher3.widget;
import android.content.Context;
-import android.content.pm.PackageManager;
import android.graphics.Bitmap;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
@@ -36,6 +35,7 @@
import com.android.launcher3.DragController;
import com.android.launcher3.DragSource;
import com.android.launcher3.DropTarget.DragObject;
+import com.android.launcher3.model.WidgetsModel;
import com.android.launcher3.Folder;
import com.android.launcher3.IconCache;
import com.android.launcher3.ItemInfo;
@@ -47,8 +47,6 @@
import com.android.launcher3.WidgetPreviewLoader;
import com.android.launcher3.Workspace;
-import java.util.ArrayList;
-
/**
* The widgets list view container.
*/
@@ -68,9 +66,6 @@
private DragController mDragController;
private IconCache mIconCache;
- /* Data model for the widget */
- private WidgetsModel mWidgets;
-
/* Recycler view related member variables */
private RecyclerView mView;
private WidgetsListAdapter mAdapter;
@@ -98,8 +93,6 @@
mDragController = mLauncher.getDragController();
mWidgetHostViewLoader = new WidgetHostViewLoader(mLauncher);
mAdapter = new WidgetsListAdapter(context, this, this, mLauncher);
- mWidgets = new WidgetsModel(context, mAdapter);
- mAdapter.setWidgetsModel(mWidgets);
mIconCache = (LauncherAppState.getInstance()).getIconCache();
if (DEBUG) {
@@ -109,10 +102,6 @@
@Override
protected void onFinishInflate() {
- if (DEBUG) {
- Log.d(TAG, String.format("onFinishInflate [widgets size=%d]",
- mWidgets.getPackageSize()));
- }
mView = (RecyclerView) findViewById(R.id.widgets_list_view);
mView.setAdapter(mAdapter);
@@ -135,7 +124,7 @@
//
public View getContentView() {
- return findViewById(R.id.widgets_content);
+ return mView;
}
public View getRevealView() {
@@ -145,10 +134,6 @@
public void scrollToTop() {
mView.scrollToPosition(0);
- if (DEBUG) {
- Log.d(TAG, String.format("scrollToTop, [widgets size=%d]",
- mWidgets.getPackageSize()));
- }
}
//
@@ -374,8 +359,9 @@
/**
* Initialize the widget data model.
*/
- public void addWidgets(ArrayList<Object> widgetsShortcuts, PackageManager pm) {
- mWidgets.addWidgetsAndShortcuts(widgetsShortcuts, pm);
+ public void addWidgets(WidgetsModel model) {
+ mAdapter.setWidgetsModel(model);
+ mAdapter.notifyDataSetChanged();
}
private WidgetPreviewLoader getWidgetPreviewLoader() {
diff --git a/src/com/android/launcher3/widget/WidgetsListAdapter.java b/src/com/android/launcher3/widget/WidgetsListAdapter.java
index 2f733dc..918ec1b 100644
--- a/src/com/android/launcher3/widget/WidgetsListAdapter.java
+++ b/src/com/android/launcher3/widget/WidgetsListAdapter.java
@@ -38,6 +38,8 @@
import com.android.launcher3.LauncherAppWidgetProviderInfo;
import com.android.launcher3.R;
import com.android.launcher3.WidgetPreviewLoader;
+import com.android.launcher3.model.PackageItemInfo;
+import com.android.launcher3.model.WidgetsModel;
import java.util.List;
@@ -183,7 +185,6 @@
}
}
- @Override
public boolean onFailedToRecycleView(WidgetsRowViewHolder holder) {
// If child views are animating, then the RecyclerView may choose not to recycle the view,
// causing extraneous onCreateViewHolder() calls. It is safe in this case to continue