From 1792a57516c52145db6dcadb247bce452410b98c Mon Sep 17 00:00:00 2001 From: Tyson Miller Date: Tue, 10 Nov 2015 08:59:15 -0800 Subject: Merge Remote Folder into mainline. Patch 1: Squash. Patch 2: Remove obsolete code. Patch 3: Clean rebase. Patch 4: Fix commit message. Change-Id: Ie9c634d7d2f0824b3b1de84e8f0af5442e5511cc --- Android.mk | 9 +- AndroidManifest.xml | 1 + RemoteFolder/Android.mk | 1 + .../com/android/launcher3/RemoteFolderManager.java | 97 ++++++ proguard.flags | 39 +++ res/drawable/download_badge.png | Bin 0 -> 2711 bytes res/drawable/triangle_icon.png | Bin 0 -> 12557 bytes res/layout/app_drawer_item.xml | 59 ++-- res/layout/app_drawer_item_custom_header.xml | 3 + res/layout/remote_folder.xml | 4 + res/layout/user_folder.xml | 15 +- res/values/colors.xml | 3 +- res/values/dimens.xml | 18 +- res/values/preferences_defaults.xml | 1 + .../android/launcher3/AppDrawerListAdapter.java | 350 +++++++++------------ .../launcher3/AppDrawerScrubberSections.java | 11 + src/com/android/launcher3/AppInfo.java | 41 +++ src/com/android/launcher3/AutoFitTextView.java | 8 + src/com/android/launcher3/BubbleTextView.java | 22 +- src/com/android/launcher3/DeleteDropTarget.java | 12 +- src/com/android/launcher3/Folder.java | 173 ++++++---- src/com/android/launcher3/FolderIcon.java | 53 +++- src/com/android/launcher3/FolderInfo.java | 59 +++- src/com/android/launcher3/ItemInfo.java | 7 + src/com/android/launcher3/Launcher.java | 50 ++- src/com/android/launcher3/LauncherApplication.java | 2 +- src/com/android/launcher3/LauncherModel.java | 15 +- src/com/android/launcher3/LauncherProvider.java | 20 +- src/com/android/launcher3/LauncherSettings.java | 4 + .../android/launcher3/OverviewSettingsPanel.java | 48 ++- src/com/android/launcher3/ShortcutInfo.java | 10 +- src/com/android/launcher3/Utilities.java | 15 +- src/com/android/launcher3/Workspace.java | 12 +- .../list/SettingsPinnedHeaderAdapter.java | 19 +- .../launcher3/settings/SettingsProvider.java | 1 + src/com/android/launcher3/stats/LauncherStats.java | 35 ++- .../stats/internal/model/TrackingEvent.java | 8 + 37 files changed, 824 insertions(+), 401 deletions(-) create mode 100644 RemoteFolder/Android.mk create mode 100644 RemoteFolder/src/com/android/launcher3/RemoteFolderManager.java create mode 100644 res/drawable/download_badge.png create mode 100644 res/drawable/triangle_icon.png create mode 100644 res/layout/app_drawer_item_custom_header.xml create mode 100644 res/layout/remote_folder.xml diff --git a/Android.mk b/Android.mk index af967ac5a..04d4e4e9a 100644 --- a/Android.mk +++ b/Android.mk @@ -27,6 +27,7 @@ LOCAL_STATIC_JAVA_LIBRARIES := android-support-v13 \ android-support-v7-recyclerview \ guava + LOCAL_SRC_FILES := $(call all-java-files-under, src) \ $(call all-java-files-under, WallpaperPicker/src) \ $(call all-renderscript-files-under, src) \ @@ -49,10 +50,15 @@ LOCAL_AAPT_FLAGS += --rename-manifest-package com.cyanogenmod.trebuchet LOCAL_OVERRIDES_PACKAGES := Launcher3 LOCAL_PROGUARD_FLAG_FILES := proguard.flags -LOCAL_PROGUARD_ENABLED := disabled +LOCAL_PROGUARD_ENABLED := full + +REMOTE_FOLDER_UPDATER ?= $(LOCAL_PATH)/RemoteFolder +include $(REMOTE_FOLDER_UPDATER)/Android.mk include $(BUILD_PACKAGE) +include $(CLEAR_VARS) +include $(BUILD_MULTI_PREBUILT) # # Protocol Buffer Debug Utility in Java @@ -72,6 +78,7 @@ LOCAL_JAR_MANIFEST := util/etc/manifest.txt include $(BUILD_HOST_JAVA_LIBRARY) + # # Protocol Buffer Debug Utility Wrapper Script # diff --git a/AndroidManifest.xml b/AndroidManifest.xml index 70da93e2a..df64f92de 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -68,6 +68,7 @@ + diff --git a/RemoteFolder/Android.mk b/RemoteFolder/Android.mk new file mode 100644 index 000000000..2a7441345 --- /dev/null +++ b/RemoteFolder/Android.mk @@ -0,0 +1 @@ +LOCAL_SRC_FILES += $(call all-java-files-under, RemoteFolder/src) diff --git a/RemoteFolder/src/com/android/launcher3/RemoteFolderManager.java b/RemoteFolder/src/com/android/launcher3/RemoteFolderManager.java new file mode 100644 index 000000000..b3a147c19 --- /dev/null +++ b/RemoteFolder/src/com/android/launcher3/RemoteFolderManager.java @@ -0,0 +1,97 @@ +package com.android.launcher3; + +import android.content.res.Resources; +import android.graphics.drawable.Drawable; +import android.view.View; +import android.view.ViewGroup; + +import java.util.ArrayList; + +/** + * Manages adding and removing the remote folder from the workspace. + */ +public class RemoteFolderManager { + + public RemoteFolderManager(final Launcher launcher) { } + + /** + * Create a remote folder view. + * @param icon folder icon view on the workspace. + * @return a view for the remote folder. + */ + public Folder createRemoteFolder(final FolderIcon icon, ViewGroup root) { return null; } + + /** + * Get a drawable for the supplied item in the folder icon preview. + * @param items list of views in the folder. + * @param position index of icon to retreive. + * @return an icon to draw in the folder preview. + */ + public Drawable getFolderIconDrawable(final ArrayList items, + final int position) { return null; } + + /** + * Called when Launcher finishes binding items from the model. + */ + public void bindFinished() { } + + /** + * Called when the setting for remote folder is updated. + * @param newValue the new setting for remote folder + */ + public void onSettingChanged(final boolean newValue) { } + + /** + * 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 new apps are added to launcher. + * @param apps list of added apps. + */ + public void onBindAddApps(ArrayList apps) { } + + /** + * Called when launcher loads apps and applies them to the drawer. + */ + public void onSetApps() { } + + /** + * Called when the info icon is clicked + */ + public void onInfoIconClicked() { } + + /** + * Change the appearance of FolderIcon for our RemoteFolder by adding a badge + * @param icon the FolderIcon to update + * @return a FolderIcon with an added ImageView + */ + public static FolderIcon addBadgeToFolderIcon(FolderIcon icon) { + return icon; + } + + /** + * Called when the view holder is created for the remote header. + * @param holder remote view holder. + */ + public void onCreateViewHolder(final AppDrawerListAdapter.ViewHolder holder) { } + /** + * Called when the view holder is bound for the remote header. + * @param holder remote view holder. + * @param indexedInfo header info. + */ + public void onBindViewHolder(final AppDrawerListAdapter.ViewHolder holder, + final AppDrawerListAdapter.AppItemIndexedInfo indexedInfo) { } + + /** + * Get the String to be used for the RemoteFolder name and its corresponding Settings option + * @return the name RemoteFolder and its Setting option should use + */ + public static String getFeatureTitle(Resources res) { return null; } +} diff --git a/proguard.flags b/proguard.flags index 0b28c0ef4..440477b26 100644 --- a/proguard.flags +++ b/proguard.flags @@ -1,3 +1,8 @@ +-keepattributes SourceFile,LineNumberTable,InnerClasses +-keep class com.inmobi.** { *; } +-dontwarn com.inmobi.** +-dontwarn com.google.android.gms** + -keep class com.android.launcher3.Launcher { public void previousScreen(android.view.View); public void nextScreen(android.view.View); @@ -57,3 +62,37 @@ public float getAnimationProgress(); public void setAnimationProgress(float); } + +-keep class * extends java.util.ListResourceBundle { + protected Object[][] getContents(); +} + +-keep public class com.google.android.gms.common.internal.safeparcel.SafeParcelable { + public static final *** NULL; +} + +-keepnames @com.google.android.gms.common.annotation.KeepName class * +-keepclassmembernames class * { + @com.google.android.gms.common.annotation.KeepName *; +} + +-keepnames class * implements android.os.Parcelable { + public static final ** CREATOR; +} + +-keep class android.content.res.** { *; } +-dontwarn android.content.res.* + +-keep class android.view.inputmethod.** { *; } +-dontwarn android.view.inputmethod.* + +-keep class com.google.android.gms.common.api.GoogleApiClient { public *; } +-keep class com.google.android.gms.common.api.GoogleApiClient$* {public *;} +-keep class com.google.android.gms.location.LocationServices {public *;} +-keep class com.google.android.gms.location.FusedLocationProviderApi {public *;} +-keep class com.google.android.gms.location.ActivityRecognition {public *;} +-keep class com.google.android.gms.location.ActivityRecognitionApi {public *;} +-keep class com.google.android.gms.location.ActivityRecognitionResult {public *;} +-keep class com.google.android.gms.location.DetectedActivity {public *;} +-keep class com.google.android.gms.ads.identifier.AdvertisingIdClient{public *;} +-keep class com.google.android.gms.ads.identifier.AdvertisingIdClient$Info{public *;} \ No newline at end of file diff --git a/res/drawable/download_badge.png b/res/drawable/download_badge.png new file mode 100644 index 000000000..fad49b65f Binary files /dev/null and b/res/drawable/download_badge.png differ diff --git a/res/drawable/triangle_icon.png b/res/drawable/triangle_icon.png new file mode 100644 index 000000000..209cf851e Binary files /dev/null and b/res/drawable/triangle_icon.png differ diff --git a/res/layout/app_drawer_item.xml b/res/layout/app_drawer_item.xml index 7322b73e3..03ca60a00 100644 --- a/res/layout/app_drawer_item.xml +++ b/res/layout/app_drawer_item.xml @@ -15,57 +15,62 @@ limitations under the License. --> + xmlns:autofit="http://schemas.android.com/apk/res-auto" + android:splitMotionEvents="false" + android:layout_width="match_parent" + android:layout_height="wrap_content"> + + - + + + 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_header_layout" + android:orientation="horizontal" /> + android:shadowRadius="@dimen/drawer_header_text_shadow_radius" + android:shadowDy="@dimen/drawer_header_text_shadow_dy" + android:shadowColor="@color/drawer_header_text_shadow"/> diff --git a/res/layout/app_drawer_item_custom_header.xml b/res/layout/app_drawer_item_custom_header.xml new file mode 100644 index 000000000..14c171ee6 --- /dev/null +++ b/res/layout/app_drawer_item_custom_header.xml @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/res/layout/remote_folder.xml b/res/layout/remote_folder.xml new file mode 100644 index 000000000..49d47292d --- /dev/null +++ b/res/layout/remote_folder.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/res/layout/user_folder.xml b/res/layout/user_folder.xml index d1a7536a3..a25710ec8 100644 --- a/res/layout/user_folder.xml +++ b/res/layout/user_folder.xml @@ -23,12 +23,17 @@ android:paddingStart="@dimen/folder_preview_padding" android:paddingEnd="@dimen/folder_preview_padding" android:paddingBottom="@dimen/folder_preview_padding" - android:layout_gravity="center" + android:layout_margin="@dimen/folder_margin" + android:layout_gravity="bottom|center_horizontal" android:background="@drawable/folder_bg"> + + + android:layout_alignParentStart="true"/> diff --git a/res/values/colors.xml b/res/values/colors.xml index 5c04aa3e4..55f17a394 100644 --- a/res/values/colors.xml +++ b/res/values/colors.xml @@ -48,13 +48,14 @@ #FFFFFFFF #FF000000 + #76000000 #bf14191e @android:color/white @android:color/darker_gray #CC14191E - #b0000000 + #b0000000 #141a1e diff --git a/res/values/dimens.xml b/res/values/dimens.xml index b92eae20e..a100beb58 100644 --- a/res/values/dimens.xml +++ b/res/values/dimens.xml @@ -105,6 +105,7 @@ 6dp 10dp 48dp + 24dp 64dp @@ -131,14 +132,15 @@ 20dp - - 27dp - 6dp - 6dp - 32dp - 10dp - 8sp - 24sp + + 27dp + 8sp + 24dp + 6dp + 6dp + 20sp + 4 + 2 300 diff --git a/res/values/preferences_defaults.xml b/res/values/preferences_defaults.xml index 5693dbb75..fd82dbffa 100644 --- a/res/values/preferences_defaults.xml +++ b/res/values/preferences_defaults.xml @@ -6,6 +6,7 @@ @bool/config_workspaceDefaultShowOutlines @@bool/config_workspaceFadeAdjacentScreens false + false none false true diff --git a/src/com/android/launcher3/AppDrawerListAdapter.java b/src/com/android/launcher3/AppDrawerListAdapter.java index 3639e11f4..c68f52f7a 100644 --- a/src/com/android/launcher3/AppDrawerListAdapter.java +++ b/src/com/android/launcher3/AppDrawerListAdapter.java @@ -33,7 +33,6 @@ 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.LinearLayout; import android.widget.SectionIndexer; import com.android.launcher3.locale.LocaleSetManager; @@ -53,6 +52,9 @@ import java.util.List; public class AppDrawerListAdapter extends RecyclerView.Adapter implements View.OnLongClickListener, 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 +73,34 @@ public class AppDrawerListAdapter extends RecyclerView.Adapter mAllApps; private ArrayList mHeaderList; private LayoutInflater mLayoutInflater; @@ -113,8 +143,11 @@ public class AppDrawerListAdapter extends RecyclerView.Adapter(); + mAllApps = new HashSet(); mLayoutInflater = LayoutInflater.from(launcher); + mRemoteFolderManager = mLauncher.getRemoteFolderManager(); + mLocaleSetManager = new LocaleSetManager(mLauncher); mLocaleSetManager.updateLocaleSet(mLocaleSetManager.getSystemLocaleSet()); mItemAnimatorSet = new ItemAnimatorSet(launcher); @@ -388,7 +424,6 @@ public class AppDrawerListAdapter extends RecyclerView.Adapter 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 tempInfo = (ArrayList) info.clone(); + HashSet appsToProcess = (HashSet) mAllApps.clone(); + while (!appsToProcess.isEmpty()) { + ArrayList matchingApps = new ArrayList(); - ArrayList appInfos = new ArrayList(); + AppInfo app = appsToProcess.iterator().next(); + Bucket bucket = getBucketForApp(app); - // get next app - AppInfo app = tempInfo.get(0); + // Find other apps that fall into the same bucket + for (Iterator iter = appsToProcess.iterator(); iter.hasNext(); ) { + AppInfo otherApp = iter.next(); + Bucket otherBucket = getBucketForApp(otherApp); - // 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); - } - - // 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 subList = new ArrayList(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 subList = + new ArrayList(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 list) { + public void setApps(List list) { if (!LauncherAppState.isDisableAllApps()) { 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 +551,30 @@ public class AppDrawerListAdapter extends RecyclerView.Adapter infos = getAllApps(); - mLauncher.mAppDrawer.getLayoutManager().removeAllViews(); - setApps(infos); - } - - private ArrayList getAllApps() { - ArrayList indexedInfos = new ArrayList(); - - for (int j = 0; j < mHeaderList.size(); ++j) { - AppItemIndexedInfo indexedInfo = mHeaderList.get(j); - for (AppInfo info : indexedInfo.mInfo) { - indexedInfos.add(info); - } - } - return indexedInfos; - } - - public void updateApps(ArrayList 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); - reset(); - } + processApps(); } - - public void addApps(ArrayList list) { + public void updateApps(List list) { if (!LauncherAppState.isDisableAllApps()) { - addAppsWithoutInvalidate(list); + mAllApps.removeAll(list); + mAllApps.addAll(list); reset(); } } - private void addAppsWithoutInvalidate(ArrayList 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 newInfos = new ArrayList(); - newInfos.add(info); - AppItemIndexedInfo newInfo = - new AppItemIndexedInfo(start, bucketIndex, newInfos, false); - mHeaderList.add(newInfo); - Collections.sort(mHeaderList); - } - } - } + public void addApps(List list) { + updateApps(list); } - public void removeApps(ArrayList appInfos) { + public void removeApps(List appInfos) { if (!LauncherAppState.isDisableAllApps()) { - removeAppsWithoutInvalidate(appInfos); - //recreate everything + mAllApps.removeAll(appInfos); reset(); } } - private void removeAppsWithoutInvalidate(ArrayList 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 clonedIndexedInfoApps = - (ArrayList) indexedInfo.mInfo.clone(); - int index = - findAppByComponent(clonedIndexedInfoApps, info); - if (index > -1) { - indexedInfo.mInfo.remove(info); - } - } - } - } - - private int findAppByComponent(List 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,27 +587,33 @@ public class AppDrawerListAdapter extends RecyclerView.Adapter getInfo() { + return mInfo; + } } @Override @@ -916,7 +866,7 @@ public class AppDrawerListAdapter extends RecyclerView.Adapter list) { + private void filterProtectedApps(List list) { updateProtectedAppsList(mLauncher); Iterator iterator = list.iterator(); diff --git a/src/com/android/launcher3/AppDrawerScrubberSections.java b/src/com/android/launcher3/AppDrawerScrubberSections.java index 2d4fcaaaa..6ac12fdbf 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 sHeaderScrubberStringMap; + static { + sHeaderScrubberStringMap = new HashMap(); + 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 6d1350a59..d14b0e086 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,15 @@ public class AppInfo extends ItemInfo { this.user = user; } + public AppInfo(Intent intent, String title, UserHandleCompat user) { + this.componentName = intent.getComponent(); + this.container = ItemInfo.NO_ID; + + this.intent = intent; + this.title = title; + this.user = user; + } + public static int initFlags(LauncherActivityInfoCompat info) { int appFlags = info.getApplicationInfo().flags; int flags = 0; @@ -128,6 +138,23 @@ public class AppInfo extends ItemInfo { iconBitmap = info.iconBitmap; } + /** + * Check if this app has a specific flag. + * @param flag flag to check. + * @return true if the flag is present, false otherwise. + */ + public boolean hasFlag(int flag) { + return (flags & flag) != 0; + } + + /** + * Set a flag for this app + * @param flag flag to apply. + */ + public void setFlag(int flag) { + flags |= flag; + } + @Override public String toString() { return "ApplicationInfo(title=" + title.toString() + " id=" + this.id @@ -159,4 +186,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 208dd4073..3d6b65894 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/BubbleTextView.java b/src/com/android/launcher3/BubbleTextView.java index d26577ac0..9bfde23a7 100644 --- a/src/com/android/launcher3/BubbleTextView.java +++ b/src/com/android/launcher3/BubbleTextView.java @@ -118,15 +118,21 @@ public class BubbleTextView extends TextView { public void applyFromShortcutInfo(ShortcutInfo info, IconCache iconCache, boolean setDefaultPadding, boolean promiseStateChanged) { - Bitmap b = info.getIcon(iconCache); LauncherAppState app = LauncherAppState.getInstance(); + DeviceProfile grid = app.getDynamicGrid().getDeviceProfile(); - FastBitmapDrawable iconDrawable = Utilities.createIconDrawable(b); - iconDrawable.setGhostModeEnabled(info.isDisabled != 0); + Drawable iconDrawable; + if (info.customDrawable != null) { + iconDrawable = info.customDrawable; + } else { + Bitmap b = info.getIcon(iconCache); + iconDrawable = Utilities.createIconDrawable(b); + ((FastBitmapDrawable) iconDrawable).setGhostModeEnabled(info.isDisabled != 0); + } + iconDrawable.setBounds(0, 0, grid.iconSizePx, grid.iconSizePx); setCompoundDrawables(null, iconDrawable, null, null); if (setDefaultPadding) { - DeviceProfile grid = app.getDynamicGrid().getDeviceProfile(); setCompoundDrawablePadding(grid.iconDrawablePaddingPx); } if (info.contentDescription != null) { @@ -144,7 +150,12 @@ public class BubbleTextView extends TextView { LauncherAppState app = LauncherAppState.getInstance(); DeviceProfile grid = app.getDynamicGrid().getDeviceProfile(); - Drawable topDrawable = Utilities.createIconDrawable(info.iconBitmap); + Drawable topDrawable; + if (info.customDrawable != null) { + topDrawable = info.customDrawable; + } else { + topDrawable = Utilities.createIconDrawable(info.iconBitmap); + } topDrawable.setBounds(0, 0, grid.allAppsIconSizePx, grid.allAppsIconSizePx); setCompoundDrawables(null, topDrawable, null, null); setCompoundDrawablePadding(grid.iconDrawablePaddingPx); @@ -155,7 +166,6 @@ public class BubbleTextView extends TextView { setTag(info); } - @Override protected boolean setFrame(int left, int top, int right, int bottom) { if (getLeft() != left || getRight() != right || getTop() != top || getBottom() != bottom) { diff --git a/src/com/android/launcher3/DeleteDropTarget.java b/src/com/android/launcher3/DeleteDropTarget.java index ea058ea71..74b21df50 100644 --- a/src/com/android/launcher3/DeleteDropTarget.java +++ b/src/com/android/launcher3/DeleteDropTarget.java @@ -330,10 +330,16 @@ public class DeleteDropTarget extends ButtonDropTarget { } else if (isWorkspaceOrFolderApplication(d)) { LauncherModel.deleteItemFromDatabase(mLauncher, item); } else if (isWorkspaceFolder(d)) { - // Remove the folder from the workspace and delete the contents from launcher model FolderInfo folderInfo = (FolderInfo) item; - mLauncher.removeFolder(folderInfo); - LauncherModel.deleteFolderContentsFromDatabase(mLauncher, folderInfo); + + // Remote folder should not really be deleted. Let the manager handle it. + if (folderInfo.isRemote()) { + mLauncher.getRemoteFolderManager().onFolderDeleted(); + } else { + // Remove the folder from the workspace and delete the contents from launcher model + mLauncher.removeFolder(folderInfo); + LauncherModel.deleteFolderContentsFromDatabase(mLauncher, folderInfo); + } } else if (isWorkspaceOrFolderWidget(d)) { // Remove the widget from the workspace mLauncher.removeAppWidget((LauncherAppWidgetInfo) item); diff --git a/src/com/android/launcher3/Folder.java b/src/com/android/launcher3/Folder.java index d10be4efc..486d5e514 100644 --- a/src/com/android/launcher3/Folder.java +++ b/src/com/android/launcher3/Folder.java @@ -37,7 +37,6 @@ import android.support.v4.widget.AutoScrollHelper; import android.text.InputType; import android.text.Selection; import android.text.Spannable; -import android.text.TextUtils; import android.util.AttributeSet; import android.util.DisplayMetrics; import android.util.Log; @@ -49,6 +48,7 @@ import android.view.Menu; import android.view.MenuItem; import android.view.MotionEvent; import android.view.View; +import android.view.ViewGroup; import android.view.accessibility.AccessibilityEvent; import android.view.accessibility.AccessibilityManager; import android.view.animation.AccelerateInterpolator; @@ -102,7 +102,7 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList private int mMaterialExpandDuration; private int mMaterialExpandStagger; protected CellLayout mContent; - private ScrollView mScrollView; + protected ScrollView mScrollView; private final LayoutInflater mInflater; private final IconCache mIconCache; private int mState = STATE_NONE; @@ -132,7 +132,7 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList private boolean mDeleteFolderOnDropCompleted = false; private boolean mSuppressFolderDeletion = false; private boolean mItemAddedBackToSelfViaIcon = false; - View mFolderNameLockContainer; + protected View mFolderNameLockContainer; FolderEditText mFolderName; ImageView mFolderLock; private int mScreenWidth; @@ -218,7 +218,6 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList super.onFinishInflate(); mScrollView = (ScrollView) findViewById(R.id.scroll_view); mContent = (CellLayout) findViewById(R.id.folder_content); - int measureSpec = MeasureSpec.UNSPECIFIED; mFocusIndicatorHandler = new FocusIndicatorView(getContext()); mContent.addView(mFocusIndicatorHandler, 0); @@ -232,16 +231,11 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList mContent.setGridSize(0, 0); mContent.getShortcutsAndWidgets().setMotionEventSplittingEnabled(false); mContent.setInvertIfRtl(true); - mFolderNameLockContainer = findViewById(R.id.folder_name_lock_container); + mFolderName = (FolderEditText) findViewById(R.id.folder_name); mFolderName.setFolder(this); mFolderName.setOnFocusChangeListener(this); - // We find out how tall the text view wants to be (it is set to wrap_content), so that - // we can allocate the appropriate amount of space for it. - mFolderName.measure(measureSpec, measureSpec); - mFolderNameHeight = mFolderName.getMeasuredHeight(); - // We disable action mode for now since it messes up the view on phones mFolderName.setCustomSelectionActionModeCallback(mActionModeCallback); mFolderName.setOnEditorActionListener(this); @@ -257,11 +251,13 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList mFolderName.setVisibility(View.GONE); } + mFolderNameLockContainer = findViewById(R.id.folder_name_lock_container); mFolderLock = (ImageView) findViewById(R.id.folder_lock); - mFolderLock.measure(measureSpec, measureSpec); - mFolderLock.setOnClickListener(this); - mFolderLockHeight = mFolderLock.getMeasuredHeight(); + // Could be null if this Folder an instance of the RemoteFolder subclass + if (mFolderLock != null) { + mFolderLock.setOnClickListener(this); + } DisplayMetrics displayMetrics = this.getResources().getDisplayMetrics(); mScreenWidth = displayMetrics.widthPixels; } @@ -476,7 +472,7 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList } } - void bind(FolderInfo info) { + void bind(final FolderInfo info) { mInfo = info; ArrayList children = info.contents; ArrayList overflow = new ArrayList(); @@ -533,11 +529,12 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList * Creates a new UserFolder, inflated from R.layout.user_folder. * * @param context The application's context. + * @param root The {@link View} parent of this folder. * * @return A new UserFolder. */ - static Folder fromXml(Context context) { - return (Folder) LayoutInflater.from(context).inflate(R.layout.user_folder, null); + static Folder fromXml(Context context, ViewGroup root) { + return (Folder) LayoutInflater.from(context).inflate(R.layout.user_folder, root, false); } /** @@ -659,7 +656,8 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList circX, circY, 0, mScreenWidth); final View[] alphaViewSet = new View[] { mFolderNameLockContainer, - mContent, mFolderName, mFolderLock }; + mContent, mFolderName, mFolderLock }; + for (View view : alphaViewSet) { view.setAlpha(0f); } @@ -1218,7 +1216,16 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList // Do nothing } + /** + * @return true if contents should persist their status to the database. + */ + protected boolean shouldUpdateContentsInDatabase() { + return true; + } + private void updateItemLocationsInDatabase() { + if (!shouldUpdateContentsInDatabase()) return; + ArrayList list = getItemsInReadingOrder(); for (int i = 0; i < list.size(); i++) { View v = list.get(i); @@ -1229,6 +1236,8 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList } private void updateItemLocationsInDatabaseBatch() { + if (!shouldUpdateContentsInDatabase()) return; + ArrayList list = getItemsInReadingOrder(); ArrayList items = new ArrayList(); for (int i = 0; i < list.size(); i++) { @@ -1241,6 +1250,8 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList } public void addItemLocationsInDatabase() { + if (!shouldUpdateContentsInDatabase()) return; + ArrayList list = getItemsInReadingOrder(); for (int i = 0; i < list.size(); i++) { View v = list.get(i); @@ -1260,7 +1271,7 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList return true; } - private void setupContentDimensions(int count) { + protected void setupContentDimensions(int count) { ArrayList list = getItemsInReadingOrder(); int countX = mContent.getCountX(); @@ -1297,12 +1308,19 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList return mMaxNumItems; } - private void centerAboutIcon() { + protected void centerAboutIcon() { + requestLayout(); + int width = getMeasuredWidth(); + int height = getMeasuredHeight(); + if (width > 0 && height > 0) { + centerAboutIcon(width, height); + } + } + + private void centerAboutIcon(int width, int height) { DragLayer.LayoutParams lp = (DragLayer.LayoutParams) getLayoutParams(); DragLayer parent = (DragLayer) mLauncher.findViewById(R.id.drag_layer); - int width = getPaddingLeft() + getPaddingRight() + mContent.getDesiredWidth(); - int height = getFolderHeight(); float scale = parent.getDescendantRectRelativeToSelf(mFolderIcon, mTempRect); @@ -1329,6 +1347,8 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList int left = (grid.availableWidthPx - width) / 2; // Drop the top down a little so it isn't bounded by the page indicators int top = (int) (bounds.top + (bounds.height() * 1.15) - height); + // Make sure the top margin stays consistent + lp.topMargin = getResources().getDimensionPixelSize(R.dimen.folder_margin); if (width >= bounds.width()) { // If the folder doesn't fit within the bounds, center it about the desired bounds @@ -1361,38 +1381,17 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList private void setupContentForNumItems(int count) { setupContentDimensions(count); - - DragLayer.LayoutParams lp = (DragLayer.LayoutParams) getLayoutParams(); - if (lp == null) { - lp = new DragLayer.LayoutParams(0, 0); - lp.customPosition = true; - setLayoutParams(lp); - } - centerAboutIcon(); } - private int getContentAreaHeight() { + protected int getContentAreaHeight() { return Math.max(mContent.getDesiredHeight(), MIN_CONTENT_DIMEN); } - private int getContentAreaWidth() { + protected int getContentAreaWidth() { return Math.max(mContent.getDesiredWidth(), MIN_CONTENT_DIMEN); } - private int getFolderHeight() { - int height = getPaddingTop() + getPaddingBottom() + mFolderNameHeight - + getContentAreaHeight(); - return height; - } - protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { - int width = getPaddingLeft() + getPaddingRight() + mContent.getDesiredWidth(); - int height = getFolderHeight(); - int contentAreaWidthSpec = MeasureSpec.makeMeasureSpec(getContentAreaWidth(), - MeasureSpec.EXACTLY); - int contentAreaHeightSpec = MeasureSpec.makeMeasureSpec(getContentAreaHeight(), - MeasureSpec.EXACTLY); - if (LauncherAppState.isDisableAllApps()) { // Don't cap the height of the content to allow scrolling. mContent.setFixedSize(getContentAreaWidth(), mContent.getDesiredHeight()); @@ -1400,14 +1399,7 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList mContent.setFixedSize(getContentAreaWidth(), getContentAreaHeight()); } - mScrollView.measure(contentAreaWidthSpec, contentAreaHeightSpec); - if (TextUtils.isEmpty(mInfo.title)) { - mFolderName.measure(contentAreaWidthSpec, MeasureSpec.makeMeasureSpec( - mFolderNameHeight, MeasureSpec.EXACTLY)); - } - mFolderNameLockContainer.measure(contentAreaWidthSpec, - MeasureSpec.makeMeasureSpec(mFolderNameHeight,MeasureSpec.EXACTLY)); - setMeasuredDimension(width, height); + super.onMeasure(widthMeasureSpec, heightMeasureSpec); } private void arrangeChildren(ArrayList list) { @@ -1427,8 +1419,11 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList if (info.cellX != vacant[0] || info.cellY != vacant[1]) { info.cellX = vacant[0]; info.cellY = vacant[1]; - LauncherModel.addOrMoveItemInDatabase(mLauncher, info, mInfo.id, 0, - info.cellX, info.cellY); + + if (shouldUpdateContentsInDatabase()) { + LauncherModel.addOrMoveItemInDatabase(mLauncher, info, mInfo.id, 0, + info.cellX, info.cellY); + } } boolean insert = false; mContent.addViewToCellLayout(v, insert ? 0 : -1, (int)info.id, lp, true); @@ -1467,7 +1462,7 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList mSuppressFolderDeletion = false; } - private void replaceFolderWithFinalItem() { + protected void replaceFolderWithFinalItem() { if (mInfo.hidden && getItemCount() >= 1) { return; } @@ -1557,8 +1552,10 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList // Actually move the item in the database if it was an external drag. Call this // before creating the view, so that ShortcutInfo is updated appropriately. - LauncherModel.addOrMoveItemInDatabase( - mLauncher, si, mInfo.id, 0, si.cellX, si.cellY); + if (shouldUpdateContentsInDatabase()) { + LauncherModel.addOrMoveItemInDatabase( + mLauncher, si, mInfo.id, 0, si.cellX, si.cellY); + } // We only need to update the locations if it doesn't get handled in #onDropCompleted. if (d.dragSource != this) { @@ -1624,8 +1621,11 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList findAndSetEmptyCells(item); } createAndAddShortcut(item); - LauncherModel.addOrMoveItemInDatabase( - mLauncher, item, mInfo.id, 0, item.cellX, item.cellY); + + if (shouldUpdateContentsInDatabase()) { + LauncherModel.addOrMoveItemInDatabase( + mLauncher, item, mInfo.id, 0, item.cellX, item.cellY); + } } public void onRemove(ShortcutInfo item) { @@ -1633,8 +1633,7 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList // If this item is being dragged from this open folder, we have already handled // the work associated with removing the item, so we don't have to do anything here. if (item == mCurrentDragInfo) return; - View v = getViewForInfo(item); - mContent.removeView(v); + mContent.removeView(getViewForInfo(item)); if (mState == STATE_ANIMATING) { mRearrangeOnClose = true; } else { @@ -1645,16 +1644,52 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList } } - private View getViewForInfo(ShortcutInfo item) { - for (int j = 0; j < mContent.getCountY(); j++) { - for (int i = 0; i < mContent.getCountX(); i++) { - View v = mContent.getChildAt(i, j); - if (v.getTag() == item) { - return v; - } + @Override + public void onRemoveAll() { + // Clear the UX after folder contents are removed from the DB + removeViewsForItems(null); + mLauncher.closeFolder(this); + replaceFolderWithFinalItem(); + } + + @Override + public void onRemoveAll(ArrayList items) { + removeViewsForItems(items); + if (mInfo.contents.isEmpty()) { + mLauncher.closeFolder(this); + } + replaceFolderWithFinalItem(); + } + + /** + * Remove all the supplied item views from this folder. + * @param items info of views to remove, or null if all views should be removed. + */ + protected void removeViewsForItems(ArrayList items) { + if (items == null) { + mContent.removeAllViews(); + } else { + for (ShortcutInfo item : items) { + mContent.removeView(getViewForInfo(item)); } } - return null; + } + + /** + * Update the view tied to this shortcut. + * @param info updated info to be applied to view. + */ + public void updateViewForInfo(final ShortcutInfo info) { + View v = getViewForInfo(info); + if (v != null & v instanceof BubbleTextView) { + ((BubbleTextView) v).applyFromShortcutInfo(info, mIconCache, false); + + mItemsInvalidated = true; + } + } + + public View getViewForInfo(ShortcutInfo item) { + return mContent.getChildAt(item.cellX, item.cellY); } public void onItemsChanged() { diff --git a/src/com/android/launcher3/FolderIcon.java b/src/com/android/launcher3/FolderIcon.java index 214ec3241..c9f7556c1 100644 --- a/src/com/android/launcher3/FolderIcon.java +++ b/src/com/android/launcher3/FolderIcon.java @@ -170,7 +170,16 @@ public class FolderIcon extends FrameLayout implements FolderListener { icon.mLauncher = launcher; icon.setContentDescription(String.format(launcher.getString(R.string.folder_name_format), folderInfo.title)); - Folder folder = Folder.fromXml(launcher); + Folder folder; + if (folderInfo.isRemote()) { + folder = launcher.getRemoteFolderManager().createRemoteFolder(icon, launcher.getDragLayer()); + if (folder == null) { + LauncherModel.deleteItemFromDatabase(launcher, folderInfo); + return null; + } + } else { + folder = Folder.fromXml(launcher, launcher.getDragLayer()); + } folder.setDragController(launcher.getDragController()); folder.setFolderIcon(icon); folder.bind(folderInfo); @@ -232,6 +241,11 @@ public class FolderIcon extends FrameLayout implements FolderListener { } } + // Create an overlay badge if this FolderIcon is for a RemoteFolder + if (folderInfo.isRemote()) { + icon = RemoteFolderManager.addBadgeToFolderIcon(icon); + } + return icon; } @@ -366,10 +380,14 @@ public class FolderIcon extends FrameLayout implements FolderListener { } private boolean willAcceptItem(ItemInfo item) { + if (mInfo.isRemote()) return false; + final int itemType = item.itemType; boolean hidden = false; if (item instanceof FolderInfo){ + if (((FolderInfo) item).isRemote()) return false; + hidden = ((FolderInfo) item).hidden; } return ((itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION || @@ -683,7 +701,7 @@ public class FolderIcon extends FrameLayout implements FolderListener { super.dispatchDraw(canvas); if (mFolder == null) return; - if (mFolder.getItemCount() == 0 && !mAnimating) return; + if (mFolder.getItemCount() == 0 && !mAnimating && !mInfo.isRemote()) return; ArrayList items = mFolder.getItemsInReadingOrder(); Drawable d; @@ -692,13 +710,13 @@ public class FolderIcon extends FrameLayout implements FolderListener { // Update our drawing parameters if necessary if (mAnimating) { computePreviewDrawingParams(mAnimParams.drawable); - } else { + } else if (!items.isEmpty()) { v = (TextView) items.get(0); d = getTopDrawable(v); - computePreviewDrawingParams(d); + if (d != null) computePreviewDrawingParams(d); } - int nItemsInPreview = Math.min(items.size(), NUM_ITEMS_IN_PREVIEW); + int ntemsInPreview = Math.min(items.size(), NUM_ITEMS_IN_PREVIEW); // Hidden folder - don't display Preview View folderLock = findViewById(R.id.folder_lock_image); @@ -717,17 +735,22 @@ public class FolderIcon extends FrameLayout implements FolderListener { } if (!mAnimating) { - for (int i = NUM_ITEMS_IN_PREVIEW; i >= 0; i--) { + for (int i = 0; i < NUM_ITEMS_IN_PREVIEW; i++) { d = null; - if (i < items.size()) { + if (mInfo.isRemote()) { + d = mLauncher.getRemoteFolderManager().getFolderIconDrawable(items, i); + } else if (i < items.size()) { v = (TextView) items.get(i); if (!mHiddenItems.contains(v.getTag())) { d = getTopDrawable(v); - mParams = computePreviewItemDrawingParams(i, mParams); - mParams.drawable = d; } } + if (d != null) { + mParams = computePreviewItemDrawingParams(i, mParams); + mParams.drawable = d; + } + ImageView appIcon = null; switch(i) { case 0: @@ -826,6 +849,18 @@ public class FolderIcon extends FrameLayout implements FolderListener { requestLayout(); } + @Override + public void onRemoveAll() { + invalidate(); + requestLayout(); + } + + @Override + public void onRemoveAll(ArrayList items) { + invalidate(); + requestLayout(); + } + public void onTitleChanged(CharSequence title) { mFolderName.setText(title.toString()); setContentDescription(String.format(getContext().getString(R.string.folder_name_format), diff --git a/src/com/android/launcher3/FolderInfo.java b/src/com/android/launcher3/FolderInfo.java index 104bd6cc1..bed1a5d47 100644 --- a/src/com/android/launcher3/FolderInfo.java +++ b/src/com/android/launcher3/FolderInfo.java @@ -28,11 +28,13 @@ import java.util.Arrays; * Represents a folder containing shortcuts or apps. */ public class FolderInfo extends ItemInfo { + public static final int REMOTE_SUBTYPE = 1; /** * Whether this folder has been opened */ boolean opened; + int subType; /** * The apps and shortcuts and hidden status @@ -73,6 +75,50 @@ public class FolderInfo extends ItemInfo { itemsChanged(); } + /** + * Remove all apps and shortcuts. Does not change the DB unless + * LauncherModel.deleteFolderContentsFromDatabase(Context, FolderInfo) is called first. + */ + public void removeAll() { + contents.clear(); + for (int i = 0; i < listeners.size(); i++) { + listeners.get(i).onRemoveAll(); + } + itemsChanged(); + } + + /** + * Remove all supplied shortcuts. Does not change the DB unless + * LauncherModel.deleteFolderContentsFromDatabase(Context, FolderInfo) is called first. + * @param items the shortcuts to remove. + */ + public void removeAll(ArrayList items) { + contents.removeAll(items); + for (int i = 0; i < listeners.size(); i++) { + listeners.get(i).onRemoveAll(items); + } + itemsChanged(); + } + + /** + * @return true if this info represents a remote folder, false otherwise + */ + public boolean isRemote() { + return (subType & REMOTE_SUBTYPE) != 0; + } + + /** + * Set flag indicating whether this folder is remote + * @param remote true if folder is remote, false otherwise + */ + public void setRemote(final boolean remote) { + if (remote) { + subType |= REMOTE_SUBTYPE; + } else { + subType &= ~REMOTE_SUBTYPE; + } + } + public void setTitle(CharSequence title) { this.title = title; for (int i = 0; i < listeners.size(); i++) { @@ -85,6 +131,7 @@ public class FolderInfo extends ItemInfo { super.onAddToDatabase(context, values); values.put(LauncherSettings.Favorites.TITLE, title.toString()); values.put(LauncherSettings.Favorites.HIDDEN, hidden ? 1 : 0); + values.put(LauncherSettings.BaseLauncherColumns.SUBTYPE, subType); } void addListener(FolderListener listener) { @@ -110,15 +157,17 @@ public class FolderInfo extends ItemInfo { } interface FolderListener { - public void onAdd(ShortcutInfo item); - public void onRemove(ShortcutInfo item); - public void onTitleChanged(CharSequence title); - public void onItemsChanged(); + void onAdd(ShortcutInfo item); + void onRemove(ShortcutInfo item); + void onRemoveAll(); + void onRemoveAll(ArrayList items); + void onTitleChanged(CharSequence title); + void onItemsChanged(); } @Override public String toString() { - return "FolderInfo(id=" + this.id + " type=" + this.itemType + return "FolderInfo(id=" + this.id + " type=" + this.itemType + " subtype=" + this.subType + " container=" + this.container + " screen=" + screenId + " cellX=" + cellX + " cellY=" + cellY + " spanX=" + spanX + " spanY=" + spanY + " dropPos=" + Arrays.toString(dropPos) + ")"; diff --git a/src/com/android/launcher3/ItemInfo.java b/src/com/android/launcher3/ItemInfo.java index 91af5873e..5c1bc0d55 100644 --- a/src/com/android/launcher3/ItemInfo.java +++ b/src/com/android/launcher3/ItemInfo.java @@ -20,6 +20,7 @@ import android.content.ContentValues; import android.content.Context; import android.content.Intent; import android.graphics.Bitmap; +import android.graphics.drawable.Drawable; import android.util.Log; import com.android.launcher3.compat.UserHandleCompat; @@ -125,6 +126,12 @@ public class ItemInfo { UserHandleCompat user; + /** + * A custom drawable to use for the icon. Not persisted to the database because + * it is not guaranteed to be a bitmap (could be a vector). + */ + Drawable customDrawable; + ItemInfo() { user = UserHandleCompat.myUserHandle(); } diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index 030e2712d..44f8f4fe1 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -32,9 +32,7 @@ import android.app.AlertDialog; import android.app.Fragment; import android.app.FragmentManager; import android.app.FragmentTransaction; -import android.app.Dialog; import android.app.SearchManager; -import android.appwidget.AppWidgetHost; import android.appwidget.AppWidgetHostView; import android.appwidget.AppWidgetManager; import android.appwidget.AppWidgetProviderInfo; @@ -62,7 +60,6 @@ import android.graphics.Canvas; import android.graphics.Color; import android.graphics.PorterDuff; import android.graphics.Rect; -import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.Drawable; import android.net.Uri; import android.os.AsyncTask; @@ -73,8 +70,6 @@ import android.os.Handler; import android.os.Message; import android.os.StrictMode; import android.os.SystemClock; -import android.os.UserHandle; -import android.preference.PreferenceManager; import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; import android.text.Selection; @@ -82,7 +77,6 @@ import android.text.SpannableStringBuilder; import android.text.TextUtils; import android.text.method.TextKeyListener; import android.util.Log; -import android.util.Pair; import android.view.Display; import android.view.Gravity; import android.view.HapticFeedbackConstants; @@ -124,7 +118,6 @@ import com.android.launcher3.compat.UserManagerCompat; import com.android.launcher3.PagedView.TransitionEffect; import com.android.launcher3.settings.SettingsProvider; import com.android.launcher3.stats.LauncherStats; -import com.android.launcher3.stats.internal.service.AggregationIntentService; import java.io.DataInputStream; import java.io.DataOutputStream; @@ -294,6 +287,7 @@ public class Launcher extends Activity private DynamicGridSizeFragment mDynamicGridSizeFragment; private LauncherClings mLauncherClings; protected HiddenFolderFragment mHiddenFolderFragment; + private RemoteFolderManager mRemoteFolderManager; private AppWidgetManagerCompat mAppWidgetManager; private LauncherAppWidgetHost mAppWidgetHost; @@ -512,6 +506,8 @@ public class Launcher extends Activity mAppWidgetHost = new LauncherAppWidgetHost(this, APPWIDGET_HOST_ID); mAppWidgetHost.startListening(); + mRemoteFolderManager = new RemoteFolderManager(this); + // If we are getting an onCreate, we can actually preempt onResume and unset mPaused here, // this also ensures that any synchronous binding below doesn't re-trigger another // LauncherModel load. @@ -2252,6 +2248,14 @@ public class Launcher extends Activity return mWorkspace; } + public RemoteFolderManager getRemoteFolderManager() { + return mRemoteFolderManager; + } + + public AppDrawerListAdapter getAppDrawerListAdapter() { + return mAppDrawerAdapter; + } + public Hotseat getHotseat() { return mHotseat; } @@ -2814,10 +2818,14 @@ public class Launcher extends Activity } FolderIcon addFolder(CellLayout layout, long container, final long screenId, int cellX, - int cellY) { - final FolderInfo folderInfo = new FolderInfo(); + int cellY) { + FolderInfo folderInfo = new FolderInfo(); folderInfo.title = getText(R.string.folder_name); + return addFolder(layout, container, screenId, cellX, cellY, folderInfo); + } + FolderIcon addFolder(CellLayout layout, long container, final long screenId, int cellX, + int cellY, FolderInfo folderInfo) { // Update the model LauncherModel.addItemToDatabase(Launcher.this, folderInfo, container, screenId, cellX, cellY, false); @@ -2987,10 +2995,11 @@ public class Launcher extends Activity } else if (v == mAllAppsButton) { onClickAllAppsButton(v); } else if (tag instanceof AppInfo) { + AppInfo info = (AppInfo) tag; 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); @@ -3845,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; } @@ -4872,6 +4882,7 @@ public class Launcher extends Activity addedApps != null && mAppsCustomizeContent != null) { mAppsCustomizeContent.addApps(addedApps); mAppDrawerAdapter.addApps(addedApps); + mRemoteFolderManager.onBindAddApps(addedApps); } } @@ -4895,7 +4906,7 @@ public class Launcher extends Activity final AnimatorSet anim = LauncherAnimUtils.createAnimatorSet(); final Collection bounceAnims = new ArrayList(); final boolean animateIcons = forceAnimateIcons && canRunNewAppsAnimation(); - Workspace workspace = mWorkspace; + final Workspace workspace = mWorkspace; long newShortcutsScreenId = -1; for (int i = start; i < end; i++) { final ItemInfo item = shortcuts.get(i); @@ -4946,12 +4957,14 @@ public class Launcher extends Activity } break; case LauncherSettings.Favorites.ITEM_TYPE_FOLDER: - FolderIcon newFolder = FolderIcon.fromXml(R.layout.folder_icon, this, + final FolderIcon newFolder = FolderIcon.fromXml(R.layout.folder_icon, this, (ViewGroup) workspace.getChildAt(workspace.getCurrentPage()), (FolderInfo) item, mIconCache); - newFolder.setTextVisible(!mHideIconLabels); - workspace.addInScreenFromBind(newFolder, item.container, item.screenId, item.cellX, - item.cellY, 1, 1); + if (newFolder != null) { + newFolder.setTextVisible(!mHideIconLabels); + workspace.addInScreenFromBind(newFolder, item.container, item.screenId, item.cellX, + item.cellY, 1, 1); + } break; default: throw new RuntimeException("Invalid Item Type"); @@ -5203,6 +5216,8 @@ public class Launcher extends Activity mWorkspace.resetOverviewMode(); } mModel.updateCount(); + + mRemoteFolderManager.bindFinished(); } private void sendLoadingCompleteBroadcastIfNecessary() { @@ -5289,6 +5304,7 @@ public class Launcher extends Activity } else { if (mAppDrawerAdapter != null) { mAppDrawerAdapter.setApps(apps); + mRemoteFolderManager.onSetApps(); } if (mAppsCustomizeContent != null) { mAppsCustomizeContent.setApps(apps); @@ -5433,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/LauncherApplication.java b/src/com/android/launcher3/LauncherApplication.java index eab9861bd..3facb3c21 100644 --- a/src/com/android/launcher3/LauncherApplication.java +++ b/src/com/android/launcher3/LauncherApplication.java @@ -66,7 +66,7 @@ public class LauncherApplication extends Application { if (getResources().getBoolean(R.bool.config_launcher_stkAppRename)) { registerAppNameChangeReceiver(); } - sLauncherStats = LauncherStats.createInstance(this); + sLauncherStats = LauncherStats.getInstance(this); AggregationIntentService.scheduleService(this); } diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java index dae2c180f..5470e177d 100644 --- a/src/com/android/launcher3/LauncherModel.java +++ b/src/com/android/launcher3/LauncherModel.java @@ -30,7 +30,6 @@ import android.content.Intent; import android.content.Intent.ShortcutIconResource; import android.content.IntentFilter; import android.content.SharedPreferences; -import android.content.pm.LauncherApps.Callback; import android.content.pm.PackageManager; import android.content.pm.ProviderInfo; import android.content.pm.ResolveInfo; @@ -933,10 +932,10 @@ public class LauncherModel extends BroadcastReceiver String userSerial = Long.toString(UserManagerCompat.getInstance(context) .getSerialNumberForUser(user)); Cursor c = cr.query(LauncherSettings.Favorites.CONTENT_URI, - new String[] { "title", "intent", "profileId" }, - "title=? and (intent=? or intent=?) and profileId=?", - new String[] { title, intentWithPkg.toUri(0), intentWithoutPkg.toUri(0), userSerial }, - null); + new String[] { "title", "intent", "profileId" }, + "title=? and (intent=? or intent=?) and profileId=?", + new String[] { title, intentWithPkg.toUri(0), intentWithoutPkg.toUri(0), userSerial}, + null); try { return c.moveToFirst(); } finally { @@ -1012,6 +1011,7 @@ public class LauncherModel extends BroadcastReceiver final int cellXIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CELLX); final int cellYIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CELLY); final int hiddenIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.HIDDEN); + final int subType = c.getColumnIndexOrThrow(LauncherSettings.Favorites.SUBTYPE); FolderInfo folderInfo = null; switch (c.getInt(itemTypeIndex)) { @@ -1027,6 +1027,7 @@ public class LauncherModel extends BroadcastReceiver folderInfo.cellX = c.getInt(cellXIndex); folderInfo.cellY = c.getInt(cellYIndex); folderInfo.hidden = c.getInt(hiddenIndex) > 0; + folderInfo.subType = subType; return folderInfo; } @@ -2126,6 +2127,7 @@ public class LauncherModel extends BroadcastReceiver //final int displayModeIndex = c.getColumnIndexOrThrow( final int hiddenIndex = c.getColumnIndexOrThrow( LauncherSettings.Favorites.HIDDEN); + final int subTypeIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.SUBTYPE); //final int uriIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.URI); //final int displayModeIndex = c.getColumnIndexOrThrow( // LauncherSettings.Favorites.DISPLAY_MODE); @@ -2363,6 +2365,7 @@ public class LauncherModel extends BroadcastReceiver folderInfo.spanX = 1; folderInfo.spanY = 1; folderInfo.hidden = c.getInt(hiddenIndex) > 0; + folderInfo.subType = c.getInt(subTypeIndex); // check & update map of what's occupied if (!checkItemPlacement(occupied, folderInfo, shouldResize)) { @@ -2859,7 +2862,7 @@ public class LauncherModel extends BroadcastReceiver } workspaceItems.remove(i); folders.remove(Long.valueOf(item.id)); - } else if (folder.contents.size() == 0 /*&& !(folder instanceof LiveFolderInfo)*/) { + } else if (folder.contents.size() == 0 && !folder.isRemote()) { LauncherModel.deleteFolderContentsFromDatabase(mContext, folder); workspaceItems.remove(i); folders.remove(Long.valueOf(item.id)); diff --git a/src/com/android/launcher3/LauncherProvider.java b/src/com/android/launcher3/LauncherProvider.java index 14492488a..abe082eaa 100644 --- a/src/com/android/launcher3/LauncherProvider.java +++ b/src/com/android/launcher3/LauncherProvider.java @@ -67,7 +67,7 @@ public class LauncherProvider extends ContentProvider { private static final String TAG = "Launcher.LauncherProvider"; private static final boolean LOGD = false; - private static final int DATABASE_VERSION = 22; + private static final int DATABASE_VERSION = 23; static final String OLD_AUTHORITY = "com.android.launcher2.settings"; static final String AUTHORITY = ProviderConfig.AUTHORITY; @@ -505,7 +505,8 @@ public class LauncherProvider extends ContentProvider { "modified INTEGER NOT NULL DEFAULT 0," + "restored INTEGER NOT NULL DEFAULT 0," + "profileId INTEGER DEFAULT " + userSerialNumber + - ",hidden INTEGER DEFAULT 0" + + ",hidden INTEGER DEFAULT 0," + + "subType INTEGER DEFAULT 0" + ");"); addWorkspacesTable(db); @@ -995,6 +996,21 @@ public class LauncherProvider extends ContentProvider { version = 22; } + if (version < 23) { + db.beginTransaction(); + try { + db.execSQL("ALTER TABLE favorites " + + "ADD COLUMN subType INTEGER DEFAULT 0;"); + db.setTransactionSuccessful(); + version = 23; + } catch (SQLException ex) { + // Old version remains, which means we wipe old data + Log.e(TAG, ex.getMessage(), ex); + } finally { + db.endTransaction(); + } + } + if (version != DATABASE_VERSION) { Log.w(TAG, "Destroying all old data."); db.execSQL("DROP TABLE IF EXISTS " + TABLE_FAVORITES); diff --git a/src/com/android/launcher3/LauncherSettings.java b/src/com/android/launcher3/LauncherSettings.java index 357bac52d..d7df5a714 100644 --- a/src/com/android/launcher3/LauncherSettings.java +++ b/src/com/android/launcher3/LauncherSettings.java @@ -44,6 +44,10 @@ class LauncherSettings { */ static final String HIDDEN = "hidden"; + /** + * Folder subtype, used for Remote Folders + */ + static final String SUBTYPE = "subType"; /** * The Intent URL of the gesture, describing what it points to. This * value is given to {@link android.content.Intent#parseUri(String, int)} to create diff --git a/src/com/android/launcher3/OverviewSettingsPanel.java b/src/com/android/launcher3/OverviewSettingsPanel.java index fef164383..92f2bd46a 100644 --- a/src/com/android/launcher3/OverviewSettingsPanel.java +++ b/src/com/android/launcher3/OverviewSettingsPanel.java @@ -10,6 +10,10 @@ import android.widget.ImageView; import android.widget.ListView; import com.android.launcher3.list.PinnedHeaderListView; import com.android.launcher3.list.SettingsPinnedHeaderAdapter; +import com.android.launcher3.settings.SettingsProvider; + +import java.util.ArrayList; +import java.util.Arrays; public class OverviewSettingsPanel { public static final String ANDROID_SETTINGS = "com.android.settings"; @@ -41,13 +45,6 @@ public class OverviewSettingsPanel { res.getString(R.string.drawer_settings), res.getString(R.string.app_settings)}; - String[] values = new String[]{ - res.getString(R.string.home_screen_search_text), - res.getString(R.string.scroll_effect_text), - res.getString(R.string.icon_labels), - res.getString(R.string.scrolling_wallpaper), - res.getString(R.string.grid_size_text)}; - String[] valuesDrawer = new String[] { res.getString(R.string.drawer_type), res.getString(R.string.scroll_effect_text), @@ -58,7 +55,6 @@ public class OverviewSettingsPanel { res.getString(R.string.larger_icons_text), res.getString(R.string.protected_app_settings)}; - mSettingsAdapter = new SettingsPinnedHeaderAdapter(mLauncher); mSettingsAdapter.setHeaders(headers); mSettingsAdapter.addPartition(false, true); @@ -66,7 +62,8 @@ public class OverviewSettingsPanel { mSettingsAdapter.addPartition(false, true); mSettingsAdapter.mPinnedHeaderCount = headers.length; - mSettingsAdapter.changeCursor(HOME_SETTINGS_POSITION, createCursor(headers[0], values)); + mSettingsAdapter.changeCursor(HOME_SETTINGS_POSITION, + createCursor(headers[0], getValues())); mSettingsAdapter.changeCursor(DRAWER_SETTINGS_POSITION, createCursor(headers[1], valuesDrawer)); mSettingsAdapter.changeCursor(APP_SETTINGS_POSITION, createCursor(headers[2], valuesApp)); @@ -82,6 +79,30 @@ public class OverviewSettingsPanel { return cursor; } + private String[] getValues() { + Resources res = mLauncher.getResources(); + ArrayList values = new ArrayList(Arrays.asList(new String[]{ + res.getString(R.string.home_screen_search_text), + res.getString(R.string.scroll_effect_text), + res.getString(R.string.icon_labels), + res.getString(R.string.scrolling_wallpaper), + res.getString(R.string.grid_size_text)})); + + // Optionally add additional value based on setting + boolean remoteAppsEnabled = SettingsProvider.getBoolean(mLauncher, null, + R.bool.preferences_interface_homescreen_remote_folder_default); + if (remoteAppsEnabled) { + String remoteAppsName = RemoteFolderManager.getFeatureTitle(res); + if (remoteAppsName != null) { + values.add(remoteAppsName); + } + } + + String[] valuesArr = new String[values.size()]; + values.toArray(valuesArr); + return valuesArr; + } + // One time View setup public void initializeViews() { mOverviewPanel.setAlpha(0f); @@ -169,14 +190,8 @@ public class OverviewSettingsPanel { mSettingsAdapter.changeCursor(0, createCursor(res .getString(R.string.home_screen_settings), new String[]{})); } else { - String[] values = new String[] { - res.getString(R.string.home_screen_search_text), - res.getString(R.string.scroll_effect_text), - res.getString(R.string.icon_labels), - res.getString(R.string.scrolling_wallpaper), - res.getString(R.string.grid_size_text)}; mSettingsAdapter.changeCursor(0, createCursor(res - .getString(R.string.home_screen_settings), values)); + .getString(R.string.home_screen_settings), getValues())); } // Make sure overview panel is drawn above apps customize and collapsed @@ -192,7 +207,6 @@ public class OverviewSettingsPanel { mSettingsAdapter.notifyDataSetInvalidated(); } - class SettingsSimplePanelSlideListener extends SlidingUpPanelLayout.SimplePanelSlideListener { ImageView mAnimatedArrow; diff --git a/src/com/android/launcher3/ShortcutInfo.java b/src/com/android/launcher3/ShortcutInfo.java index 01f79314e..288030d9e 100644 --- a/src/com/android/launcher3/ShortcutInfo.java +++ b/src/com/android/launcher3/ShortcutInfo.java @@ -85,7 +85,7 @@ public class ShortcutInfo extends ItemInfo { /** * The application icon. */ - private Bitmap mIcon; + Bitmap mIcon; /** * Indicates that the icon is disabled due to safe mode restrictions. @@ -243,6 +243,14 @@ public class ShortcutInfo extends ItemInfo { return (status & flag) != 0; } + /** + * Check if this shortcut has a specific flag. + * @param flag flag to check. + * @return true if the flag is present, false otherwise. + */ + public boolean hasFlag(int flag) { + return (flags & flag) != 0; + } public final boolean isPromise() { return hasStatusFlag(FLAG_RESTORED_ICON | FLAG_AUTOINTALL_ICON); diff --git a/src/com/android/launcher3/Utilities.java b/src/com/android/launcher3/Utilities.java index 8fe2e841b..5eecf1b19 100644 --- a/src/com/android/launcher3/Utilities.java +++ b/src/com/android/launcher3/Utilities.java @@ -36,14 +36,14 @@ import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Matrix; import android.graphics.Paint; -import android.graphics.Paint.Style; import android.graphics.PaintFlagsDrawFilter; import android.graphics.Rect; import android.graphics.RectF; -import android.graphics.Typeface; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; import android.graphics.drawable.PaintDrawable; +import android.net.ConnectivityManager; +import android.net.NetworkInfo; import android.os.Build; import android.util.DisplayMetrics; import android.util.Log; @@ -627,4 +627,15 @@ public final class Utilities { return false; } } + + public static boolean isNetworkConnected(Context context) { + ConnectivityManager connectivityManager = (ConnectivityManager)context.getSystemService(Context.CONNECTIVITY_SERVICE); + NetworkInfo activeNetwork = connectivityManager.getActiveNetworkInfo(); + return activeNetwork != null && activeNetwork.isConnected(); + } + + public static boolean isConnectedToWiFi(Context context) { + ConnectivityManager connectivityManager = (ConnectivityManager)context.getSystemService(Context.CONNECTIVITY_SERVICE); + return connectivityManager.getNetworkInfo(ConnectivityManager.TYPE_WIFI).isConnected(); + } } diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java index 71407b9fc..e98f12f6e 100644 --- a/src/com/android/launcher3/Workspace.java +++ b/src/com/android/launcher3/Workspace.java @@ -31,10 +31,7 @@ import android.appwidget.AppWidgetHostView; import android.appwidget.AppWidgetProviderInfo; import android.content.ComponentName; import android.content.Context; -import android.content.Intent; import android.content.SharedPreferences; -import android.content.pm.PackageManager; -import android.content.pm.ResolveInfo; import android.content.res.Resources; import android.content.res.TypedArray; import android.graphics.Bitmap; @@ -81,7 +78,6 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; -import java.util.Map; import java.util.Set; import java.util.concurrent.atomic.AtomicInteger; @@ -398,6 +394,13 @@ public class Workspace extends SmoothPagedView } } + /** + * @return A {@link Set} of {@link Long}s representing ids of the workspace screens + */ + public Set getWorkspaceScreenIds() { + return mWorkspaceScreens.keySet(); + } + // estimate the size of a widget with spans hSpan, vSpan. return MAX_VALUE for each // dimension if unsuccessful public int[] estimateItemSize(int hSpan, int vSpan, @@ -4460,6 +4463,7 @@ public class Workspace extends SmoothPagedView } if (cellLayout != null) { cellLayout.onDropChild(mDragInfo.cell); + cellLayout.setUseTempCoords(false); } } if ((d.cancelled || (beingCalledAfterUninstall && !mUninstallSuccessful)) diff --git a/src/com/android/launcher3/list/SettingsPinnedHeaderAdapter.java b/src/com/android/launcher3/list/SettingsPinnedHeaderAdapter.java index df9a1f05b..a9364d49f 100644 --- a/src/com/android/launcher3/list/SettingsPinnedHeaderAdapter.java +++ b/src/com/android/launcher3/list/SettingsPinnedHeaderAdapter.java @@ -132,6 +132,14 @@ public class SettingsPinnedHeaderAdapter extends PinnedHeaderListAdapter { case 4: updateDynamicGridSizeSettingsItem(v); break; + case 5: + current = SettingsProvider.getBoolean(mContext, + SettingsProvider.SETTINGS_UI_HOMESCREEN_REMOTE_FOLDER, + R.bool.preferences_interface_homescreen_remote_folder_default); + state = current ? res.getString(R.string.setting_state_on) + : res.getString(R.string.setting_state_off); + ((TextView) v.findViewById(R.id.item_state)).setText(state); + break; default: ((TextView) v.findViewById(R.id.item_state)).setText(""); } @@ -308,7 +316,12 @@ public class SettingsPinnedHeaderAdapter extends PinnedHeaderListAdapter { case 4: mLauncher.onClickDynamicGridSizeButton(); break; - + case 5: + boolean newValue = onSettingsBooleanChanged(v, + SettingsProvider.SETTINGS_UI_HOMESCREEN_REMOTE_FOLDER, + R.bool.preferences_interface_homescreen_remote_folder_default); + mLauncher.getRemoteFolderManager().onSettingChanged(newValue); + break; } break; case OverviewSettingsPanel.DRAWER_SETTINGS_POSITION: @@ -372,7 +385,7 @@ public class SettingsPinnedHeaderAdapter extends PinnedHeaderListAdapter { R.bool.preferences_interface_homescreen_search_default); } - private void onSettingsBooleanChanged(View v, String key, int res) { + private boolean onSettingsBooleanChanged(View v, String key, int res) { boolean current = SettingsProvider.getBoolean( mContext, key, res); @@ -384,6 +397,8 @@ public class SettingsPinnedHeaderAdapter extends PinnedHeaderListAdapter { R.string.setting_state_off) : mLauncher.getResources().getString( R.string.setting_state_on); ((TextView) v.findViewById(R.id.item_state)).setText(state); + + return !current; } private void onIconLabelsBooleanChanged(View v, String key, int res) { diff --git a/src/com/android/launcher3/settings/SettingsProvider.java b/src/com/android/launcher3/settings/SettingsProvider.java index 042053cef..841fa3e2a 100644 --- a/src/com/android/launcher3/settings/SettingsProvider.java +++ b/src/com/android/launcher3/settings/SettingsProvider.java @@ -31,6 +31,7 @@ public final class SettingsProvider { public static final String SETTINGS_UI_HOMESCREEN_SCROLLING_WALLPAPER_SCROLL = "ui_homescreen_scrolling_wallpaper_scroll"; public static final String SETTINGS_UI_HOMESCREEN_SCROLLING_PAGE_OUTLINES = "ui_homescreen_scrolling_page_outlines"; public static final String SETTINGS_UI_HOMESCREEN_SCROLLING_FADE_ADJACENT = "ui_homescreen_scrolling_fade_adjacent"; + public static final String SETTINGS_UI_HOMESCREEN_REMOTE_FOLDER = "ui_homescreen_remote_folder"; public static final String SETTINGS_UI_DYNAMIC_GRID_SIZE = "ui_dynamic_grid_size"; public static final String SETTINGS_UI_HOMESCREEN_ROWS = "ui_homescreen_rows"; public static final String SETTINGS_UI_HOMESCREEN_COLUMNS = "ui_homescreen_columns"; diff --git a/src/com/android/launcher3/stats/LauncherStats.java b/src/com/android/launcher3/stats/LauncherStats.java index e1dd1bbc6..5e8cb83d5 100644 --- a/src/com/android/launcher3/stats/LauncherStats.java +++ b/src/com/android/launcher3/stats/LauncherStats.java @@ -16,12 +16,12 @@ package com.android.launcher3.stats; +import android.content.Context; import android.os.Handler; import android.os.HandlerThread; import android.os.Message; import android.text.TextUtils; import android.util.Log; -import com.android.launcher3.LauncherApplication; import com.android.launcher3.stats.internal.db.DatabaseHelper; import com.android.launcher3.stats.internal.model.TrackingEvent; @@ -41,6 +41,9 @@ public class LauncherStats { public static final String ORIGIN_APPDRAWER = "appdrawer"; public static final String ORIGIN_TREB_LONGPRESS = "trebuchet_longpress"; public static final String ORIGIN_CHOOSER = "theme_chooser"; + public static final String ORIGIN_SETTINGS = "settings"; + public static final String ORIGIN_DRAG_DROP = "drag_drop"; + public static final String ORIGIN_FOLDER = "folder"; private static void log(String msg) throws IllegalArgumentException { if (TextUtils.isEmpty(msg)) { @@ -101,17 +104,16 @@ public class LauncherStats { private static LauncherStats sInstance = null; // Members - private static WriteHandlerThread sHandlerThread = new WriteHandlerThread(); + private static WriteHandlerThread sHandlerThread; private static WriteHandler sWriteHandler; private static DatabaseHelper sDatabaseHelper; - private LauncherApplication mApplication; /** * Send a message to the handler to store event data * * @param trackingEvent {@link TrackingEvent} */ - private void sendStoreEventMessage(TrackingEvent trackingEvent) { + protected void sendStoreEventMessage(TrackingEvent trackingEvent) { log("Sending tracking event to handler: " + trackingEvent); Message msg = new Message(); msg.what = MSG_STORE_EVENT; @@ -133,33 +135,38 @@ public class LauncherStats { } } + /** + * Used only for overlay extensions + */ + protected LauncherStats() { } + /** * Constructor * - * @param application {@link LauncherApplication} not null! + * @param context {@link Context} not null! * @throws IllegalArgumentException {@link IllegalArgumentException} */ - private LauncherStats(LauncherApplication application) throws IllegalArgumentException { - if (application == null) { - throw new IllegalArgumentException("'application' cannot be null!"); + private LauncherStats(Context context) throws IllegalArgumentException { + if (context == null) { + throw new IllegalArgumentException("'context' cannot be null!"); } - mApplication = application; - sDatabaseHelper = new DatabaseHelper(application); + sDatabaseHelper = new DatabaseHelper(context); + sHandlerThread = new WriteHandlerThread(); sHandlerThread.start(); sWriteHandler = new WriteHandler(); } /** - * Creates a singleton instance of the stats utility + * Gets a singleton instance of the stats utility * - * @param application {@link LauncherApplication} not null! + * @param context {@link Context} not null! * @return {@link LauncherStats} * @throws IllegalArgumentException {@link IllegalArgumentException} */ - public static LauncherStats createInstance(LauncherApplication application) + public static LauncherStats getInstance(Context context) throws IllegalArgumentException { if (sInstance == null) { - sInstance = new LauncherStats(application); + sInstance = new LauncherStats(context); } return sInstance; } diff --git a/src/com/android/launcher3/stats/internal/model/TrackingEvent.java b/src/com/android/launcher3/stats/internal/model/TrackingEvent.java index 91a9017be..a44d3f148 100644 --- a/src/com/android/launcher3/stats/internal/model/TrackingEvent.java +++ b/src/com/android/launcher3/stats/internal/model/TrackingEvent.java @@ -51,6 +51,14 @@ public class TrackingEvent { WALLPAPER_CHANGE, HOMESCREEN_PAGE, WIDGET, + + // Remote folder specific + REMOTE_FOLDER_DISABLED, + REMOTE_FOLDER_OPENED, + REMOTE_FOLDER_INFO_OPENED, + REMOTE_APP_OPENED, + REMOTE_APP_INSTALLED, + REMOTE_SYNC_TIME } public static final String KEY_ORIGIN = TrackingBundle.KEY_METADATA_ORIGIN; -- cgit v1.2.3