Refactor Utils.startWithFragment to SubSettingLauncher

- Use the new launcher in AccountTypePrefernce for now. Will migrate the
  rest in follow up CLs.

Change-Id: I67aa49f54b39ecea4ecfdc32ccbd827d21fc79b8
Bug: 73250851
Test: robotest
diff --git a/res/values/strings.xml b/res/values/strings.xml
index d16e438..b1435c9 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -6319,13 +6319,6 @@
     <!-- Applicaitons with restrictions - settings button [CHAR LIMIT=30] -->
     <string name="apps_with_restrictions_settings_button">Expand settings for application</string>
 
-    <!-- Warning message when changing a global setting for a tablet.[CHAR LIMIT=none] -->
-    <string name="global_change_warning" product="tablet">This setting affects all users on this tablet.</string>
-    <!-- Warning message when changing a global setting for a phone.[CHAR LIMIT=none] -->
-    <string name="global_change_warning" product="default">This setting affects all users on this phone.</string>
-    <!-- Warning message title for global locale change [CHAR LIMIT=40] -->
-    <string name="global_locale_change_title">Change language</string>
-
     <!-- NFC payment settings --><skip/>
     <string name="nfc_payment_settings_title">Tap &amp; pay</string>
     <!-- Caption for button linking to a page explaining how Tap and Pay works-->
diff --git a/src/com/android/settings/Utils.java b/src/com/android/settings/Utils.java
index 1a32127..a989c34 100644
--- a/src/com/android/settings/Utils.java
+++ b/src/com/android/settings/Utils.java
@@ -23,9 +23,7 @@
 
 import android.annotation.Nullable;
 import android.app.ActivityManager;
-import android.app.AlertDialog;
 import android.app.AppGlobals;
-import android.app.Dialog;
 import android.app.Fragment;
 import android.app.IActivityManager;
 import android.app.KeyguardManager;
@@ -34,7 +32,6 @@
 import android.content.ComponentName;
 import android.content.ContentResolver;
 import android.content.Context;
-import android.content.DialogInterface;
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.content.pm.ApplicationInfo;
@@ -52,9 +49,6 @@
 import android.graphics.drawable.BitmapDrawable;
 import android.graphics.drawable.Drawable;
 import android.hardware.fingerprint.FingerprintManager;
-import android.icu.text.RelativeDateTimeFormatter;
-import android.icu.text.RelativeDateTimeFormatter.RelativeUnit;
-import android.icu.util.ULocale;
 import android.net.ConnectivityManager;
 import android.net.LinkProperties;
 import android.net.Network;
@@ -107,7 +101,6 @@
 import com.android.settings.wrapper.FingerprintManagerWrapper;
 import com.android.settingslib.core.instrumentation.VisibilityLoggerMixin;
 
-import com.android.settingslib.utils.StringUtil;
 import java.net.InetAddress;
 import java.util.ArrayList;
 import java.util.Iterator;
@@ -237,16 +230,6 @@
         return null;
     }
 
-    /**
-     * Returns the default link's IP addresses, if any, taking into account IPv4 and IPv6 style
-     * addresses.
-     * @return the formatted and newline-separated IP addresses, or null if none.
-     */
-    public static String getDefaultIpAddresses(ConnectivityManager cm) {
-        LinkProperties prop = cm.getActiveLinkProperties();
-        return formatIpAddresses(prop);
-    }
-
     private static String formatIpAddresses(LinkProperties prop) {
         if (prop == null) return null;
         Iterator<InetAddress> iter = prop.getAllAddresses().iterator();
@@ -400,23 +383,6 @@
         }
     }
 
-    /** Not global warming, it's global change warning. */
-    public static Dialog buildGlobalChangeWarningDialog(final Context context, int titleResId,
-            final Runnable positiveAction) {
-        final AlertDialog.Builder builder = new AlertDialog.Builder(context);
-        builder.setTitle(titleResId);
-        builder.setMessage(R.string.global_change_warning);
-        builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
-            @Override
-            public void onClick(DialogInterface dialog, int which) {
-                positiveAction.run();
-            }
-        });
-        builder.setNegativeButton(android.R.string.cancel, null);
-
-        return builder.create();
-    }
-
     public static boolean hasMultipleUsers(Context context) {
         return ((UserManager) context.getSystemService(Context.USER_SERVICE))
                 .getUsers().size() > 1;
@@ -477,21 +443,12 @@
      * @param resultTo Option fragment that should receive the result of the activity launch.
      * @param resultRequestCode If resultTo is non-null, this is the request code in which
      *                          to report the result.
-     * @param titleResPackageName Optional package name for the resource id of the title.
      * @param titleResId resource id for the String to display for the title of this set
      *                   of preferences.
      * @param title String to display for the title of this set of preferences.
      * @param metricsCategory The current metricsCategory for logging source when fragment starts
      */
     public static void startWithFragment(Context context, String fragmentName, Bundle args,
-            Fragment resultTo, int resultRequestCode, String titleResPackageName, int titleResId,
-            CharSequence title, int metricsCategory) {
-        startWithFragment(context, fragmentName, args, resultTo, resultRequestCode,
-                titleResPackageName, titleResId, title, false /* not a shortcut */,
-                metricsCategory);
-    }
-
-    public static void startWithFragment(Context context, String fragmentName, Bundle args,
             Fragment resultTo, int resultRequestCode, int titleResId,
             CharSequence title, boolean isShortcut, int metricsCategory) {
         Intent intent = onBuildStartFragmentIntent(context, fragmentName, args,
@@ -511,7 +468,6 @@
                 Intent.FLAG_ACTIVITY_NEW_TASK);
     }
 
-
     public static void startWithFragment(Context context, String fragmentName, Bundle args,
             Fragment resultTo, int resultRequestCode, String titleResPackageName, int titleResId,
             CharSequence title, boolean isShortcut, int metricsCategory, int flags) {
diff --git a/src/com/android/settings/accounts/AccountTypePreference.java b/src/com/android/settings/accounts/AccountTypePreference.java
index 574cbd5..e08f113 100644
--- a/src/com/android/settings/accounts/AccountTypePreference.java
+++ b/src/com/android/settings/accounts/AccountTypePreference.java
@@ -28,11 +28,13 @@
 import android.support.v7.preference.Preference.OnPreferenceClickListener;
 
 import com.android.settings.Utils;
+import com.android.settings.core.SubSettingLauncher;
 import com.android.settings.widget.AppPreference;
 
 public class AccountTypePreference extends AppPreference implements OnPreferenceClickListener {
     /**
      * Title of the tile that is shown to the user.
+     *
      * @attr ref android.R.styleable#PreferenceHeader_title
      */
     private final CharSequence mTitle;
@@ -56,6 +58,7 @@
     /**
      * Full class name of the fragment to display when this tile is
      * selected.
+     *
      * @attr ref android.R.styleable#PreferenceHeader_fragment
      */
     private final String mFragment;
@@ -92,19 +95,21 @@
     public boolean onPreferenceClick(Preference preference) {
         if (mFragment != null) {
             UserManager userManager =
-                (UserManager) getContext().getSystemService(Context.USER_SERVICE);
+                    (UserManager) getContext().getSystemService(Context.USER_SERVICE);
             UserHandle user = mFragmentArguments.getParcelable(EXTRA_USER);
             if (user != null && Utils.startQuietModeDialogIfNecessary(getContext(), userManager,
-                user.getIdentifier())) {
+                    user.getIdentifier())) {
                 return true;
             } else if (user != null && Utils.unlockWorkProfileIfNecessary(getContext(),
-                user.getIdentifier())) {
+                    user.getIdentifier())) {
                 return true;
             }
-            Utils.startWithFragment(getContext(), mFragment, mFragmentArguments,
-                null /* resultTo */, 0 /* resultRequestCode */, mTitleResPackageName,
-                mTitleResId, null /* title */,false /* isShortCut */, mMetricsCategory,
-                    0 /* flag */);
+            new SubSettingLauncher(getContext())
+                    .setDestination(mFragment)
+                    .setArguments(mFragmentArguments)
+                    .setTitle(mTitleResPackageName, mTitleResId)
+                    .setSourceMetricsCategory(mMetricsCategory)
+                    .launch();
             return true;
         }
         return false;
diff --git a/src/com/android/settings/core/SubSettingLauncher.java b/src/com/android/settings/core/SubSettingLauncher.java
new file mode 100644
index 0000000..a787972
--- /dev/null
+++ b/src/com/android/settings/core/SubSettingLauncher.java
@@ -0,0 +1,160 @@
+/*
+ * Copyright (C) 2018 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.settings.core;
+
+import android.annotation.StringRes;
+import android.app.Fragment;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+import android.os.UserHandle;
+import android.support.annotation.VisibleForTesting;
+
+import com.android.settings.SettingsActivity;
+import com.android.settings.SubSettings;
+import com.android.settingslib.core.instrumentation.VisibilityLoggerMixin;
+
+public class SubSettingLauncher {
+
+    private final Context mContext;
+    private final LaunchRequest mLaunchRequest;
+    private boolean mLaunched;
+
+    public SubSettingLauncher(Context context) {
+        if (context == null) {
+            throw new IllegalArgumentException("Context must be non-null.");
+        }
+        mContext = context;
+        mLaunchRequest = new LaunchRequest();
+    }
+
+    public SubSettingLauncher setDestination(String fragmentName) {
+        mLaunchRequest.destinationName = fragmentName;
+        return this;
+    }
+
+    public SubSettingLauncher setTitle(@StringRes int titleResId) {
+        return setTitle(null /*titlePackageName*/, titleResId);
+    }
+
+    public SubSettingLauncher setTitle(String titlePackageName, @StringRes int titleResId) {
+        mLaunchRequest.titleResPackageName = titlePackageName;
+        mLaunchRequest.titleResId = titleResId;
+        mLaunchRequest.title = null;
+        return this;
+    }
+
+    public SubSettingLauncher setTitle(CharSequence title) {
+        mLaunchRequest.title = title;
+        return this;
+    }
+
+    public SubSettingLauncher setIsShortCut(boolean isShortCut) {
+        mLaunchRequest.isShortCut = isShortCut;
+        return this;
+    }
+
+    public SubSettingLauncher setArguments(Bundle arguments) {
+        mLaunchRequest.arguments = arguments;
+        return this;
+    }
+
+    public SubSettingLauncher setSourceMetricsCategory(int sourceMetricsCategory) {
+        mLaunchRequest.sourceMetricsCategory = sourceMetricsCategory;
+        return this;
+    }
+
+    public SubSettingLauncher setResultListener(Fragment listener, int resultRequestCode) {
+        mLaunchRequest.mRequestCode = resultRequestCode;
+        mLaunchRequest.mResultListener = listener;
+        return this;
+    }
+
+    public SubSettingLauncher addFlags(int flags) {
+        mLaunchRequest.flags |= flags;
+        return this;
+    }
+
+    public SubSettingLauncher setUserHandle(UserHandle userHandle) {
+        mLaunchRequest.userHandle = userHandle;
+        return this;
+    }
+
+    public void launch() {
+        if (mLaunched) {
+            throw new IllegalStateException(
+                    "This launcher has already been executed. Do not reuse");
+        }
+        mLaunched = true;
+        final Intent intent = new Intent(Intent.ACTION_MAIN);
+        intent.setClass(mContext, SubSettings.class);
+
+        intent.putExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT, mLaunchRequest.destinationName);
+        intent.putExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT_ARGUMENTS, mLaunchRequest.arguments);
+        intent.putExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT_TITLE_RES_PACKAGE_NAME,
+                mLaunchRequest.titleResPackageName);
+        intent.putExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT_TITLE_RESID,
+                mLaunchRequest.titleResId);
+        intent.putExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT_TITLE, mLaunchRequest.title);
+        intent.putExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT_AS_SHORTCUT,
+                mLaunchRequest.isShortCut);
+        intent.putExtra(VisibilityLoggerMixin.EXTRA_SOURCE_METRICS_CATEGORY,
+                mLaunchRequest.sourceMetricsCategory);
+        intent.addFlags(mLaunchRequest.flags);
+
+        if (mLaunchRequest.userHandle != null
+                && mLaunchRequest.userHandle.getIdentifier() != UserHandle.myUserId()) {
+            launchAsUser(mContext, intent, mLaunchRequest.userHandle);
+        } else if (mLaunchRequest.mResultListener != null) {
+            launchForResult(mLaunchRequest.mResultListener, intent, mLaunchRequest.mRequestCode);
+        } else {
+            launch(intent);
+        }
+    }
+
+    @VisibleForTesting
+    void launch(Intent intent) {
+        mContext.startActivity(intent);
+    }
+
+    private static void launchForResult(Fragment listener, Intent intent, int requestCode) {
+        listener.getActivity().startActivityForResult(intent, requestCode);
+    }
+
+    private static void launchAsUser(Context context, Intent intent, UserHandle userHandle) {
+        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+        intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
+        context.startActivityAsUser(intent, userHandle);
+    }
+
+    /**
+     * Simple container that has information about how to launch a subsetting.
+     */
+    static class LaunchRequest {
+        String destinationName;
+        int titleResId;
+        String titleResPackageName;
+        CharSequence title;
+        boolean isShortCut;
+        int sourceMetricsCategory;
+        int flags;
+        Fragment mResultListener;
+        int mRequestCode;
+        UserHandle userHandle;
+        Bundle arguments;
+    }
+}
diff --git a/tests/robotests/src/com/android/settings/core/SubSettingLauncherTest.java b/tests/robotests/src/com/android/settings/core/SubSettingLauncherTest.java
new file mode 100644
index 0000000..abb3312
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/core/SubSettingLauncherTest.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2018 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.settings.core;
+
+import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+
+import android.content.Context;
+import android.content.Intent;
+
+import com.android.settings.SettingsActivity;
+import com.android.settings.TestConfig;
+import com.android.settings.testutils.SettingsRobolectricTestRunner;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
+
+@RunWith(SettingsRobolectricTestRunner.class)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION)
+public class SubSettingLauncherTest {
+
+    private Context mContext;
+
+    @Before
+    public void setUp() {
+        mContext = RuntimeEnvironment.application;
+    }
+
+    @Test(expected = IllegalStateException.class)
+    public void cannotReuseLauncher() {
+        final SubSettingLauncher launcher = spy(new SubSettingLauncher(mContext));
+        doNothing().when(launcher).launch(any(Intent.class));
+        launcher.launch();
+        launcher.launch();
+    }
+
+    @Test
+    public void launch_shouldIncludeAllParams() {
+        final ArgumentCaptor<Intent> intentArgumentCaptor = ArgumentCaptor.forClass(Intent.class);
+        final SubSettingLauncher launcher = spy(new SubSettingLauncher(mContext));
+        launcher.setTitle("123")
+                .setDestination(SubSettingLauncherTest.class.getName())
+                .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
+                .launch();
+        doNothing().when(launcher).launch(any(Intent.class));
+        verify(launcher).launch(intentArgumentCaptor.capture());
+        final Intent intent = intentArgumentCaptor.getValue();
+
+        assertThat(intent.getStringExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT_TITLE))
+                .isEqualTo("123");
+        assertThat(intent.getStringExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT))
+                .isEqualTo(SubSettingLauncherTest.class.getName());
+        assertThat(intent.getFlags()).isEqualTo(Intent.FLAG_ACTIVITY_NEW_TASK);
+    }
+}