Merge "Add meta data to indicate launcher_dump provider name" into ub-launcher3-dorval
diff --git a/res/layout/widgets_bottom_sheet.xml b/res/layout/widgets_bottom_sheet.xml
index 826235b..c2270d2 100644
--- a/res/layout/widgets_bottom_sheet.xml
+++ b/res/layout/widgets_bottom_sheet.xml
@@ -19,7 +19,6 @@
     android:orientation="vertical"
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
-    android:paddingStart="16dp"
     android:paddingTop="28dp"
     android:background="?android:attr/colorPrimary"
     android:elevation="@dimen/deep_shortcuts_elevation"
diff --git a/src/com/android/launcher3/InfoDropTarget.java b/src/com/android/launcher3/InfoDropTarget.java
index 34adf47..0608fdd 100644
--- a/src/com/android/launcher3/InfoDropTarget.java
+++ b/src/com/android/launcher3/InfoDropTarget.java
@@ -19,6 +19,8 @@
 import android.content.ActivityNotFoundException;
 import android.content.ComponentName;
 import android.content.Context;
+import android.graphics.Rect;
+import android.os.Bundle;
 import android.provider.Settings;
 import android.util.AttributeSet;
 import android.util.Log;
@@ -60,6 +62,11 @@
      */
     public static boolean startDetailsActivityForInfo(
             ItemInfo info, Launcher launcher, DropTargetResultCallback callback) {
+        return startDetailsActivityForInfo(info, launcher, callback, null, null);
+    }
+
+    public static boolean startDetailsActivityForInfo(ItemInfo info, Launcher launcher,
+            DropTargetResultCallback callback, Rect sourceBounds, Bundle opts) {
         boolean result = false;
         ComponentName componentName = null;
         if (info instanceof AppInfo) {
@@ -74,7 +81,7 @@
         if (componentName != null) {
             try {
                 LauncherAppsCompat.getInstance(launcher)
-                        .showAppDetailsForProfile(componentName, info.user);
+                        .showAppDetailsForProfile(componentName, info.user, sourceBounds, opts);
                 result = true;
             } catch (SecurityException | ActivityNotFoundException e) {
                 Toast.makeText(launcher, R.string.activity_not_found, Toast.LENGTH_SHORT).show();
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 3c29f5e..eef578d 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -2555,6 +2555,7 @@
         Intent intent = new Intent(Intent.ACTION_APPLICATION_PREFERENCES)
                 .setPackage(getPackageName());
         intent.setSourceBounds(getViewBounds(v));
+        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
         startActivity(intent, getActivityLaunchOptions(v));
     }
 
@@ -2668,7 +2669,7 @@
     }
 
     @TargetApi(Build.VERSION_CODES.M)
-    private Bundle getActivityLaunchOptions(View v) {
+    public Bundle getActivityLaunchOptions(View v) {
         if (Utilities.ATLEAST_MARSHMALLOW) {
             int left = 0, top = 0;
             int width = v.getMeasuredWidth(), height = v.getMeasuredHeight();
@@ -2694,7 +2695,7 @@
         return null;
     }
 
-    private Rect getViewBounds(View v) {
+    public Rect getViewBounds(View v) {
         int[] pos = new int[2];
         v.getLocationOnScreen(pos);
         return new Rect(pos[0], pos[1], pos[0] + v.getWidth(), pos[1] + v.getHeight());
@@ -3902,6 +3903,7 @@
         // Update AllApps
         if (mAppsView != null) {
             mAppsView.removeApps(appInfos);
+            tryAndUpdatePredictedApps();
         }
     }
 
diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java
index 5bde839..e68e637 100644
--- a/src/com/android/launcher3/LauncherModel.java
+++ b/src/com/android/launcher3/LauncherModel.java
@@ -1062,7 +1062,8 @@
                                     info = c.loadSimpleShortcut();
 
                                     // Shortcuts are only available on the primary profile
-                                    if (pmHelper.isAppSuspended(targetPkg, c.user)) {
+                                    if (!TextUtils.isEmpty(targetPkg)
+                                            && pmHelper.isAppSuspended(targetPkg, c.user)) {
                                         disabledState |= ShortcutInfo.FLAG_DISABLED_SUSPENDED;
                                     }
 
diff --git a/src/com/android/launcher3/LauncherProvider.java b/src/com/android/launcher3/LauncherProvider.java
index 7d85ac1..d4e3171 100644
--- a/src/com/android/launcher3/LauncherProvider.java
+++ b/src/com/android/launcher3/LauncherProvider.java
@@ -124,6 +124,7 @@
         // always available in the main process.
         FileLog.setDir(getContext().getApplicationContext().getFilesDir());
         IconShapeOverride.apply(getContext());
+        SessionCommitReceiver.applyDefaultUserPrefs(getContext());
         return true;
     }
 
diff --git a/src/com/android/launcher3/SessionCommitReceiver.java b/src/com/android/launcher3/SessionCommitReceiver.java
index 203bc25..61bcc17 100644
--- a/src/com/android/launcher3/SessionCommitReceiver.java
+++ b/src/com/android/launcher3/SessionCommitReceiver.java
@@ -16,6 +16,7 @@
 
 package com.android.launcher3;
 
+import android.annotation.TargetApi;
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
@@ -23,39 +24,51 @@
 import android.content.pm.LauncherActivityInfo;
 import android.content.pm.PackageInstaller;
 import android.content.pm.PackageInstaller.SessionInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.AsyncTask;
+import android.os.Build;
 import android.os.Process;
 import android.os.UserHandle;
+import android.provider.Settings;
 import android.text.TextUtils;
 import android.util.Log;
 
 import com.android.launcher3.compat.LauncherAppsCompat;
-import com.android.launcher3.compat.UserManagerCompat;
 
 import java.util.List;
 
 /**
  * BroadcastReceiver to handle session commit intent.
  */
+@TargetApi(Build.VERSION_CODES.O)
 public class SessionCommitReceiver extends BroadcastReceiver {
 
-    private static final long SESSION_IGNORE_DURATION = 3 * 60 * 60 * 1000; // 3 hours
+    private static final String TAG = "SessionCommitReceiver";
+
+    // The content provider for the add to home screen setting. It should be of the format:
+    // <package name>.addtohomescreen
+    private static final String MARKER_PROVIDER_PREFIX = ".addtohomescreen";
 
     // Preference key for automatically adding icon to homescreen.
     public static final String ADD_ICON_PREFERENCE_KEY = "pref_add_icon_to_home";
-
-    private static final String KEY_FIRST_TIME = "first_session_broadcast_time";
+    public static final String ADD_ICON_PREFERENCE_INITIALIZED_KEY =
+            "pref_add_icon_to_home_initialized";
 
     @Override
     public void onReceive(Context context, Intent intent) {
-        if (!isEnabled(context)) {
+        if (!isEnabled(context) || !Utilities.isAtLeastO()) {
             // User has decided to not add icons on homescreen.
             return;
         }
 
         SessionInfo info = intent.getParcelableExtra(PackageInstaller.EXTRA_SESSION);
         UserHandle user = intent.getParcelableExtra(Intent.EXTRA_USER);
-        // TODO: Verify install reason
-        if (TextUtils.isEmpty(info.getAppPackageName())) {
+
+        if (TextUtils.isEmpty(info.getAppPackageName()) ||
+                info.getInstallReason() != PackageManager.INSTALL_REASON_USER) {
             return;
         }
 
@@ -64,17 +77,6 @@
             return;
         }
 
-        // STOPSHIP: Remove this workaround when we start getting proper install reason
-        SharedPreferences prefs = context
-                .getSharedPreferences(LauncherFiles.DEVICE_PREFERENCES_KEY, 0);
-        long now = System.currentTimeMillis();
-        long firstTime = prefs.getLong(KEY_FIRST_TIME, now);
-        prefs.edit().putLong(KEY_FIRST_TIME, firstTime).apply();
-        if ((now - firstTime) < SESSION_IGNORE_DURATION) {
-            Log.d("SessionCommitReceiver", "Temporarily ignoring session broadcast");
-            return;
-        }
-
         List<LauncherActivityInfo> activities = LauncherAppsCompat.getInstance(context)
                 .getActivityList(info.getAppPackageName(), user);
         if (activities == null || activities.isEmpty()) {
@@ -87,4 +89,68 @@
     public static boolean isEnabled(Context context) {
         return Utilities.getPrefs(context).getBoolean(ADD_ICON_PREFERENCE_KEY, true);
     }
+
+    public static void applyDefaultUserPrefs(final Context context) {
+        if (!Utilities.isAtLeastO()) {
+            return;
+        }
+        SharedPreferences prefs = Utilities.getPrefs(context);
+        if (prefs.getAll().isEmpty()) {
+            // This logic assumes that the code is the first thing that is executed (before any
+            // shared preference is written).
+            // TODO: Move this logic to DB upgrade once we have proper support for db downgrade
+            // If it is a fresh start, just apply the default value. We use prefs.isEmpty() to infer
+            // a fresh start as put preferences always contain some values corresponding to current
+            // grid.
+            prefs.edit().putBoolean(ADD_ICON_PREFERENCE_KEY, true).apply();
+        } else if (!prefs.contains(ADD_ICON_PREFERENCE_INITIALIZED_KEY)) {
+            new PrefInitTask(context).executeOnExecutor(Utilities.THREAD_POOL_EXECUTOR);
+        }
+    }
+
+    private static class PrefInitTask extends AsyncTask<Void, Void, Void> {
+        private final Context mContext;
+
+        PrefInitTask(Context context) {
+            mContext = context;
+        }
+
+        @Override
+        protected Void doInBackground(Void... voids) {
+            boolean addIconToHomeScreenEnabled = readValueFromMarketApp();
+            Utilities.getPrefs(mContext).edit()
+                    .putBoolean(ADD_ICON_PREFERENCE_KEY, addIconToHomeScreenEnabled)
+                    .putBoolean(ADD_ICON_PREFERENCE_INITIALIZED_KEY, true)
+                    .apply();
+            return null;
+        }
+
+        public boolean readValueFromMarketApp() {
+            // Get the marget package
+            ResolveInfo ri = mContext.getPackageManager().resolveActivity(
+                    new Intent(Intent.ACTION_MAIN).addCategory(Intent.CATEGORY_APP_MARKET),
+                    PackageManager.MATCH_DEFAULT_ONLY | PackageManager.MATCH_SYSTEM_ONLY);
+            if (ri == null) {
+                return true;
+            }
+
+            Cursor c = null;
+            try {
+                c = mContext.getContentResolver().query(
+                        Uri.parse("content://" + ri.activityInfo.packageName
+                                + MARKER_PROVIDER_PREFIX),
+                        null, null, null, null);
+                if (c.moveToNext()) {
+                    return c.getInt(c.getColumnIndexOrThrow(Settings.NameValueTable.VALUE)) != 0;
+                }
+            } catch (Exception e) {
+                Log.d(TAG, "Error reading add to homescreen preference", e);
+            } finally {
+                if (c != null) {
+                    c.close();
+                }
+            }
+            return true;
+        }
+    }
 }
diff --git a/src/com/android/launcher3/compat/LauncherAppsCompat.java b/src/com/android/launcher3/compat/LauncherAppsCompat.java
index f17d8de..e997a99 100644
--- a/src/com/android/launcher3/compat/LauncherAppsCompat.java
+++ b/src/com/android/launcher3/compat/LauncherAppsCompat.java
@@ -78,7 +78,8 @@
             Rect sourceBounds, Bundle opts);
     public abstract ApplicationInfo getApplicationInfo(
             String packageName, int flags, UserHandle user);
-    public abstract void showAppDetailsForProfile(ComponentName component, UserHandle user);
+    public abstract void showAppDetailsForProfile(ComponentName component, UserHandle user,
+            Rect sourceBounds, Bundle opts);
     public abstract void addOnAppsChangedCallback(OnAppsChangedCallbackCompat listener);
     public abstract void removeOnAppsChangedCallback(OnAppsChangedCallbackCompat listener);
     public abstract boolean isPackageEnabledForProfile(String packageName, UserHandle user);
@@ -142,4 +143,8 @@
             return null;
         }
     }
+
+    public void showAppDetailsForProfile(ComponentName component, UserHandle user) {
+        showAppDetailsForProfile(component, user, null, null);
+    }
 }
diff --git a/src/com/android/launcher3/compat/LauncherAppsCompatVL.java b/src/com/android/launcher3/compat/LauncherAppsCompatVL.java
index 58683db..647c315 100644
--- a/src/com/android/launcher3/compat/LauncherAppsCompatVL.java
+++ b/src/com/android/launcher3/compat/LauncherAppsCompatVL.java
@@ -95,8 +95,9 @@
     }
 
     @Override
-    public void showAppDetailsForProfile(ComponentName component, UserHandle user) {
-        mLauncherApps.startAppDetailsActivity(component, user, null, null);
+    public void showAppDetailsForProfile(ComponentName component, UserHandle user,
+            Rect sourceBounds, Bundle opts) {
+        mLauncherApps.startAppDetailsActivity(component, user, sourceBounds, opts);
     }
 
     @Override
diff --git a/src/com/android/launcher3/graphics/LauncherIcons.java b/src/com/android/launcher3/graphics/LauncherIcons.java
index 746a639..53521f2 100644
--- a/src/com/android/launcher3/graphics/LauncherIcons.java
+++ b/src/com/android/launcher3/graphics/LauncherIcons.java
@@ -265,7 +265,7 @@
             final int top = (textureHeight-height) / 2;
 
             sOldBounds.set(icon.getBounds());
-            if (icon instanceof AdaptiveIconDrawable) {
+            if (Utilities.isAtLeastO() && icon instanceof AdaptiveIconDrawable) {
                 int offset = Math.min(left, top);
                 int size = Math.max(width, height);
                 icon.setBounds(offset, offset, offset + size, offset + size);
diff --git a/src/com/android/launcher3/popup/SystemShortcut.java b/src/com/android/launcher3/popup/SystemShortcut.java
index 81460e4..f158f71 100644
--- a/src/com/android/launcher3/popup/SystemShortcut.java
+++ b/src/com/android/launcher3/popup/SystemShortcut.java
@@ -1,7 +1,9 @@
 package com.android.launcher3.popup;
 
 import android.content.Context;
+import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
+import android.os.Bundle;
 import android.view.View;
 
 import com.android.launcher3.AbstractFloatingView;
@@ -82,7 +84,9 @@
             return new View.OnClickListener() {
                 @Override
                 public void onClick(View view) {
-                    InfoDropTarget.startDetailsActivityForInfo(itemInfo, launcher, null);
+                    Rect sourceBounds = launcher.getViewBounds(view);
+                    Bundle opts = launcher.getActivityLaunchOptions(view);
+                    InfoDropTarget.startDetailsActivityForInfo(itemInfo, launcher, null, sourceBounds, opts);
                 }
             };
         }
diff --git a/src/com/android/launcher3/widget/WidgetsBottomSheet.java b/src/com/android/launcher3/widget/WidgetsBottomSheet.java
index a423154..5fe00c2 100644
--- a/src/com/android/launcher3/widget/WidgetsBottomSheet.java
+++ b/src/com/android/launcher3/widget/WidgetsBottomSheet.java
@@ -133,10 +133,19 @@
             }
         }
 
-        // If there is only one widget, we want to center it instead of left-align.
-        WidgetsBottomSheet.LayoutParams params = (WidgetsBottomSheet.LayoutParams)
-                widgetRow.getLayoutParams();
-        params.gravity = widgets.size() == 1 ? Gravity.CENTER_HORIZONTAL : Gravity.START;
+        if (widgets.size() == 1) {
+            // If there is only one widget, we want to center it instead of left-align.
+            WidgetsBottomSheet.LayoutParams params = (WidgetsBottomSheet.LayoutParams)
+                    widgetRow.getLayoutParams();
+            params.gravity = Gravity.CENTER_HORIZONTAL;
+        } else {
+            // Otherwise, add an empty view to the start as padding (but still scroll edge to edge).
+            View leftPaddingView = LayoutInflater.from(getContext()).inflate(
+                    R.layout.widget_list_divider, widgetRow, false);
+            leftPaddingView.getLayoutParams().width = Utilities.pxFromDp(
+                    16, getResources().getDisplayMetrics());
+            widgetCells.addView(leftPaddingView, 0);
+        }
     }
 
     private void addDivider(ViewGroup parent) {