summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorArtem Shvadskiy <ashvadskiy@cyngn.com>2015-12-03 00:05:01 (GMT)
committerArtem Shvadskiy <ashvadskiy@cyngn.com>2015-12-21 20:04:03 (GMT)
commitb7121babd1abedc085244a5145052ad453ce86a6 (patch)
tree30cd03d013c41ccaaf4a5de3feccfff7d2aa0082
parentacf61e0834d2c0f605a3abc6fde86f0866a8b5a2 (diff)
downloadandroid_packages_apps_Trebuchet-b7121babd1abedc085244a5145052ad453ce86a6.zip
android_packages_apps_Trebuchet-b7121babd1abedc085244a5145052ad453ce86a6.tar.gz
android_packages_apps_Trebuchet-b7121babd1abedc085244a5145052ad453ce86a6.tar.bz2
Add remote apps to application drawer.
Patch 2: 1. Use isRemote() method instead of checking flag manually. 2. Launch remote app directly through intent, bypassing regular app logic. 3. Pull syncing code out of LauncherModel and into RemoteFolderManager. 4. Check setting before loading apps. 5. Delete old apps before loading new ones. 6. Add/remove directly from AppDrawerListAdapter, since we are not sure if we want remote apps to show up in Google-style paged drawer. 7. Get number of columns for drawer, so we always load just enough to fill one row. Patch 3: Remove old ArrayList from LauncherModel. Patch 4: 1. Update redlines. 2. Clean up layout/dimens. 3. Add custom scrubber icon for remote apps. 4. Apply custom styles to remote header. TODO: Implement info icon click event, and replace remote apps based on updater cache status. Patch 5: Clean up app_drawer_item.xml Patch 6: Rebase. Patch 7: 1. Add forgotten rebase method to RemoteFolderManager stub. 2. Apply default styles to app_drawer_item and don't bother checking to apply styles unless remote folder is enabled. 3. Minor redline fix. Patch 8: Add larger bottom margin to remote header. Patch 9: 1. Create new view type for custom header to avoid on click bug. 2. Refactor custom style to apply on view holder creation, instead of on bind. 3. Add click event for custom header icon. Change-Id: If123d7ac82a0af0fa171fa3408b6c2f1f8a48b7b issue-id: CYNGNOS-1380
-rw-r--r--RemoteFolder/src/com/android/launcher3/RemoteFolderManager.java10
-rw-r--r--res/drawable/triangle_icon.pngbin0 -> 12557 bytes
-rw-r--r--res/layout/app_drawer_item.xml78
-rw-r--r--res/values/cm_strings.xml1
-rw-r--r--res/values/colors.xml6
-rw-r--r--res/values/dimens.xml34
-rw-r--r--src/com/android/launcher3/AppDrawerIconView.java1
-rw-r--r--src/com/android/launcher3/AppDrawerListAdapter.java364
-rw-r--r--src/com/android/launcher3/AppDrawerScrubberSections.java11
-rw-r--r--src/com/android/launcher3/AppInfo.java34
-rw-r--r--src/com/android/launcher3/AutoFitTextView.java8
-rw-r--r--src/com/android/launcher3/Launcher.java20
-rw-r--r--src/com/android/launcher3/LauncherModel.java2
13 files changed, 335 insertions, 234 deletions
diff --git a/RemoteFolder/src/com/android/launcher3/RemoteFolderManager.java b/RemoteFolder/src/com/android/launcher3/RemoteFolderManager.java
index 477f710..c93b833 100644
--- a/RemoteFolder/src/com/android/launcher3/RemoteFolderManager.java
+++ b/RemoteFolder/src/com/android/launcher3/RemoteFolderManager.java
@@ -24,4 +24,14 @@ public class RemoteFolderManager {
* Called when the remote folder is dropped into the delete area on the workspace.
*/
public void onFolderDeleted() { }
+
+ /**
+ * Called when the app drawer is opened.
+ */
+ public void onAppDrawerOpened() { }
+
+ /**
+ * Called when the info icon is clicked
+ */
+ public void onInfoIconClicked() { }
}
diff --git a/res/drawable/triangle_icon.png b/res/drawable/triangle_icon.png
new file mode 100644
index 0000000..209cf85
--- /dev/null
+++ b/res/drawable/triangle_icon.png
Binary files differ
diff --git a/res/layout/app_drawer_item.xml b/res/layout/app_drawer_item.xml
index 7322b73..d8b3eb4 100644
--- a/res/layout/app_drawer_item.xml
+++ b/res/layout/app_drawer_item.xml
@@ -15,57 +15,81 @@
limitations under the License.
-->
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:autofit="http://schemas.android.com/apk/res-auto"
- android:splitMotionEvents="false"
- android:layout_width="match_parent"
- android:layout_height="wrap_content">
+ xmlns:autofit="http://schemas.android.com/apk/res-auto"
+ android:splitMotionEvents="false"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content">
+
+ <!-- Layout in back to front render order -->
<View
android:id="@+id/fading_background_back"
android:alpha="0"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
- android:layout_alignTop="@+id/drawer_item_flow"
+ android:layout_alignParentTop="true"
android:layout_alignBottom="@+id/drawer_item_flow"
android:background="@color/app_drawer_drag_background" />
- <!-- Layout in back to front render order -->
+ <TextView
+ android:id="@+id/custom_title"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textColor="@android:color/white"
+ android:textSize="@dimen/drawer_custom_title_text_size"
+ android:layout_marginStart="@dimen/drawer_custom_title_margin_start"
+ android:layout_marginTop="@dimen/drawer_custom_title_margin_top"
+ android:includeFontPadding="false"
+ android:fontFamily="roboto-light"
+ android:text="@string/recommendations_title"
+ android:visibility="gone" />
+
+ <ImageView
+ android:id="@+id/custom_title_icon"
+ android:layout_width="@dimen/drawer_custom_icon_size"
+ android:layout_height="@dimen/drawer_custom_icon_size"
+ android:layout_marginEnd="@dimen/drawer_custom_icon_margin_end"
+ android:layout_marginTop="@dimen/drawer_custom_icon_margin_top"
+ android:layout_alignParentEnd="true"
+ android:src="@drawable/triangle_icon"
+ android:visibility="gone" />
+
<LinearLayout
- android:id="@+id/drawer_item_flow"
- android:layout_alignParentEnd="true"
- android:layout_toEndOf="@+id/drawer_item_title"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="horizontal" />
+ android:id="@+id/drawer_item_flow"
+ android:layout_alignParentEnd="true"
+ android:layout_toEndOf="@+id/drawer_item_title"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_below="@id/custom_title"
+ android:orientation="horizontal" />
<View
android:id="@+id/fading_background_front"
android:alpha="0"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
- android:layout_alignTop="@id/drawer_item_flow"
+ android:layout_alignParentTop="true"
android:layout_alignBottom="@id/drawer_item_flow"
android:background="@color/app_drawer_drag_background" />
<com.android.launcher3.AutoFitTextView
- android:id="@+id/drawer_item_title"
- android:layout_width="@dimen/app_drawer_char_width"
- android:layout_height="fill_parent"
- android:layout_marginTop="6dp"
- android:layout_marginBottom="24dp"
- android:layout_marginStart="6dp"
- android:layout_alignTop="@id/drawer_item_flow"
+ android:id="@id/drawer_item_title"
+ android:layout_width="@dimen/drawer_header_text_char_width"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="@dimen/drawer_header_text_margin_top_default"
+ android:layout_marginBottom="@dimen/drawer_header_text_margin_bottom"
+ android:layout_marginStart="@dimen/drawer_header_text_margin_start"
+ android:layout_alignTop="@id/custom_title"
+ android:layout_alignWithParentIfMissing="true"
android:layout_alignBottom="@id/drawer_item_flow"
- android:layout_centerVertical="true"
android:includeFontPadding="false"
android:gravity="center"
android:singleLine="true"
- autofit:minTextSize="8sp"
- android:textSize="24sp"
- android:layout_gravity="center"
+ autofit:minTextSize="@dimen/drawer_header_text_min_text_size"
+ android:textSize="@dimen/drawer_header_text_size_default"
android:fontFamily="sans-serif-light"
android:textColor="@android:color/white"
- android:shadowRadius="4.0"
- android:shadowDy="2"
- android:shadowColor="#b0000000"/>
+ android:shadowRadius="@dimen/drawer_header_text_shadow_radius"
+ android:shadowDy="@dimen/drawer_header_text_shadow_dy"
+ android:shadowColor="@color/drawer_header_text_shadow"/>
</RelativeLayout>
diff --git a/res/values/cm_strings.xml b/res/values/cm_strings.xml
index a235a2a..8235834 100644
--- a/res/values/cm_strings.xml
+++ b/res/values/cm_strings.xml
@@ -93,4 +93,5 @@
<string name="recommendations_help_text">This is a dynamic folder that will automatically load with recommended and featured apps from us at Cyanogen Inc. If you would not like to receive these awesome offers, simply disable below and the folder will be removed.</string>
<string name="disable">DISABLE</string>
<string name="close">CLOSE</string>
+ <string name="required_icon_link"></string>
</resources>
diff --git a/res/values/colors.xml b/res/values/colors.xml
index 5c04aa3..302ab21 100644
--- a/res/values/colors.xml
+++ b/res/values/colors.xml
@@ -48,13 +48,17 @@
<color name="dynamic_grid_preview_background">#FFFFFFFF</color>
<color name="dynamic_grid_preview_foreground">#FF000000</color>
+ <!-- App drawer -->
<color name="app_drawer_background">#76000000</color>
<color name="app_drawer_drag_background">#bf14191e</color>
<color name="app_scrubber_highlight_color">@android:color/white</color>
<color name="app_scrubber_gray_color">@android:color/darker_gray</color>
<color name="scrubber_background">#CC14191E</color>
- <color name="aftv_shadowColor">#b0000000</color>
+ <color name="drawer_header_text_shadow">#b0000000</color>
+
+ <color name="drawer_container_background_default">#00000000</color>
+ <color name="drawer_container_background_custom">#66000000</color>
<color name="folder_background">#141a1e</color>
</resources>
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index b92eae2..f6459a5 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -131,14 +131,32 @@
<dimen name="app_drawer_scrubber_padding">20dp</dimen>
- <!-- App Drawer Item -->
- <dimen name="app_drawer_char_width">27dp</dimen>
- <dimen name="container_paddingStart">6dp</dimen>
- <dimen name="container_paddingEnd">6dp</dimen>
- <dimen name="aftv_width">32dp</dimen>
- <dimen name="aftv_marginTop">10dp</dimen>
- <dimen name="aftv_minTextSize">8sp</dimen>
- <dimen name="aftv_textSize">24sp</dimen>
+<!-- App Drawer Item -->
+ <dimen name="drawer_header_text_char_width">27dp</dimen>
+ <dimen name="drawer_header_text_min_text_size">8sp</dimen>
+ <dimen name="drawer_header_text_margin_bottom">24dp</dimen>
+ <dimen name="drawer_header_text_margin_start">6dp</dimen>
+ <item name="drawer_header_text_shadow_radius" format="float" type="dimen">4</item>
+ <item name="drawer_header_text_shadow_dy" format="float" type="dimen">2</item>
+
+ <!-- Custom/default variances -->
+ <dimen name="drawer_header_text_size_default">20sp</dimen>
+ <dimen name="drawer_header_text_margin_top_default">6dp</dimen>
+ <dimen name="drawer_item_flow_padding_bottom_default">0dp</dimen>
+ <dimen name="drawer_container_bottom_margin_default">0dp</dimen>
+
+ <dimen name="drawer_header_text_size_custom">16sp</dimen>
+ <dimen name="drawer_header_text_margin_top_custom">-2dp</dimen>
+ <dimen name="drawer_item_flow_padding_bottom_custom">15dp</dimen>
+ <dimen name="drawer_container_bottom_margin_custom">20dp</dimen>
+
+ <!-- Custom header -->
+ <dimen name="drawer_custom_title_text_size">16sp</dimen>
+ <dimen name="drawer_custom_title_margin_start">50dp</dimen>
+ <dimen name="drawer_custom_title_margin_top">15dp</dimen>
+ <dimen name="drawer_custom_icon_margin_top">20dp</dimen>
+ <dimen name="drawer_custom_icon_margin_end">14dp</dimen>
+ <dimen name="drawer_custom_icon_size">12dp</dimen>
<!-- Folder open animation -->
<integer name="folder_translate_y_dist">300</integer>
diff --git a/src/com/android/launcher3/AppDrawerIconView.java b/src/com/android/launcher3/AppDrawerIconView.java
index d8564b6..94b5e93 100644
--- a/src/com/android/launcher3/AppDrawerIconView.java
+++ b/src/com/android/launcher3/AppDrawerIconView.java
@@ -33,6 +33,7 @@ public class AppDrawerIconView extends LinearLayout {
TextView mLabel;
ImageView mIcon;
+ boolean mDraggable = true;
public AppDrawerIconView(Context context) {
super(context);
diff --git a/src/com/android/launcher3/AppDrawerListAdapter.java b/src/com/android/launcher3/AppDrawerListAdapter.java
index 3639e11..fb47eb8 100644
--- a/src/com/android/launcher3/AppDrawerListAdapter.java
+++ b/src/com/android/launcher3/AppDrawerListAdapter.java
@@ -21,6 +21,7 @@ import android.animation.AnimatorListenerAdapter;
import android.animation.ValueAnimator;
import android.content.ComponentName;
import android.content.Context;
+import android.content.res.Resources;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.provider.Settings;
@@ -33,9 +34,11 @@ import android.view.View;
import android.view.ViewGroup;
import android.view.animation.DecelerateInterpolator;
import android.view.animation.Interpolator;
-import android.widget.FrameLayout;
+import android.widget.ImageView;
import android.widget.LinearLayout;
+import android.widget.RelativeLayout;
import android.widget.SectionIndexer;
+import android.widget.TextView;
import com.android.launcher3.locale.LocaleSetManager;
import com.android.launcher3.locale.LocaleUtils;
import com.android.launcher3.settings.SettingsProvider;
@@ -45,13 +48,15 @@ import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
-import java.util.List;
/**
* AppDrawerListAdapter - list adapter for the vertical app drawer
*/
public class AppDrawerListAdapter extends RecyclerView.Adapter<AppDrawerListAdapter.ViewHolder>
- implements View.OnLongClickListener, DragSource, SectionIndexer {
+ implements View.OnLongClickListener, View.OnClickListener, DragSource, SectionIndexer {
+
+ public static final String REMOTE_HEADER = "☆";
+ public static final String REMOTE_SCRUBBER = "★";
private static final String TAG = AppDrawerListAdapter.class.getSimpleName();
private static final String NUMERIC_OR_SPECIAL_HEADER = "#";
@@ -71,6 +76,32 @@ public class AppDrawerListAdapter extends RecyclerView.Adapter<AppDrawerListAdap
}
}
+ private static class Bucket {
+ private int index;
+ private String startString;
+
+ public Bucket(int index, String startString) {
+ this.index = index;
+ this.startString = startString;
+ }
+ }
+
+ private static Bucket getBucketForApp(AppInfo app) {
+ if (app.isRemote()) {
+ return new Bucket(Integer.MIN_VALUE, REMOTE_HEADER);
+ } else {
+ LocaleUtils localeUtils = LocaleUtils.getInstance();
+ int index = localeUtils.getBucketIndex(app.title.toString());
+ String start = localeUtils.getBucketLabel(index);
+ if (TextUtils.isEmpty(start)) {
+ start = NUMERIC_OR_SPECIAL_HEADER;
+ index = localeUtils.getBucketIndex(start);
+ }
+ return new Bucket(index, start);
+ }
+ }
+
+ private HashSet<AppInfo> mAllApps;
private ArrayList<AppItemIndexedInfo> mHeaderList;
private LayoutInflater mLayoutInflater;
@@ -113,8 +144,13 @@ public class AppDrawerListAdapter extends RecyclerView.Adapter<AppDrawerListAdap
}
public static class ViewHolder extends RecyclerView.ViewHolder {
- public AutoFitTextView mTextView;
- public ViewGroup mLayout;
+ public static int TYPE_NORMAL = 0;
+ public static int TYPE_CUSTOM = 1;
+
+ public TextView mCustomTitleTextView;
+ public ImageView mCustomImageView;
+ public AutoFitTextView mHeaderTextView;
+ public ViewGroup mIconLayout;
public View mContainerView;
public View mFadingBackgroundBackView;
public View mFadingBackgroundFrontView;
@@ -123,9 +159,11 @@ public class AppDrawerListAdapter extends RecyclerView.Adapter<AppDrawerListAdap
mContainerView = itemView;
mFadingBackgroundBackView = itemView.findViewById(R.id.fading_background_back);
mFadingBackgroundFrontView = itemView.findViewById(R.id.fading_background_front);
- mTextView = (AutoFitTextView) itemView.findViewById(R.id.drawer_item_title);
- mTextView.bringToFront();
- mLayout = (ViewGroup) itemView.findViewById(R.id.drawer_item_flow);
+ mHeaderTextView = (AutoFitTextView) itemView.findViewById(R.id.drawer_item_title);
+ mHeaderTextView.bringToFront();
+ mCustomTitleTextView = (TextView) itemView.findViewById(R.id.custom_title);
+ mCustomImageView = (ImageView) itemView.findViewById(R.id.custom_title_icon);
+ mIconLayout = (ViewGroup) itemView.findViewById(R.id.drawer_item_flow);
}
}
@@ -180,14 +218,14 @@ public class AppDrawerListAdapter extends RecyclerView.Adapter<AppDrawerListAdap
public void add(ViewHolder holder) {
mViewHolderSet.add(holder);
- holder.mTextView.addOnLayoutChangeListener(mLayoutChangeListener);
+ holder.mHeaderTextView.addOnLayoutChangeListener(mLayoutChangeListener);
createAnimationHook(holder);
}
public void remove(ViewHolder holder) {
mViewHolderSet.remove(holder);
- holder.mTextView.removeOnLayoutChangeListener(mLayoutChangeListener);
+ holder.mHeaderTextView.removeOnLayoutChangeListener(mLayoutChangeListener);
}
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
@@ -252,8 +290,8 @@ public class AppDrawerListAdapter extends RecyclerView.Adapter<AppDrawerListAdap
}
public void createAnimationHook(final ViewHolder holder) {
- holder.mTextView.animate().cancel();
- holder.mTextView.animate()
+ holder.mHeaderTextView.animate().cancel();
+ holder.mHeaderTextView.animate()
.setUpdateListener(new ItemAnimator(holder, mItemAnimatorSet))
.setListener(new AnimatorListenerAdapter() {
@Override
@@ -325,8 +363,8 @@ public class AppDrawerListAdapter extends RecyclerView.Adapter<AppDrawerListAdap
// Scale header text letters
final float targetScale = (MAX_SCALE - MIN_SCALE) * percentage + MIN_SCALE;
- holder.mTextView.setScaleX(targetScale);
- holder.mTextView.setScaleY(targetScale);
+ holder.mHeaderTextView.setScaleX(targetScale);
+ holder.mHeaderTextView.setScaleY(targetScale);
// Perform animation
if (getSectionForPosition(holder.getPosition()) == mSectionTarget) {
@@ -370,6 +408,7 @@ public class AppDrawerListAdapter extends RecyclerView.Adapter<AppDrawerListAdap
public AppDrawerListAdapter(Launcher launcher) {
mLauncher = launcher;
mHeaderList = new ArrayList<AppItemIndexedInfo>();
+ mAllApps = new HashSet<AppInfo>();
mLayoutInflater = LayoutInflater.from(launcher);
mLocaleSetManager = new LocaleSetManager(mLauncher);
@@ -388,7 +427,6 @@ public class AppDrawerListAdapter extends RecyclerView.Adapter<AppDrawerListAdap
mItemAnimatorSet.onScrolled(recyclerView, dx, dy);
}
-
public void setDragging(boolean dragging) {
mItemAnimatorSet.setDragging(dragging);
}
@@ -400,6 +438,13 @@ public class AppDrawerListAdapter extends RecyclerView.Adapter<AppDrawerListAdap
mItemAnimatorSet.setSectionTarget(sectionIndex);
}
+ /**
+ * @return the number of columns shown in the app drawer
+ */
+ public int getNumColumns() {
+ return mNumColumns;
+ }
+
private void initParams() {
mDeviceProfile = LauncherAppState.getInstance().getDynamicGrid().getDeviceProfile();
@@ -407,7 +452,7 @@ public class AppDrawerListAdapter extends RecyclerView.Adapter<AppDrawerListAdap
// set aside the space needed for the container character
int neededWidth = mLauncher.getResources()
- .getDimensionPixelSize(R.dimen.app_drawer_char_width);
+ .getDimensionPixelSize(R.dimen.drawer_header_text_char_width);
int availableWidth = mDeviceProfile.availableWidthPx - neededWidth;
mNumColumns = (int) Math.floor(availableWidth / iconWidth);
int leftOverPx = availableWidth % iconWidth;
@@ -432,63 +477,46 @@ public class AppDrawerListAdapter extends RecyclerView.Adapter<AppDrawerListAdap
/**
* Create and populate mHeaderList (buckets for app sorting)
- * @param info
*/
- public void populateByCharacter(ArrayList<AppInfo> info) {
- if (info == null || info.size() <= 0) {
- Collections.sort(mHeaderList);
- return;
- }
+ public void populateByCharacter() {
+ mHeaderList.clear();
// Create a clone of AppInfo ArrayList to preserve data
- ArrayList<AppInfo> tempInfo = (ArrayList<AppInfo>) info.clone();
+ HashSet<AppInfo> appsToProcess = (HashSet<AppInfo>) mAllApps.clone();
+ while (!appsToProcess.isEmpty()) {
+ ArrayList<AppInfo> matchingApps = new ArrayList<AppInfo>();
- ArrayList<AppInfo> appInfos = new ArrayList<AppInfo>();
+ AppInfo app = appsToProcess.iterator().next();
+ Bucket bucket = getBucketForApp(app);
- // get next app
- AppInfo app = tempInfo.get(0);
-
- // get starting character
- LocaleUtils localeUtils = LocaleUtils.getInstance();
- int bucketIndex = localeUtils.getBucketIndex(app.title.toString());
- String startString
- = localeUtils.getBucketLabel(bucketIndex);
- if (TextUtils.isEmpty(startString)) {
- startString = NUMERIC_OR_SPECIAL_HEADER;
- bucketIndex = localeUtils.getBucketIndex(startString);
- }
+ // Find other apps that fall into the same bucket
+ for (Iterator<AppInfo> iter = appsToProcess.iterator(); iter.hasNext(); ) {
+ AppInfo otherApp = iter.next();
+ Bucket otherBucket = getBucketForApp(otherApp);
- // now iterate through
- for (AppInfo info1 : tempInfo) {
- int newBucketIndex = localeUtils.getBucketIndex(info1.title.toString());
+ if (bucket.index == otherBucket.index) {
+ matchingApps.add(otherApp);
- String newChar
- = localeUtils.getBucketLabel(newBucketIndex);
- if (TextUtils.isEmpty(newChar)) {
- newChar = NUMERIC_OR_SPECIAL_HEADER;
- }
- // if same character
- if (newChar.equals(startString)) {
- // add it
- appInfos.add(info1);
+ // This app doesn't need to be processed again for other strings
+ iter.remove();
+ }
}
- }
- Collections.sort(appInfos, LauncherModel.getAppNameComparator());
-
- for (int i = 0; i < appInfos.size(); i += mNumColumns) {
- int endIndex = (int) Math.min(i + mNumColumns, appInfos.size());
- ArrayList<AppInfo> subList = new ArrayList<AppInfo>(appInfos.subList(i, endIndex));
- AppItemIndexedInfo indexInfo;
- indexInfo = new AppItemIndexedInfo(startString, bucketIndex, subList, i != 0);
- mHeaderList.add(indexInfo);
+ // Sort so they display in alphabetical order
+ Collections.sort(matchingApps, LauncherModel.getAppNameComparator());
+
+ // Split app list by number of columns and add rows to header list
+ for (int i = 0; i < matchingApps.size(); i += mNumColumns) {
+ int endIndex = Math.min(i + mNumColumns, matchingApps.size());
+ ArrayList<AppInfo> subList =
+ new ArrayList<AppInfo>(matchingApps.subList(i, endIndex));
+ AppItemIndexedInfo indexedInfo =
+ new AppItemIndexedInfo(bucket.startString, bucket.index, subList, i != 0);
+ mHeaderList.add(indexedInfo);
+ }
}
- for (AppInfo remove : appInfos) {
- // remove from mApps
- tempInfo.remove(remove);
- }
- populateByCharacter(tempInfo);
+ Collections.sort(mHeaderList);
}
public void setApps(ArrayList<AppInfo> list) {
@@ -496,15 +524,20 @@ public class AppDrawerListAdapter extends RecyclerView.Adapter<AppDrawerListAdap
initParams();
filterProtectedApps(list);
+ mAllApps.clear();
+ mAllApps.addAll(list);
- mHeaderList.clear();
- populateByCharacter(list);
- populateSectionHeaders();
- mLauncher.updateScrubber();
- this.notifyDataSetChanged();
+ processApps();
}
}
+ private void processApps() {
+ populateByCharacter();
+ populateSectionHeaders();
+ mLauncher.updateScrubber();
+ this.notifyDataSetChanged();
+ }
+
private void populateSectionHeaders() {
if (mSectionHeaders == null || mSectionHeaders.size() != mHeaderList.size()) {
mSectionHeaders = new LinkedHashMap<>();
@@ -521,128 +554,30 @@ public class AppDrawerListAdapter extends RecyclerView.Adapter<AppDrawerListAdap
}
public void reset() {
- ArrayList<AppInfo> infos = getAllApps();
-
mLauncher.mAppDrawer.getLayoutManager().removeAllViews();
- setApps(infos);
- }
-
- private ArrayList<AppInfo> getAllApps() {
- ArrayList<AppInfo> indexedInfos = new ArrayList<AppInfo>();
-
- for (int j = 0; j < mHeaderList.size(); ++j) {
- AppItemIndexedInfo indexedInfo = mHeaderList.get(j);
- for (AppInfo info : indexedInfo.mInfo) {
- indexedInfos.add(info);
- }
- }
- return indexedInfos;
+ processApps();
}
public void updateApps(ArrayList<AppInfo> list) {
- // We remove and re-add the updated applications list because it's properties may have
- // changed (ie. the title), and this will ensure that the items will be in their proper
- // place in the list.
if (!LauncherAppState.isDisableAllApps()) {
- removeAppsWithoutInvalidate(list);
- addAppsWithoutInvalidate(list);
+ mAllApps.removeAll(list);
+ mAllApps.addAll(list);
reset();
}
}
-
public void addApps(ArrayList<AppInfo> list) {
- if (!LauncherAppState.isDisableAllApps()) {
- addAppsWithoutInvalidate(list);
- reset();
- }
- }
-
- private void addAppsWithoutInvalidate(ArrayList<AppInfo> list) {
- // We add it in place, in alphabetical order
- LocaleUtils localeUtils = LocaleUtils.getInstance();
-
- int count = list.size();
- for (int i = 0; i < count; ++i) {
- AppInfo info = list.get(i);
- boolean found = false;
- AppItemIndexedInfo lastInfoForSection = null;
- int bucketIndex = localeUtils.getBucketIndex(info.title.toString());
- String start = localeUtils.getBucketLabel(bucketIndex);
- if (TextUtils.isEmpty(start)) {
- start = NUMERIC_OR_SPECIAL_HEADER;
- bucketIndex = localeUtils.getBucketIndex(start);
- }
- for (int j = 0; j < mHeaderList.size(); ++j) {
- AppItemIndexedInfo indexedInfo = mHeaderList.get(j);
- if (start.equals(indexedInfo.mStartString)) {
- Collections.sort(indexedInfo.mInfo, LauncherModel.getAppNameComparator());
- int index =
- Collections.binarySearch(indexedInfo.mInfo,
- info, LauncherModel.getAppNameComparator());
- if (index >= 0) {
- found = true;
- break;
- } else {
- lastInfoForSection = indexedInfo;
- }
- }
- }
- if (!found) {
- if (lastInfoForSection != null) {
- lastInfoForSection.mInfo.add(info);
- } else {
- // we need to create a new section
- ArrayList<AppInfo> newInfos = new ArrayList<AppInfo>();
- newInfos.add(info);
- AppItemIndexedInfo newInfo =
- new AppItemIndexedInfo(start, bucketIndex, newInfos, false);
- mHeaderList.add(newInfo);
- Collections.sort(mHeaderList);
- }
- }
- }
+ updateApps(list);
}
public void removeApps(ArrayList<AppInfo> appInfos) {
if (!LauncherAppState.isDisableAllApps()) {
- removeAppsWithoutInvalidate(appInfos);
- //recreate everything
+ mAllApps.removeAll(appInfos);
reset();
}
}
- private void removeAppsWithoutInvalidate(ArrayList<AppInfo> list) {
- // loop through all the apps and remove apps that have the same component
- int length = list.size();
- for (int i = 0; i < length; ++i) {
- AppInfo info = list.get(i);
- for (int j = 0; j < mHeaderList.size(); ++j) {
- AppItemIndexedInfo indexedInfo = mHeaderList.get(j);
- ArrayList<AppInfo> clonedIndexedInfoApps =
- (ArrayList<AppInfo>) indexedInfo.mInfo.clone();
- int index =
- findAppByComponent(clonedIndexedInfoApps, info);
- if (index > -1) {
- indexedInfo.mInfo.remove(info);
- }
- }
- }
- }
-
- private int findAppByComponent(List<AppInfo> list, AppInfo item) {
- ComponentName removeComponent = item.intent.getComponent();
- int length = list.size();
- for (int i = 0; i < length; ++i) {
- AppInfo info = list.get(i);
- if (info.intent.getComponent().equals(removeComponent)) {
- return i;
- }
- }
- return -1;
- }
-
/*
* AllAppsView implementation
*/
@@ -655,28 +590,30 @@ public class AppDrawerListAdapter extends RecyclerView.Adapter<AppDrawerListAdap
View v = LayoutInflater.from(parent.getContext()).
inflate(R.layout.app_drawer_item, parent, false);
ViewHolder holder = new ViewHolder(v);
- ViewGroup.LayoutParams params = holder.mTextView.getLayoutParams();
-
- // set the margin parameter to account for the text size of the icons so that the text view
- // is based on the icon size only
- if (params instanceof ViewGroup.MarginLayoutParams) {
- ViewGroup.MarginLayoutParams marginParams = (ViewGroup.MarginLayoutParams) params;
- marginParams.setMargins(marginParams.leftMargin, marginParams.topMargin,
- marginParams.rightMargin, marginParams.bottomMargin);
- holder.mTextView.setLayoutParams(marginParams);
- }
for (int i = 0; i < mNumColumns; i++) {
AppDrawerIconView icon = (AppDrawerIconView) mLayoutInflater.inflate(
- R.layout.drawer_icon, holder.mLayout, false);
+ R.layout.drawer_icon, holder.mIconLayout, false);
icon.setOnClickListener(mLauncher);
icon.setOnLongClickListener(this);
- holder.mLayout.addView(icon);
+ holder.mIconLayout.addView(icon);
+ }
+
+ if (viewType == ViewHolder.TYPE_CUSTOM) {
+ applyCustomStyle(holder);
+ holder.mCustomImageView.setOnClickListener(this);
}
+
return holder;
}
@Override
+ public int getItemViewType(int position) {
+ return mHeaderList.get(position).isRemote() ?
+ ViewHolder.TYPE_CUSTOM : ViewHolder.TYPE_NORMAL;
+ }
+
+ @Override
public void onViewAttachedToWindow(ViewHolder holder) {
super.onViewAttachedToWindow(holder);
@@ -704,23 +641,20 @@ public class AppDrawerListAdapter extends RecyclerView.Adapter<AppDrawerListAdap
@Override
public void onBindViewHolder(ViewHolder holder, int position) {
AppItemIndexedInfo indexedInfo = mHeaderList.get(position);
- holder.mTextView.setVisibility(indexedInfo.isChild ? View.INVISIBLE : View.VISIBLE);
+
+ holder.mHeaderTextView.setVisibility(indexedInfo.isChild ? View.INVISIBLE : View.VISIBLE);
if (!indexedInfo.isChild) {
- if (indexedInfo.mStartString.equals(NUMERIC_OR_SPECIAL_HEADER)) {
- holder.mTextView.setText(NUMERIC_OR_SPECIAL_HEADER);
- } else {
- holder.mTextView.setText(String.valueOf(indexedInfo.mStartString));
- }
+ holder.mHeaderTextView.setText(indexedInfo.mStartString);
}
- holder.mTextView.setPivotX(0);
- holder.mTextView.setPivotY(holder.mTextView.getHeight() / 2);
+ holder.mHeaderTextView.setPivotX(0);
+ holder.mHeaderTextView.setPivotY(holder.mHeaderTextView.getHeight() / 2);
final int size = indexedInfo.mInfo.size();
- int childSize = holder.mLayout.getChildCount();
+ int childSize = holder.mIconLayout.getChildCount();
for (int i = 0; i < childSize; i++) {
- AppDrawerIconView icon = (AppDrawerIconView) holder.mLayout.getChildAt(i);
+ AppDrawerIconView icon = (AppDrawerIconView) holder.mIconLayout.getChildAt(i);
icon.setLayoutParams(mIconParams);
int extraStart = mExtraPadding;
int extraEnd = mExtraPadding;
@@ -741,6 +675,7 @@ public class AppDrawerListAdapter extends RecyclerView.Adapter<AppDrawerListAdap
} else {
icon.setVisibility(View.VISIBLE);
AppInfo info = indexedInfo.mInfo.get(i);
+
icon.setTag(info);
Drawable d = Utilities.createIconDrawable(info.iconBitmap);
d.setBounds(mIconRect);
@@ -751,6 +686,12 @@ public class AppDrawerListAdapter extends RecyclerView.Adapter<AppDrawerListAdap
mDeviceProfile.iconDrawablePaddingPx);
icon.mLabel.setText(info.title);
icon.mLabel.setVisibility(mHideIconLabels ? View.INVISIBLE : View.VISIBLE);
+
+ if (info.isRemote()) {
+ RemoteFolderUpdater.getInstance()
+ .registerViewForInteraction(icon, info.getIntent());
+ icon.mDraggable = false;
+ }
}
}
holder.itemView.setTag(indexedInfo);
@@ -758,7 +699,7 @@ public class AppDrawerListAdapter extends RecyclerView.Adapter<AppDrawerListAdap
@Override
public boolean onLongClick(View v) {
- if (v instanceof AppDrawerIconView) {
+ if (v instanceof AppDrawerIconView && ((AppDrawerIconView) v).mDraggable) {
beginDraggingApplication(v);
mLauncher.enterSpringLoadedDragMode();
}
@@ -766,6 +707,13 @@ public class AppDrawerListAdapter extends RecyclerView.Adapter<AppDrawerListAdap
}
@Override
+ public void onClick(View v) {
+ if (v.getId() == R.id.custom_title_icon) {
+ mLauncher.getRemoteFolderManager().onInfoIconClicked();
+ }
+ }
+
+ @Override
public void onDropCompleted(View target, DropTarget.DragObject d, boolean isFlingToDelete,
boolean success) {
// Return early and wait for onFlingToDeleteCompleted if this was the result of a fling
@@ -877,6 +825,10 @@ public class AppDrawerListAdapter extends RecyclerView.Adapter<AppDrawerListAdap
}
return 0;
}
+
+ public boolean isRemote() {
+ return mStartString == REMOTE_HEADER;
+ }
}
@Override
@@ -941,4 +893,30 @@ public class AppDrawerListAdapter extends RecyclerView.Adapter<AppDrawerListAdap
}
}
}
+
+ /**
+ * Remote header uses a custom styled view. Apply it here.
+ * @param holder view which needs a custom style.
+ */
+ private void applyCustomStyle(final ViewHolder holder) {
+ Resources res = mLauncher.getResources();
+ RelativeLayout.LayoutParams lp =
+ (RelativeLayout.LayoutParams) holder.mHeaderTextView.getLayoutParams();
+ lp.removeRule(RelativeLayout.ALIGN_BOTTOM);
+ lp.topMargin = res.getDimensionPixelOffset(R.dimen.drawer_header_text_margin_top_custom);
+
+ holder.mContainerView.setBackgroundColor(
+ res.getColor(R.color.drawer_container_background_custom));
+ holder.mCustomTitleTextView.setVisibility(View.VISIBLE);
+ holder.mCustomImageView.setVisibility(View.VISIBLE);
+ holder.mHeaderTextView.setTextSize(
+ res.getDimension(R.dimen.drawer_header_text_size_custom));
+ holder.mIconLayout.setPadding(0, 0, 0,
+ res.getDimensionPixelOffset(R.dimen.drawer_item_flow_padding_bottom_custom));
+
+ RecyclerView.LayoutParams containerLP =
+ (RecyclerView.LayoutParams) holder.mContainerView.getLayoutParams();
+ containerLP.bottomMargin =
+ res.getDimensionPixelOffset(R.dimen.drawer_container_bottom_margin_custom);
+ }
}
diff --git a/src/com/android/launcher3/AppDrawerScrubberSections.java b/src/com/android/launcher3/AppDrawerScrubberSections.java
index 2d4fcaa..6ac12fd 100644
--- a/src/com/android/launcher3/AppDrawerScrubberSections.java
+++ b/src/com/android/launcher3/AppDrawerScrubberSections.java
@@ -29,12 +29,23 @@ public class AppDrawerScrubberSections {
private static final int MAX_HEADERS = ALPHA_LETTERS.length() + MAX_NUMBER_CUSTOM_HEADERS;
public static final int INVALID_INDEX = -1;
+ /** Header strings which have different strings in the scrubber **/
+ private static final HashMap<String, String> sHeaderScrubberStringMap;
+ static {
+ sHeaderScrubberStringMap = new HashMap<String, String>();
+ sHeaderScrubberStringMap.put(AppDrawerListAdapter.REMOTE_HEADER,
+ AppDrawerListAdapter.REMOTE_SCRUBBER);
+ }
+
private AutoExpandTextView.HighlightedText mHighlightedText;
private int mPreviousValidIndex;
private int mNextValidIndex;
private int mAdapterIndex;
public AppDrawerScrubberSections(String text, boolean highlight, int idx) {
+ if (sHeaderScrubberStringMap.get(text) != null) {
+ text = sHeaderScrubberStringMap.get(text);
+ }
mHighlightedText = new AutoExpandTextView.HighlightedText(text, highlight);
mAdapterIndex = idx;
mPreviousValidIndex = mNextValidIndex = idx;
diff --git a/src/com/android/launcher3/AppInfo.java b/src/com/android/launcher3/AppInfo.java
index 6d1350a..e8767e6 100644
--- a/src/com/android/launcher3/AppInfo.java
+++ b/src/com/android/launcher3/AppInfo.java
@@ -62,6 +62,7 @@ public class AppInfo extends ItemInfo {
static final int DOWNLOADED_FLAG = 1;
static final int UPDATED_SYSTEM_APP_FLAG = 2;
+ static final int REMOTE_APP_FLAG = 4;
int flags = 0;
@@ -92,6 +93,18 @@ public class AppInfo extends ItemInfo {
this.user = user;
}
+ public AppInfo(Intent intent, String title, Bitmap icon, UserHandleCompat user,
+ boolean remote) {
+ this.componentName = intent.getComponent();
+ this.container = ItemInfo.NO_ID;
+
+ this.intent = intent;
+ this.title = title;
+ iconBitmap = icon;
+ this.user = user;
+ flags = remote ? REMOTE_APP_FLAG : 0;
+ }
+
public static int initFlags(LauncherActivityInfoCompat info) {
int appFlags = info.getApplicationInfo().flags;
int flags = 0;
@@ -128,6 +141,13 @@ public class AppInfo extends ItemInfo {
iconBitmap = info.iconBitmap;
}
+ /**
+ * @return true if this info represents a remote app, false otherwise
+ */
+ public boolean isRemote() {
+ return (flags & REMOTE_APP_FLAG) != 0;
+ }
+
@Override
public String toString() {
return "ApplicationInfo(title=" + title.toString() + " id=" + this.id
@@ -159,4 +179,18 @@ public class AppInfo extends ItemInfo {
.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED)
.putExtra(EXTRA_PROFILE, serialNumber);
}
+
+ @Override
+ public boolean equals(Object o) {
+ if (o != null && o instanceof AppInfo) {
+ return componentName.equals(((AppInfo) o).componentName);
+ } else {
+ return false;
+ }
+ }
+
+ @Override
+ public int hashCode() {
+ return componentName.hashCode();
+ }
}
diff --git a/src/com/android/launcher3/AutoFitTextView.java b/src/com/android/launcher3/AutoFitTextView.java
index 208dd40..3d6b658 100644
--- a/src/com/android/launcher3/AutoFitTextView.java
+++ b/src/com/android/launcher3/AutoFitTextView.java
@@ -143,6 +143,14 @@ public class AutoFitTextView extends TextView {
setRawTextSize(TypedValue.applyDimension(unit, size, r.getDisplayMetrics()));
}
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void setTextSize(float size) {
+ setRawTextSize(size);
+ }
+
private void setRawTextSize(float size) {
if (size != mMaxTextSize) {
mMaxTextSize = size;
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 5567a58..ff59dce 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -2252,6 +2252,10 @@ public class Launcher extends Activity
return mRemoteFolderManager;
}
+ public AppDrawerListAdapter getAppDrawerListAdapter() {
+ return mAppDrawerAdapter;
+ }
+
public Hotseat getHotseat() {
return mHotseat;
}
@@ -2979,10 +2983,19 @@ public class Launcher extends Activity
} else if (v == mAllAppsButton) {
onClickAllAppsButton(v);
} else if (tag instanceof AppInfo) {
+ AppInfo info = (AppInfo) tag;
+
+ // Remote apps do not exist in package manager, so they should be launched
+ // directly by intent.
+ if (info.isRemote()) {
+ startActivity(info.intent);
+ return;
+ }
+
startAppShortcutOrInfoActivity(v);
LauncherApplication.getLauncherStats().sendAppLaunchEvent(
- LauncherStats.ORIGIN_APPDRAWER, ((AppInfo)tag).componentName.getPackageName());
- String packageName = ((AppInfo)tag).getIntent().getComponent().getPackageName();
+ LauncherStats.ORIGIN_APPDRAWER, info.componentName.getPackageName());
+ String packageName = info.getIntent().getComponent().getPackageName();
if (LauncherStats.SETTINGS_PACKAGE_NAME.equals(packageName)) {
LauncherApplication.getLauncherStats()
.sendSettingsOpenedEvent(LauncherStats.ORIGIN_APPDRAWER);
@@ -3841,6 +3854,7 @@ public class Launcher extends Activity
if (drawer && contentType == AppsCustomizePagedView.ContentType.Applications) {
toView = findViewById(R.id.app_drawer_container);
+ mRemoteFolderManager.onAppDrawerOpened();
} else {
toView = mAppsCustomizeTabHost;
}
@@ -5435,7 +5449,7 @@ public class Launcher extends Activity
for (AppInfo info : appInfos) {
removedComponents.add(info.componentName);
}
- if (!packageNames.isEmpty()) {
+ if (packageNames != null && !packageNames.isEmpty()) {
mWorkspace.removeItemsByPackageName(packageNames, user);
}
if (!removedComponents.isEmpty()) {
diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java
index 600fbd2..050c1f7 100644
--- a/src/com/android/launcher3/LauncherModel.java
+++ b/src/com/android/launcher3/LauncherModel.java
@@ -115,8 +115,6 @@ public class LauncherModel extends BroadcastReceiver
private boolean mIsLoaderTaskRunning;
private volatile boolean mFlushingWorkerThread;
- private static RemoteFolderUpdater remoteFolderUpdater;
-
/**
* Maintain a set of packages per user, for which we added a shortcut on the workspace.
*/