Merge "Calling start/stop listening in onStart/onStop" into ub-launcher3-burnaby-nyc
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 990bde0..918ae52 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -127,13 +127,6 @@
             android:process=":settings_process">
         </activity>
 
-        <receiver
-            android:name="com.android.launcher3.WallpaperChangedReceiver">
-            <intent-filter>
-                <action android:name="android.intent.action.WALLPAPER_CHANGED" />
-            </intent-filter>
-        </receiver>
-
         <!-- Intent received used to install shortcuts from other applications -->
         <receiver
             android:name="com.android.launcher3.InstallShortcutReceiver"
diff --git a/WallpaperPicker/res/layout/actionbar_set_wallpaper.xml b/WallpaperPicker/res/layout/actionbar_set_wallpaper.xml
index 8e349b7..2b39b09 100644
--- a/WallpaperPicker/res/layout/actionbar_set_wallpaper.xml
+++ b/WallpaperPicker/res/layout/actionbar_set_wallpaper.xml
@@ -28,5 +28,6 @@
     android:drawableLeft="@drawable/ic_actionbar_accept"
     android:drawablePadding="8dp"
     android:gravity="start|center_vertical"
-    android:text="@string/wallpaper_instructions">
+    android:text="@string/wallpaper_instructions"
+    android:enabled="false">
 </com.android.launcher3.AlphaDisableableButton>
diff --git a/WallpaperPicker/res/values/arrays.xml b/WallpaperPicker/res/values/arrays.xml
new file mode 100644
index 0000000..5c10b98
--- /dev/null
+++ b/WallpaperPicker/res/values/arrays.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2016 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<resources>
+    <string-array name="which_wallpaper_options">
+        <item>@string/which_wallpaper_option_home_screen</item>
+        <item>@string/which_wallpaper_option_lock_screen</item>
+        <item>@string/which_wallpaper_option_home_screen_and_lock_screen</item>
+    </string-array>
+</resources>
\ No newline at end of file
diff --git a/WallpaperPicker/res/values/strings.xml b/WallpaperPicker/res/values/strings.xml
index 2bfd476..aef6a1a 100644
--- a/WallpaperPicker/res/values/strings.xml
+++ b/WallpaperPicker/res/values/strings.xml
@@ -53,4 +53,11 @@
     <string name="pick_wallpaper">Wallpapers</string>
     <!-- Title of activity for cropping wallpapers -->
     <string name="crop_wallpaper">Crop wallpaper</string>
+
+    <!-- Option for setting the wallpaper only on the home screen. -->
+    <string name="which_wallpaper_option_home_screen">Home screen</string>
+    <!-- Option for setting the wallpaper only on the lock screen. -->
+    <string name="which_wallpaper_option_lock_screen">Lock screen</string>
+    <!-- Option for setting the wallpaper on both the home screen and lock screen. -->
+    <string name="which_wallpaper_option_home_screen_and_lock_screen">Home screen and lock screen</string>
 </resources>
diff --git a/WallpaperPicker/src/com/android/gallery3d/common/BitmapCropTask.java b/WallpaperPicker/src/com/android/gallery3d/common/BitmapCropTask.java
index 8b51447..027e947 100644
--- a/WallpaperPicker/src/com/android/gallery3d/common/BitmapCropTask.java
+++ b/WallpaperPicker/src/com/android/gallery3d/common/BitmapCropTask.java
@@ -33,7 +33,9 @@
 import android.util.Log;
 import android.widget.Toast;
 
+import com.android.launcher3.NycWallpaperUtils;
 import com.android.launcher3.R;
+import com.android.launcher3.Utilities;
 
 import java.io.BufferedInputStream;
 import java.io.ByteArrayInputStream;
@@ -42,7 +44,7 @@
 import java.io.IOException;
 import java.io.InputStream;
 
-public class BitmapCropTask extends AsyncTask<Void, Void, Boolean> {
+public class BitmapCropTask extends AsyncTask<Integer, Void, Boolean> {
 
     public interface OnBitmapCroppedHandler {
         public void onBitmapCropped(byte[] imageBytes);
@@ -71,15 +73,6 @@
     BitmapCropTask.OnBitmapCroppedHandler mOnBitmapCroppedHandler;
     boolean mNoCrop;
 
-    public BitmapCropTask(Context c, String filePath,
-            RectF cropBounds, int rotation, int outWidth, int outHeight,
-            boolean setWallpaper, boolean saveCroppedBitmap, OnEndCropHandler onEndCropHandler) {
-        mContext = c;
-        mInFilePath = filePath;
-        init(cropBounds, rotation,
-                outWidth, outHeight, setWallpaper, saveCroppedBitmap, onEndCropHandler);
-    }
-
     public BitmapCropTask(byte[] imageBytes,
             RectF cropBounds, int rotation, int outWidth, int outHeight,
             boolean setWallpaper, boolean saveCroppedBitmap, OnEndCropHandler onEndCropHandler) {
@@ -175,7 +168,7 @@
     public Bitmap getCroppedBitmap() {
         return mCroppedBitmap;
     }
-    public boolean cropBitmap() {
+    public boolean cropBitmap(int whichWallpaper) {
         boolean failure = false;
 
 
@@ -189,7 +182,11 @@
             try {
                 InputStream is = regenerateInputStream();
                 if (is != null) {
-                    wallpaperManager.setStream(is);
+                    if (!Utilities.isNycOrAbove()) {
+                        wallpaperManager.setStream(is);
+                    } else {
+                        NycWallpaperUtils.setStream(mContext, is, null, true, whichWallpaper);
+                    }
                     Utils.closeSilently(is);
                 }
             } catch (IOException e) {
@@ -375,7 +372,13 @@
                 if (mSetWallpaper && wallpaperManager != null) {
                     try {
                         byte[] outByteArray = tmpOut.toByteArray();
-                        wallpaperManager.setStream(new ByteArrayInputStream(outByteArray));
+                        if (!Utilities.isNycOrAbove()) {
+                            wallpaperManager.setStream(new ByteArrayInputStream(outByteArray));
+                        } else {
+                            NycWallpaperUtils.setStream(mContext,
+                                    new ByteArrayInputStream(outByteArray), null, true,
+                                    whichWallpaper);
+                        }
                         if (mOnBitmapCroppedHandler != null) {
                             mOnBitmapCroppedHandler.onBitmapCropped(outByteArray);
                         }
@@ -393,8 +396,8 @@
     }
 
     @Override
-    protected Boolean doInBackground(Void... params) {
-        return cropBitmap();
+    protected Boolean doInBackground(Integer... params) {
+        return cropBitmap(params.length == 0 ? NycWallpaperUtils.FLAG_SET_SYSTEM : params[0]);
     }
 
     @Override
diff --git a/WallpaperPicker/src/com/android/launcher3/NycWallpaperUtils.java b/WallpaperPicker/src/com/android/launcher3/NycWallpaperUtils.java
new file mode 100644
index 0000000..abac830
--- /dev/null
+++ b/WallpaperPicker/src/com/android/launcher3/NycWallpaperUtils.java
@@ -0,0 +1,79 @@
+package com.android.launcher3;
+
+import android.app.AlertDialog;
+import android.app.WallpaperManager;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.graphics.Rect;
+import android.os.AsyncTask;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
+/**
+ * Utility class used to help set lockscreen wallpapers on N+.
+ */
+public class NycWallpaperUtils {
+    public static final int FLAG_SET_SYSTEM = 1 << 0; // TODO: use WallpaperManager.FLAG_SET_SYSTEM
+    public static final int FLAG_SET_LOCK = 1 << 1; // TODO: use WallpaperManager.FLAG_SET_LOCK
+
+    /**
+     * Calls cropTask.execute(), once the user has selected which wallpaper to set. On pre-N
+     * devices, the prompt is not displayed since there is no API to set the lockscreen wallpaper.
+     */
+    public static void executeCropTaskAfterPrompt(
+            Context context, final AsyncTask<Integer, ?, ?> cropTask,
+            DialogInterface.OnCancelListener onCancelListener) {
+        if (Utilities.isNycOrAbove()) {
+            new AlertDialog.Builder(context)
+                    .setTitle(R.string.wallpaper_instructions)
+                    .setItems(R.array.which_wallpaper_options, new DialogInterface.OnClickListener() {
+                        @Override
+                        public void onClick(DialogInterface dialog, int selectedItemIndex) {
+                            int whichWallpaper;
+                            if (selectedItemIndex == 0) {
+                                whichWallpaper = FLAG_SET_SYSTEM;
+                            } else if (selectedItemIndex == 1) {
+                                whichWallpaper = FLAG_SET_LOCK;
+                            } else {
+                                whichWallpaper = FLAG_SET_SYSTEM | FLAG_SET_LOCK;
+                            }
+                            cropTask.execute(whichWallpaper);
+                        }
+                    })
+                    .setOnCancelListener(onCancelListener)
+                    .show();
+        } else {
+            cropTask.execute(FLAG_SET_SYSTEM);
+        }
+    }
+
+    public static void setStream(Context context, final InputStream data, Rect visibleCropHint,
+            boolean allowBackup, int whichWallpaper) throws IOException {
+        WallpaperManager wallpaperManager = WallpaperManager.getInstance(context);
+        try {
+            // TODO: use mWallpaperManager.setStream(data, visibleCropHint, allowBackup, which)
+            // without needing reflection.
+            Method setStream = WallpaperManager.class.getMethod("setStream", InputStream.class,
+                    Rect.class, boolean.class, int.class);
+            setStream.invoke(wallpaperManager, data, visibleCropHint, allowBackup, whichWallpaper);
+        } catch (NoSuchMethodException | InvocationTargetException | IllegalAccessException e) {
+            // Fall back to previous implementation (set system)
+            wallpaperManager.setStream(data);
+        }
+    }
+
+    public static void clear(Context context, int whichWallpaper) throws IOException {
+        WallpaperManager wallpaperManager = WallpaperManager.getInstance(context);
+        try {
+            // TODO: use mWallpaperManager.clear(whichWallpaper) without needing reflection.
+            Method clear = WallpaperManager.class.getMethod("clear", int.class);
+            clear.invoke(wallpaperManager, whichWallpaper);
+        } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
+            // Fall back to previous implementation (clear system)
+            wallpaperManager.clear();
+        }
+    }
+}
\ No newline at end of file
diff --git a/WallpaperPicker/src/com/android/launcher3/WallpaperCropActivity.java b/WallpaperPicker/src/com/android/launcher3/WallpaperCropActivity.java
index 75bdb8a..364cdb8 100644
--- a/WallpaperPicker/src/com/android/launcher3/WallpaperCropActivity.java
+++ b/WallpaperPicker/src/com/android/launcher3/WallpaperCropActivity.java
@@ -21,6 +21,7 @@
 import android.app.Activity;
 import android.app.WallpaperManager;
 import android.content.Context;
+import android.content.DialogInterface;
 import android.content.Intent;
 import android.content.SharedPreferences;
 import android.content.res.Configuration;
@@ -86,6 +87,18 @@
     @Thunk Set<Bitmap> mReusableBitmaps =
             Collections.newSetFromMap(new WeakHashMap<Bitmap, Boolean>());
 
+    private final DialogInterface.OnCancelListener mOnDialogCancelListener =
+            new DialogInterface.OnCancelListener() {
+                @Override
+                public void onCancel(DialogInterface dialog) {
+                    getActionBar().show();
+                    View wallpaperStrip = findViewById(R.id.wallpaper_strip);
+                    if (wallpaperStrip != null) {
+                        wallpaperStrip.setVisibility(View.VISIBLE);
+                    }
+                }
+            };
+
     @Override
     public void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
@@ -242,6 +255,10 @@
         }
     }
 
+    public DialogInterface.OnCancelListener getOnDialogCancelListener() {
+        return mOnDialogCancelListener;
+    }
+
     protected void onLoadRequestComplete(LoadRequest req, boolean success) {
         mCurrentLoadRequest = null;
         if (success) {
@@ -326,7 +343,7 @@
         };
         cropTask.setOnEndRunnable(onEndCrop);
         cropTask.setNoCrop(true);
-        cropTask.execute();
+        NycWallpaperUtils.executeCropTaskAfterPrompt(this, cropTask, getOnDialogCancelListener());
     }
 
     protected void cropImageAndSetWallpaper(Resources res, int resId,
@@ -335,8 +352,7 @@
         // this device
         int rotation = BitmapUtils.getRotationFromExif(res, resId);
         Point inSize = mCropView.getSourceDimensions();
-        Point outSize = WallpaperUtils.getDefaultWallpaperSize(getResources(),
-                getWindowManager());
+        Point outSize = WallpaperUtils.getDefaultWallpaperSize(getResources(), getWindowManager());
         RectF crop = Utils.getMaxCropRect(
                 inSize.x, inSize.y, outSize.x, outSize.y, false);
         BitmapCropTask.OnEndCropHandler onEndCrop = new BitmapCropTask.OnEndCropHandler() {
@@ -355,7 +371,7 @@
         };
         BitmapCropTask cropTask = new BitmapCropTask(getContext(), res, resId,
                 crop, rotation, outSize.x, outSize.y, true, false, onEndCrop);
-        cropTask.execute();
+        NycWallpaperUtils.executeCropTaskAfterPrompt(this, cropTask, getOnDialogCancelListener());
     }
 
     @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
diff --git a/WallpaperPicker/src/com/android/launcher3/WallpaperPickerActivity.java b/WallpaperPicker/src/com/android/launcher3/WallpaperPickerActivity.java
index 4998bf4..5c6113f 100644
--- a/WallpaperPicker/src/com/android/launcher3/WallpaperPickerActivity.java
+++ b/WallpaperPicker/src/com/android/launcher3/WallpaperPickerActivity.java
@@ -44,6 +44,7 @@
 import android.os.Bundle;
 import android.os.Process;
 import android.provider.MediaStore;
+import android.support.annotation.NonNull;
 import android.util.Log;
 import android.util.Pair;
 import android.view.ActionMode;
@@ -77,13 +78,15 @@
 import com.android.photos.BitmapRegionTileSource;
 import com.android.photos.BitmapRegionTileSource.BitmapSource;
 
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
 import java.io.File;
 import java.io.FileOutputStream;
 import java.io.IOException;
 import java.util.ArrayList;
 
 public class WallpaperPickerActivity extends WallpaperCropActivity {
-    static final String TAG = "Launcher.WallpaperPickerActivity";
+    static final String TAG = "WallpaperPickerActivity";
 
     public static final int IMAGE_PICK = 5;
     public static final int PICK_WALLPAPER_THIRD_PARTY_ACTIVITY = 6;
@@ -317,15 +320,75 @@
             a.onLoadRequestComplete(req, true);
         }
         @Override
-        public void onSave(WallpaperPickerActivity a) {
-            try {
-                WallpaperManager.getInstance(a.getContext()).clear();
-                a.setResult(Activity.RESULT_OK);
-            } catch (IOException e) {
-                Log.w("Setting wallpaper to default threw exception", e);
+        public void onSave(final WallpaperPickerActivity a) {
+            if (!Utilities.isNycOrAbove()) {
+                try {
+                    WallpaperManager.getInstance(a.getContext()).clear();
+                    a.setResult(Activity.RESULT_OK);
+                } catch (IOException e) {
+                    Log.e(TAG, "Setting wallpaper to default threw exception", e);
+                } catch (SecurityException e) {
+                    Log.w(TAG, "Setting wallpaper to default threw exception", e);
+                    // In this case, clearing worked; the exception was thrown afterwards.
+                    a.setResult(Activity.RESULT_OK);
+                }
+                a.finish();
+            } else {
+                BitmapCropTask.OnEndCropHandler onEndCropHandler
+                        = new BitmapCropTask.OnEndCropHandler() {
+                    @Override
+                    public void run(boolean cropSucceeded) {
+                        if (cropSucceeded) {
+                            a.setResult(Activity.RESULT_OK);
+                        }
+                        a.finish();
+                    }
+                };
+                BitmapCropTask setWallpaperTask = getDefaultWallpaperCropTask(a, onEndCropHandler);
+
+                NycWallpaperUtils.executeCropTaskAfterPrompt(a, setWallpaperTask,
+                        a.getOnDialogCancelListener());
             }
-            a.finish();
         }
+
+        @NonNull
+        private BitmapCropTask getDefaultWallpaperCropTask(final WallpaperPickerActivity a,
+                final BitmapCropTask.OnEndCropHandler onEndCropHandler) {
+            return new BitmapCropTask(a, null, null, -1, -1, -1,
+                    true, false, onEndCropHandler) {
+                @Override
+                protected Boolean doInBackground(Integer... params) {
+                    int whichWallpaper = params[0];
+                    boolean succeeded = true;
+                    try {
+                        if (whichWallpaper == NycWallpaperUtils.FLAG_SET_LOCK) {
+                            Bitmap defaultWallpaper = ((BitmapDrawable) WallpaperManager
+                                    .getInstance(a.getApplicationContext()).getBuiltInDrawable())
+                                    .getBitmap();
+                            ByteArrayOutputStream tmpOut = new ByteArrayOutputStream(2048);
+                            if (defaultWallpaper.compress(Bitmap.CompressFormat.PNG, 100,
+                                    tmpOut)) {
+                                byte[] outByteArray = tmpOut.toByteArray();
+                                NycWallpaperUtils.setStream(a.getApplicationContext(),
+                                        new ByteArrayInputStream(outByteArray), null, true,
+                                        NycWallpaperUtils.FLAG_SET_LOCK);
+                            }
+                        } else {
+                            NycWallpaperUtils.clear(a, whichWallpaper);
+                        }
+                    } catch (IOException e) {
+                        Log.e(TAG, "Setting wallpaper to default threw exception", e);
+                        succeeded = false;
+                    } catch (SecurityException e) {
+                        Log.w(TAG, "Setting wallpaper to default threw exception", e);
+                        // In this case, clearing worked; the exception was thrown afterwards.
+                        succeeded = true;
+                    }
+                    return succeeded;
+                }
+            };
+        }
+
         @Override
         public boolean isSelectable() {
             return true;
@@ -442,10 +505,10 @@
                     }
                     return;
                 }
-                setWallpaperButtonEnabled(true);
                 WallpaperTileInfo info = (WallpaperTileInfo) v.getTag();
                 if (info.isSelectable() && v.getVisibility() == View.VISIBLE) {
                     selectTile(v);
+                    setWallpaperButtonEnabled(true);
                 }
                 info.onClick(WallpaperPickerActivity.this);
             }
@@ -561,7 +624,7 @@
                 new View.OnClickListener() {
                     @Override
                     public void onClick(View v) {
-                        // Ensure that a tile is slelected and loaded.
+                        // Ensure that a tile is selected and loaded.
                         if (mSelectedTile != null && mCropView.getTileSource() != null) {
                             // Prevent user from selecting any new tile.
                             mWallpaperStrip.setVisibility(View.GONE);
@@ -570,9 +633,9 @@
                             WallpaperTileInfo info = (WallpaperTileInfo) mSelectedTile.getTag();
                             info.onSave(WallpaperPickerActivity.this);
                         } else {
-                            // no tile was selected, so we just finish the activity and go back
-                            setResult(Activity.RESULT_OK);
-                            finish();
+                            // This case shouldn't be possible, since "Set wallpaper" is disabled
+                            // until user clicks on a title.
+                            Log.w(TAG, "\"Set wallpaper\" was clicked when no tile was selected");
                         }
                     }
                 });
@@ -849,7 +912,7 @@
                 (int) rotatedBounds[0], (int) rotatedBounds[1], width, height, leftAligned);
         cropTask.setCropBounds(cropRect);
 
-        if (cropTask.cropBitmap()) {
+        if (cropTask.cropBitmap(NycWallpaperUtils.FLAG_SET_SYSTEM)) {
             return cropTask.getCroppedBitmap();
         } else {
             return null;
diff --git a/src/com/android/launcher3/AppInfo.java b/src/com/android/launcher3/AppInfo.java
index 427c9c8..c7a6ae4 100644
--- a/src/com/android/launcher3/AppInfo.java
+++ b/src/com/android/launcher3/AppInfo.java
@@ -26,6 +26,7 @@
 import com.android.launcher3.compat.UserHandleCompat;
 import com.android.launcher3.compat.UserManagerCompat;
 import com.android.launcher3.util.ComponentKey;
+import com.android.launcher3.util.PackageManagerHelper;
 
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -89,7 +90,7 @@
         this.componentName = info.getComponentName();
         this.container = ItemInfo.NO_ID;
         flags = initFlags(info);
-        if ((info.getApplicationInfo().flags & LauncherActivityInfoCompat.FLAG_SUSPENDED) != 0) {
+        if (PackageManagerHelper.isAppSuspended(info.getApplicationInfo())) {
             isDisabled |= ShortcutInfo.FLAG_DISABLED_SUSPENDED;
         }
         if (quietModeEnabled) {
diff --git a/src/com/android/launcher3/LauncherAppState.java b/src/com/android/launcher3/LauncherAppState.java
index 1f7bbe0..e1ade10 100644
--- a/src/com/android/launcher3/LauncherAppState.java
+++ b/src/com/android/launcher3/LauncherAppState.java
@@ -106,6 +106,9 @@
         sContext.registerReceiver(mModel, filter);
         UserManagerCompat.getInstance(sContext).enableAndResetCache();
         new ConfigMonitor(sContext).register();
+
+        sContext.registerReceiver(
+                new WallpaperChangedReceiver(), new IntentFilter(Intent.ACTION_WALLPAPER_CHANGED));
     }
 
     /**
diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java
index 97fc2dd..c8860e3 100644
--- a/src/com/android/launcher3/LauncherModel.java
+++ b/src/com/android/launcher3/LauncherModel.java
@@ -33,7 +33,6 @@
 import android.database.Cursor;
 import android.graphics.Bitmap;
 import android.net.Uri;
-import android.os.Environment;
 import android.os.Handler;
 import android.os.HandlerThread;
 import android.os.Looper;
@@ -60,6 +59,7 @@
 import com.android.launcher3.util.FlagOp;
 import com.android.launcher3.util.LongArrayMap;
 import com.android.launcher3.util.ManagedProfileHeuristic;
+import com.android.launcher3.util.PackageManagerHelper;
 import com.android.launcher3.util.StringFilter;
 import com.android.launcher3.util.Thunk;
 
@@ -1825,6 +1825,7 @@
                     long serialNumber;
                     Intent intent;
                     UserHandleCompat user;
+                    String targetPackage;
 
                     while (!mStopped && c.moveToNext()) {
                         try {
@@ -1843,6 +1844,7 @@
                                 int promiseType = c.getInt(restoredIndex);
                                 int disabledState = 0;
                                 boolean itemReplaced = false;
+                                targetPackage = null;
                                 if (user == null) {
                                     // User has been deleted remove the item.
                                     itemsToRemove.add(id);
@@ -1856,6 +1858,9 @@
                                                 cn.getPackageName(), user);
                                         boolean validComponent = validPkg &&
                                                 launcherApps.isActivityEnabledForProfile(cn, user);
+                                        if (validPkg) {
+                                            targetPackage = cn.getPackageName();
+                                        }
 
                                         if (validComponent) {
                                             if (restored) {
@@ -1863,13 +1868,8 @@
                                                 restoredRows.add(id);
                                                 restored = false;
                                             }
-                                            boolean isSuspended = launcherApps.isPackageSuspendedForProfile(
-                                                    cn.getPackageName(), user);
-                                            if (isSuspended) {
-                                                disabledState = ShortcutInfo.FLAG_DISABLED_SUSPENDED;
-                                            }
                                             if (quietMode.get(serialNumber)) {
-                                                disabledState |= ShortcutInfo.FLAG_DISABLED_QUIET_USER;
+                                                disabledState = ShortcutInfo.FLAG_DISABLED_QUIET_USER;
                                             }
                                         } else if (validPkg) {
                                             intent = null;
@@ -1939,9 +1939,8 @@
                                                 itemsToRemove.add(id);
                                                 continue;
                                             }
-                                        } else if (launcherApps.isAppEnabled(
-                                                manager, cn.getPackageName(),
-                                                PackageManager.GET_UNINSTALLED_PACKAGES)) {
+                                        } else if (PackageManagerHelper.isAppOnSdcard(
+                                                manager, cn.getPackageName())) {
                                             // Package is present but not available.
                                             allowMissingTarget = true;
                                             disabledState = ShortcutInfo.FLAG_DISABLED_NOT_AVAILABLE;
@@ -1984,7 +1983,7 @@
 
                                 if (itemReplaced) {
                                     if (user.equals(UserHandleCompat.myUserHandle())) {
-                                        info = getAppShortcutInfo(manager, intent, user, context, null,
+                                        info = getAppShortcutInfo(intent, user, context, null,
                                                 cursorIconInfo.iconIndex, titleIndex,
                                                 false, useLowResIcon);
                                     } else {
@@ -2007,12 +2006,17 @@
                                     }
                                 } else if (itemType ==
                                         LauncherSettings.Favorites.ITEM_TYPE_APPLICATION) {
-                                    info = getAppShortcutInfo(manager, intent, user, context, c,
+                                    info = getAppShortcutInfo(intent, user, context, c,
                                             cursorIconInfo.iconIndex, titleIndex,
                                             allowMissingTarget, useLowResIcon);
                                 } else {
                                     info = getShortcutInfo(c, context, titleIndex, cursorIconInfo);
 
+                                    // Shortcuts are only available on the primary profile
+                                    if (PackageManagerHelper.isAppSuspended(manager, targetPackage)) {
+                                        disabledState |= ShortcutInfo.FLAG_DISABLED_SUSPENDED;
+                                    }
+
                                     // App shortcuts that used to be automatically added to Launcher
                                     // didn't always have the correct intent flags set, so do that
                                     // here
@@ -2040,7 +2044,7 @@
                                     if (info.promisedIntent != null) {
                                         info.promisedIntent.putExtra(ItemInfo.EXTRA_PROFILE, serialNumber);
                                     }
-                                    info.isDisabled = disabledState;
+                                    info.isDisabled |= disabledState;
                                     if (isSafeMode && !Utilities.isSystemApp(context, intent)) {
                                         info.isDisabled |= ShortcutInfo.FLAG_DISABLED_SAFEMODE;
                                     }
@@ -2957,10 +2961,7 @@
                     packagesUnavailable.clear();
                     for (String pkg : entry.getValue()) {
                         if (!launcherApps.isPackageEnabledForProfile(pkg, user)) {
-                            boolean packageOnSdcard = launcherApps.isAppEnabled(
-                                    manager, pkg, PackageManager.GET_UNINSTALLED_PACKAGES);
-                            if (packageOnSdcard) {
-                                Launcher.addDumpLog(TAG, "Package found on sd-card: " + pkg, true);
+                            if (PackageManagerHelper.isAppOnSdcard(manager, pkg)) {
                                 packagesUnavailable.add(pkg);
                             } else {
                                 Launcher.addDumpLog(TAG, "Package not found: " + pkg, true);
@@ -3447,7 +3448,7 @@
      *
      * If c is not null, then it will be used to fill in missing data like the title and icon.
      */
-    public ShortcutInfo getAppShortcutInfo(PackageManager manager, Intent intent,
+    public ShortcutInfo getAppShortcutInfo(Intent intent,
             UserHandleCompat user, Context context, Cursor c, int iconIndex, int titleIndex,
             boolean allowMissingTarget, boolean useLowResIcon) {
         if (user == null) {
@@ -3477,6 +3478,10 @@
             info.setIcon(icon == null ? mIconCache.getDefaultIcon(user) : icon);
         }
 
+        if (lai != null && PackageManagerHelper.isAppSuspended(lai.getApplicationInfo())) {
+            info.isDisabled = ShortcutInfo.FLAG_DISABLED_SUSPENDED;
+        }
+
         // from the db
         if (TextUtils.isEmpty(info.title) && c != null) {
             info.title =  Utilities.trim(c.getString(titleIndex));
diff --git a/src/com/android/launcher3/compat/LauncherActivityInfoCompat.java b/src/com/android/launcher3/compat/LauncherActivityInfoCompat.java
index aaf756e..0bc9588 100644
--- a/src/com/android/launcher3/compat/LauncherActivityInfoCompat.java
+++ b/src/com/android/launcher3/compat/LauncherActivityInfoCompat.java
@@ -24,8 +24,6 @@
 
 public abstract class LauncherActivityInfoCompat {
 
-    public static final int FLAG_SUSPENDED = 1<<30;
-
     LauncherActivityInfoCompat() {
     }
 
diff --git a/src/com/android/launcher3/compat/LauncherAppsCompat.java b/src/com/android/launcher3/compat/LauncherAppsCompat.java
index bc900bc..237a9e9 100644
--- a/src/com/android/launcher3/compat/LauncherAppsCompat.java
+++ b/src/com/android/launcher3/compat/LauncherAppsCompat.java
@@ -19,9 +19,6 @@
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageManager;
-import android.content.pm.PackageManager.NameNotFoundException;
 import android.graphics.Rect;
 import android.os.Bundle;
 
@@ -59,9 +56,7 @@
     public static LauncherAppsCompat getInstance(Context context) {
         synchronized (sInstanceLock) {
             if (sInstance == null) {
-                if (Utilities.isNycOrAbove()) {
-                    sInstance = new LauncherAppsCompatVN(context.getApplicationContext());
-                } else if (Utilities.ATLEAST_LOLLIPOP) {
+                if (Utilities.ATLEAST_LOLLIPOP) {
                     sInstance = new LauncherAppsCompatVL(context.getApplicationContext());
                 } else {
                     sInstance = new LauncherAppsCompatV16(context.getApplicationContext());
@@ -84,13 +79,4 @@
     public abstract boolean isActivityEnabledForProfile(ComponentName component,
             UserHandleCompat user);
     public abstract boolean isPackageSuspendedForProfile(String packageName, UserHandleCompat user);
-
-    public boolean isAppEnabled(PackageManager pm, String packageName, int flags) {
-        try {
-            ApplicationInfo info = pm.getApplicationInfo(packageName, flags);
-            return info != null && info.enabled;
-        } catch (NameNotFoundException e) {
-            return false;
-        }
-    }
 }
diff --git a/src/com/android/launcher3/compat/LauncherAppsCompatV16.java b/src/com/android/launcher3/compat/LauncherAppsCompatV16.java
index 2d0778d..4e2fc05 100644
--- a/src/com/android/launcher3/compat/LauncherAppsCompatV16.java
+++ b/src/com/android/launcher3/compat/LauncherAppsCompatV16.java
@@ -27,11 +27,11 @@
 import android.content.pm.ResolveInfo;
 import android.graphics.Rect;
 import android.net.Uri;
-import android.os.Build;
 import android.os.Bundle;
 import android.provider.Settings;
 
 import com.android.launcher3.Utilities;
+import com.android.launcher3.util.PackageManagerHelper;
 import com.android.launcher3.util.Thunk;
 
 import java.util.ArrayList;
@@ -114,7 +114,7 @@
     }
 
     public boolean isPackageEnabledForProfile(String packageName, UserHandleCompat user) {
-        return isAppEnabled(mPm, packageName, 0);
+        return PackageManagerHelper.isAppEnabled(mPm, packageName);
     }
 
     public boolean isActivityEnabledForProfile(ComponentName component, UserHandleCompat user) {
diff --git a/src/com/android/launcher3/compat/LauncherAppsCompatVN.java b/src/com/android/launcher3/compat/LauncherAppsCompatVN.java
deleted file mode 100644
index 0d883b6..0000000
--- a/src/com/android/launcher3/compat/LauncherAppsCompatVN.java
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.launcher3.compat;
-
-import android.content.Context;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.LauncherApps;
-import android.os.UserHandle;
-import android.util.Log;
-
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
-
-//TODO: Once gogole3 SDK is updated to N, add @TargetApi(Build.VERSION_CODES.N)
-public class LauncherAppsCompatVN extends LauncherAppsCompatVL {
-
-    private static final String TAG = "LauncherAppsCompatVN";
-
-    LauncherAppsCompatVN(Context context) {
-        super(context);
-    }
-
-    @Override
-    public boolean isPackageSuspendedForProfile(String packageName, UserHandleCompat user) {
-        if (user != null && packageName != null) {
-            try {
-                //TODO: Replace with proper API call once google3 SDK is updated.
-                Method getApplicationInfoMethod = LauncherApps.class.getMethod("getApplicationInfo",
-                        String.class, int.class, UserHandle.class);
-
-                ApplicationInfo info = (ApplicationInfo) getApplicationInfoMethod.invoke(
-                        mLauncherApps, packageName, 0, user.getUser());
-                if (info != null) {
-                    return (info.flags & LauncherActivityInfoCompat.FLAG_SUSPENDED) != 0;
-                }
-            } catch (NoSuchMethodError | NoSuchMethodException | IllegalAccessException
-                    | IllegalArgumentException | InvocationTargetException e) {
-                Log.e(TAG, "Running on N without getApplicationInfo", e);
-            }
-        }
-        return false;
-    }
-}
diff --git a/src/com/android/launcher3/util/PackageManagerHelper.java b/src/com/android/launcher3/util/PackageManagerHelper.java
new file mode 100644
index 0000000..d034572
--- /dev/null
+++ b/src/com/android/launcher3/util/PackageManagerHelper.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.util;
+
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+
+/**
+ * Utility methods using package manager
+ */
+public class PackageManagerHelper {
+
+    private static final int FLAG_SUSPENDED = 1<<30;
+
+    /**
+     * Returns true if the app can possibly be on the SDCard. This is just a workaround and doesn't
+     * guarantee that the app is on SD card.
+     */
+    public static boolean isAppOnSdcard(PackageManager pm, String packageName) {
+        return isAppEnabled(pm, packageName, PackageManager.GET_UNINSTALLED_PACKAGES);
+    }
+
+    public static boolean isAppEnabled(PackageManager pm, String packageName) {
+        return isAppEnabled(pm, packageName, 0);
+    }
+
+    public static boolean isAppEnabled(PackageManager pm, String packageName, int flags) {
+        try {
+            ApplicationInfo info = pm.getApplicationInfo(packageName, flags);
+            return info != null && info.enabled;
+        } catch (PackageManager.NameNotFoundException e) {
+            return false;
+        }
+    }
+
+    public static boolean isAppSuspended(PackageManager pm, String packageName) {
+        try {
+            ApplicationInfo info = pm.getApplicationInfo(packageName, 0);
+            return info != null && isAppSuspended(info);
+        } catch (PackageManager.NameNotFoundException e) {
+            return false;
+        }
+    }
+
+    public static boolean isAppSuspended(ApplicationInfo info) {
+        return (info.flags & FLAG_SUSPENDED) != 0;
+    }
+}