Merge "Give each preference screen its own activity and toolbar title" into sc-dev am: 8d6ca68786
Original change: https://googleplex-android-review.googlesource.com/c/platform/packages/apps/Launcher3/+/14709732
Change-Id: I058985cbd1edbdb9cf1487eed4a51f9863cda0e7
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/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