Snap for 7408786 from 52a82bac34e91f0ea803f1774393a6be921001cc to sc-v2-release

Change-Id: I579f1cec3cb044b0151b6728b0e211a137c34f1c
diff --git a/go/quickstep/src/com/android/quickstep/TaskOverlayFactoryGo.java b/go/quickstep/src/com/android/quickstep/TaskOverlayFactoryGo.java
index 117b8e6..754782b 100644
--- a/go/quickstep/src/com/android/quickstep/TaskOverlayFactoryGo.java
+++ b/go/quickstep/src/com/android/quickstep/TaskOverlayFactoryGo.java
@@ -25,7 +25,6 @@
 import android.content.Context;
 import android.content.Intent;
 import android.graphics.Matrix;
-import android.net.Uri;
 import android.os.SystemClock;
 import android.os.UserManager;
 import android.provider.Settings;
@@ -33,7 +32,6 @@
 
 import androidx.annotation.VisibleForTesting;
 
-import com.android.launcher3.BuildConfig;
 import com.android.launcher3.R;
 import com.android.quickstep.util.AssistContentRequester;
 import com.android.quickstep.views.OverviewActionsView;
@@ -54,9 +52,6 @@
     public static final String ACTIONS_ERROR_CODE = "niu_actions_app_error_code";
     public static final int ERROR_PERMISSIONS = 1;
     private static final String TAG = "TaskOverlayFactoryGo";
-    private static final String URI_AUTHORITY =
-            BuildConfig.APPLICATION_ID + ".overview.fileprovider";
-    private static final String FAKE_FILEPATH = "shared_images/null.png";
 
     // Empty constructor required for ResourceBasedOverride
     public TaskOverlayFactoryGo(Context context) {}
@@ -138,15 +133,6 @@
                 mImageApi.shareAsDataWithExplicitIntent(/* crop */ null, intent);
             } else {
                 intent.putExtra(ACTIONS_ERROR_CODE, ERROR_PERMISSIONS);
-                // The Intent recipient expects an image URI, and omitting one or using a
-                // completely invalid URI will cause the Intent parsing to crash.
-                // So we construct a URI for a nonexistent image.
-                Uri uri = new Uri.Builder()
-                        .scheme(ContentResolver.SCHEME_CONTENT)
-                        .authority(URI_AUTHORITY)
-                        .path(FAKE_FILEPATH)
-                        .build();
-                intent.setData(uri);
                 mApplicationContext.startActivity(intent);
             }
         }
diff --git a/quickstep/src/com/android/launcher3/model/WellbeingModel.java b/quickstep/src/com/android/launcher3/model/WellbeingModel.java
index 995c4b0..154b78b 100644
--- a/quickstep/src/com/android/launcher3/model/WellbeingModel.java
+++ b/quickstep/src/com/android/launcher3/model/WellbeingModel.java
@@ -319,12 +319,17 @@
 
     @WorkerThread
     private void updateActionsWithRetry(int retryCount, @Nullable String packageName) {
+        if (DEBUG || mIsInTest) {
+            Log.i(TAG,
+                    "updateActionsWithRetry(); retryCount: " + retryCount + ", package: "
+                            + packageName);
+        }
         String[] packageNames = TextUtils.isEmpty(packageName)
-                ?  mContext.getSystemService(LauncherApps.class)
+                ? mContext.getSystemService(LauncherApps.class)
                 .getActivityList(null, Process.myUserHandle()).stream()
                 .map(li -> li.getApplicationInfo().packageName).distinct()
                 .toArray(String[]::new)
-                : new String[] { packageName };
+                : new String[]{packageName};
 
         mWorkerHandler.removeCallbacksAndMessages(packageName);
         if (updateActions(packageNames)) {
@@ -335,12 +340,16 @@
             return;
         }
         mWorkerHandler.postDelayed(
-                () -> updateActionsWithRetry(retryCount + 1, packageName),
+                () -> {
+                    if (DEBUG || mIsInTest) Log.i(TAG, "Retrying; attempt " + (retryCount + 1));
+                    updateActionsWithRetry(retryCount + 1, packageName);
+                },
                 packageName, RETRY_TIMES_MS[retryCount]);
     }
 
     @WorkerThread
     private void updateAllPackages() {
+        if (DEBUG || mIsInTest) Log.i(TAG, "updateAllPackages");
         updateActionsWithRetry(0, null);
     }
 
diff --git a/quickstep/src/com/android/quickstep/RotationTouchHelper.java b/quickstep/src/com/android/quickstep/RotationTouchHelper.java
index 66929d0..fc7a3df 100644
--- a/quickstep/src/com/android/quickstep/RotationTouchHelper.java
+++ b/quickstep/src/com/android/quickstep/RotationTouchHelper.java
@@ -152,7 +152,7 @@
 
         // Register for navigation mode changes
         SysUINavigationMode.Mode newMode = mSysUiNavMode.addModeChangeListener(this);
-        onNavigationModeChanged(newMode);
+        onNavModeChangedInternal(newMode, newMode.hasGestures);
         runOnDestroy(() -> mSysUiNavMode.removeModeChangeListener(this));
 
         mOrientationListener = new OrientationEventListener(mContext) {
@@ -245,13 +245,22 @@
 
     @Override
     public void onNavigationModeChanged(SysUINavigationMode.Mode newMode) {
+        onNavModeChangedInternal(newMode, false);
+    }
+
+    /**
+     * @param forceRegister if {@code true}, this will register {@link #mFrozenTaskListener} via
+     *                      {@link #setupOrientationSwipeHandler()}
+     */
+    private void onNavModeChangedInternal(SysUINavigationMode.Mode newMode, boolean forceRegister) {
         mDisplayController.removeChangeListener(this);
         mDisplayController.addChangeListener(this);
         onDisplayInfoChanged(mContext, mDisplayController.getInfo(), CHANGE_ALL);
 
         mOrientationTouchTransformer.setNavigationMode(newMode, mDisplayController.getInfo(),
-            mContext.getResources());
-        if (!mMode.hasGestures && newMode.hasGestures) {
+                mContext.getResources());
+
+        if (forceRegister || (!mMode.hasGestures && newMode.hasGestures)) {
             setupOrientationSwipeHandler();
         } else if (mMode.hasGestures && !newMode.hasGestures){
             destroyOrientationSwipeHandlerCallback();
diff --git a/robolectric_tests/Android.bp b/robolectric_tests/Android.bp
index bf32362..9ed26ff 100644
--- a/robolectric_tests/Android.bp
+++ b/robolectric_tests/Android.bp
@@ -45,9 +45,14 @@
     java_resources: [":launcher3-robolectric-resources"],
     static_libs: [
         "truth-prebuilt",
+        "androidx.test.espresso.contrib",
+        "androidx.test.espresso.core",
+        "androidx.test.espresso.intents",
+        "androidx.test.ext.junit",
         "androidx.test.runner",
         "androidx.test.rules",
         "mockito-robolectric-prebuilt",
+        "SystemUISharedLib",
     ],
     robolectric_prebuilt_version: "4.5.1",
     instrumentation_for: "Launcher3",
diff --git a/robolectric_tests/src/com/android/launcher3/settings/SettingsActivityTest.java b/robolectric_tests/src/com/android/launcher3/settings/SettingsActivityTest.java
new file mode 100644
index 0000000..85bf28e
--- /dev/null
+++ b/robolectric_tests/src/com/android/launcher3/settings/SettingsActivityTest.java
@@ -0,0 +1,137 @@
+/*
+ * Copyright (C) 2021 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.settings;
+
+import static androidx.preference.PreferenceFragmentCompat.ARG_PREFERENCE_ROOT;
+import static androidx.test.espresso.Espresso.onView;
+import static androidx.test.espresso.action.ViewActions.click;
+import static androidx.test.espresso.assertion.ViewAssertions.matches;
+import static androidx.test.espresso.contrib.RecyclerViewActions.actionOnItem;
+import static androidx.test.espresso.intent.Intents.intended;
+import static androidx.test.espresso.intent.matcher.BundleMatchers.hasEntry;
+import static androidx.test.espresso.intent.matcher.IntentMatchers.hasComponent;
+import static androidx.test.espresso.intent.matcher.IntentMatchers.hasExtra;
+import static androidx.test.espresso.matcher.ViewMatchers.hasDescendant;
+import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed;
+import static androidx.test.espresso.matcher.ViewMatchers.withId;
+import static androidx.test.espresso.matcher.ViewMatchers.withText;
+
+import static com.android.launcher3.settings.SettingsActivity.EXTRA_FRAGMENT;
+import static com.android.launcher3.settings.SettingsActivity.EXTRA_FRAGMENT_ARGS;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.hamcrest.Matchers.allOf;
+import static org.hamcrest.Matchers.equalTo;
+
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+
+import androidx.preference.PreferenceFragmentCompat;
+import androidx.test.core.app.ActivityScenario;
+import androidx.test.core.app.ApplicationProvider;
+import androidx.test.espresso.intent.Intents;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import com.android.launcher3.R;
+import com.android.systemui.shared.plugins.PluginPrefs;
+
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+public class SettingsActivityTest {
+
+    private Context mApplicationContext;
+
+    @Before
+    public void setUp() {
+        mApplicationContext = ApplicationProvider.getApplicationContext();
+        Intents.init();
+    }
+
+    @After
+    public void tearDown() {
+        Intents.release();
+    }
+
+    @Test
+    public void testSettings_aboutTap_launchesActivity() {
+        ActivityScenario.launch(SettingsActivity.class);
+        onView(withId(R.id.recycler_view)).perform(
+                actionOnItem(hasDescendant(withText("About")), click()));
+
+        intended(allOf(
+                hasComponent(SettingsActivity.class.getName()),
+                hasExtra(
+                        equalTo(EXTRA_FRAGMENT_ARGS),
+                        hasEntry(ARG_PREFERENCE_ROOT, "about_screen"))));
+    }
+
+    @Test
+    public void testSettings_developerOptionsTap_launchesActivityWithFragment() {
+        PluginPrefs.setHasPlugins(mApplicationContext);
+        ActivityScenario.launch(SettingsActivity.class);
+        onView(withId(R.id.recycler_view)).perform(
+                actionOnItem(hasDescendant(withText("Developer Options")), click()));
+
+        intended(allOf(
+                hasComponent(SettingsActivity.class.getName()),
+                hasExtra(EXTRA_FRAGMENT, DeveloperOptionsFragment.class.getName())));
+    }
+
+    @Test
+    public void testSettings_aboutScreenIntent() {
+        Bundle fragmentArgs = new Bundle();
+        fragmentArgs.putString(ARG_PREFERENCE_ROOT, "about_screen");
+
+        Intent intent = new Intent(mApplicationContext, SettingsActivity.class)
+                .putExtra(EXTRA_FRAGMENT_ARGS, fragmentArgs);
+        ActivityScenario.launch(intent);
+
+        onView(withText("About")).check(matches(isDisplayed()));
+        onView(withText("Version")).check(matches(isDisplayed()));
+    }
+
+    @Test
+    public void testSettings_developerOptionsFragmentIntent() {
+        Intent intent = new Intent(mApplicationContext, SettingsActivity.class)
+                .putExtra(EXTRA_FRAGMENT, DeveloperOptionsFragment.class.getName());
+        ActivityScenario.launch(intent);
+
+        onView(withText("Developer Options")).check(matches(isDisplayed()));
+        onView(withId(R.id.filter_box)).check(matches(isDisplayed()));
+    }
+
+    @Test
+    public void testSettings_intentWithUnknownFragment() {
+        String fragmentClass = PreferenceFragmentCompat.class.getName();
+        Intent intent = new Intent(mApplicationContext, SettingsActivity.class)
+                .putExtra(EXTRA_FRAGMENT, fragmentClass);
+
+        try {
+            ActivityScenario.launch(intent);
+            Assert.fail("Should have thrown an IllegalArgumentException.");
+        } catch (IllegalArgumentException e) {
+            assertThat(e.getMessage()).contains(fragmentClass);
+        }
+    }
+}
diff --git a/src/com/android/launcher3/config/FeatureFlags.java b/src/com/android/launcher3/config/FeatureFlags.java
index 1715161..0336c50 100644
--- a/src/com/android/launcher3/config/FeatureFlags.java
+++ b/src/com/android/launcher3/config/FeatureFlags.java
@@ -142,7 +142,7 @@
             "MULTI_DB_GRID_MIRATION_ALGO", true, "Use the multi-db grid migration algorithm");
 
     public static final BooleanFlag ENABLE_THEMED_ICONS = getDebugFlag(
-            "ENABLE_THEMED_ICONS", false, "Enable themed icons on workspace");
+            "ENABLE_THEMED_ICONS", true, "Enable themed icons on workspace");
 
     // Keep as DeviceFlag for remote disable in emergency.
     public static final BooleanFlag ENABLE_OVERVIEW_SELECTIONS = new DeviceFlag(
diff --git a/src/com/android/launcher3/settings/DeveloperOptionsFragment.java b/src/com/android/launcher3/settings/DeveloperOptionsFragment.java
index c6b0b13..e2a69ff 100644
--- a/src/com/android/launcher3/settings/DeveloperOptionsFragment.java
+++ b/src/com/android/launcher3/settings/DeveloperOptionsFragment.java
@@ -104,6 +104,10 @@
         initFlags();
         loadPluginPrefs();
         maybeAddSandboxCategory();
+
+        if (getActivity() != null) {
+            getActivity().setTitle("Developer Options");
+        }
     }
 
     private void filterPreferences(String query, PreferenceGroup pg) {
diff --git a/src/com/android/launcher3/settings/SettingsActivity.java b/src/com/android/launcher3/settings/SettingsActivity.java
index 883ff75..85c2492 100644
--- a/src/com/android/launcher3/settings/SettingsActivity.java
+++ b/src/com/android/launcher3/settings/SettingsActivity.java
@@ -20,11 +20,13 @@
 
 import static com.android.launcher3.states.RotationHelper.ALLOW_ROTATION_PREFERENCE_KEY;
 
+import android.content.Intent;
 import android.content.SharedPreferences;
 import android.os.Bundle;
 import android.text.TextUtils;
 
 import androidx.annotation.NonNull;
+import androidx.annotation.VisibleForTesting;
 import androidx.fragment.app.DialogFragment;
 import androidx.fragment.app.Fragment;
 import androidx.fragment.app.FragmentActivity;
@@ -46,6 +48,9 @@
 import com.android.launcher3.model.WidgetsModel;
 import com.android.launcher3.uioverrides.plugins.PluginManagerWrapper;
 
+import java.util.Collections;
+import java.util.List;
+
 /**
  * Settings activity for Launcher. Currently implements the following setting: Allow rotation
  */
@@ -53,6 +58,10 @@
         implements OnPreferenceStartFragmentCallback, OnPreferenceStartScreenCallback,
         SharedPreferences.OnSharedPreferenceChangeListener{
 
+    /** List of fragments that can be hosted by this activity. */
+    private static final List<String> VALID_PREFERENCE_FRAGMENTS = Collections.singletonList(
+            DeveloperOptionsFragment.class.getName());
+
     private static final String DEVELOPER_OPTIONS_KEY = "pref_developer_options";
     private static final String FLAGS_PREFERENCE_KEY = "flag_toggler";
 
@@ -63,20 +72,30 @@
     private static final int DELAY_HIGHLIGHT_DURATION_MILLIS = 600;
     public static final String SAVE_HIGHLIGHTED_KEY = "android:preference_highlighted";
 
+    @VisibleForTesting
+    static final String EXTRA_FRAGMENT = ":settings:fragment";
+    @VisibleForTesting
+    static final String EXTRA_FRAGMENT_ARGS = ":settings:fragment_args";
+
     @Override
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
 
         if (savedInstanceState == null) {
-            Bundle args = new Bundle();
-            String prefKey = getIntent().getStringExtra(EXTRA_FRAGMENT_ARG_KEY);
+            Intent intent = getIntent();
+            Bundle args = intent.getBundleExtra(EXTRA_FRAGMENT_ARGS);
+            if (args == null) {
+                args = new Bundle();
+            }
+
+            String prefKey = intent.getStringExtra(EXTRA_FRAGMENT_ARG_KEY);
             if (!TextUtils.isEmpty(prefKey)) {
                 args.putString(EXTRA_FRAGMENT_ARG_KEY, prefKey);
             }
 
             final FragmentManager fm = getSupportFragmentManager();
             final Fragment f = fm.getFragmentFactory().instantiate(getClassLoader(),
-                    getString(R.string.settings_fragment_name));
+                    getPreferenceFragment());
             f.setArguments(args);
             // Display the fragment as the main content.
             fm.beginTransaction().replace(android.R.id.content, f).commit();
@@ -84,22 +103,45 @@
         Utilities.getPrefs(getApplicationContext()).registerOnSharedPreferenceChangeListener(this);
     }
 
+    /**
+     * Obtains the preference fragment to instantiate in this activity.
+     *
+     * @return the preference fragment class
+     * @throws IllegalArgumentException if the fragment is unknown to this activity
+     */
+    private String getPreferenceFragment() {
+        String preferenceFragment = getIntent().getStringExtra(EXTRA_FRAGMENT);
+        String defaultFragment = getString(R.string.settings_fragment_name);
+
+        if (TextUtils.isEmpty(preferenceFragment)) {
+            return defaultFragment;
+        } else if (!preferenceFragment.equals(defaultFragment)
+                && !VALID_PREFERENCE_FRAGMENTS.contains(preferenceFragment)) {
+            throw new IllegalArgumentException(
+                    "Invalid fragment for this activity: " + preferenceFragment);
+        } else {
+            return preferenceFragment;
+        }
+    }
+
     @Override
     public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) { }
 
-    private boolean startFragment(String fragment, Bundle args, String key) {
+    private boolean startPreference(String fragment, Bundle args, String key) {
         if (Utilities.ATLEAST_P && getSupportFragmentManager().isStateSaved()) {
             // Sometimes onClick can come after onPause because of being posted on the handler.
-            // Skip starting new fragments in that case.
+            // Skip starting new preferences in that case.
             return false;
         }
         final FragmentManager fm = getSupportFragmentManager();
         final Fragment f = fm.getFragmentFactory().instantiate(getClassLoader(), fragment);
-        f.setArguments(args);
         if (f instanceof DialogFragment) {
-            ((DialogFragment) f).show(getSupportFragmentManager(), key);
+            f.setArguments(args);
+            ((DialogFragment) f).show(fm, key);
         } else {
-            fm.beginTransaction().replace(android.R.id.content, f).addToBackStack(key).commit();
+            startActivity(new Intent(this, SettingsActivity.class)
+                    .putExtra(EXTRA_FRAGMENT, fragment)
+                    .putExtra(EXTRA_FRAGMENT_ARGS, args));
         }
         return true;
     }
@@ -107,14 +149,14 @@
     @Override
     public boolean onPreferenceStartFragment(
             PreferenceFragmentCompat preferenceFragment, Preference pref) {
-        return startFragment(pref.getFragment(), pref.getExtras(), pref.getKey());
+        return startPreference(pref.getFragment(), pref.getExtras(), pref.getKey());
     }
 
     @Override
     public boolean onPreferenceStartScreen(PreferenceFragmentCompat caller, PreferenceScreen pref) {
         Bundle args = new Bundle();
         args.putString(PreferenceFragmentCompat.ARG_PREFERENCE_ROOT, pref.getKey());
-        return startFragment(getString(R.string.settings_fragment_name), args, pref.getKey());
+        return startPreference(getString(R.string.settings_fragment_name), args, pref.getKey());
     }
 
     /**
@@ -148,6 +190,10 @@
                     screen.removePreference(preference);
                 }
             }
+
+            if (getActivity() != null && !TextUtils.isEmpty(getPreferenceScreen().getTitle())) {
+                getActivity().setTitle(getPreferenceScreen().getTitle());
+            }
         }
 
         @Override
diff --git a/tests/tapl/com/android/launcher3/tapl/SelectModeButtons.java b/tests/tapl/com/android/launcher3/tapl/SelectModeButtons.java
index 3507418..e1b73a4 100644
--- a/tests/tapl/com/android/launcher3/tapl/SelectModeButtons.java
+++ b/tests/tapl/com/android/launcher3/tapl/SelectModeButtons.java
@@ -48,21 +48,4 @@
             }
         }
     }
-
-    /**
-     * Click feedback button.
-     */
-    @NonNull
-    public Background clickFeedback() {
-        try (LauncherInstrumentation.Closable e = mLauncher.eventsCheck();
-             LauncherInstrumentation.Closable c =
-                     mLauncher.addContextLayer("want to click feedback button")) {
-            UiObject2 feedback = mLauncher.waitForObjectInContainer(mSelectModeButtons, "feedback");
-            mLauncher.clickLauncherObject(feedback);
-            try (LauncherInstrumentation.Closable c1 = mLauncher.addContextLayer(
-                    "clicked feedback button")) {
-                return new Background(mLauncher);
-            }
-        }
-    }
 }