Apply settings page transition
This change applies Shared X-Axis transition across settings pages. For
injected settings, the injected apps have to also apply the same
transition to take effect.
Fixes: 177479937
Test: robotests and navigate through settings pages
Change-Id: I6241860766f5969428faf9a5b3730a7614fdcd92
diff --git a/src/com/android/settings/Utils.java b/src/com/android/settings/Utils.java
index 031fb8a..fd67aa8 100644
--- a/src/com/android/settings/Utils.java
+++ b/src/com/android/settings/Utils.java
@@ -77,6 +77,7 @@
import android.provider.ContactsContract.Data;
import android.provider.ContactsContract.Profile;
import android.provider.ContactsContract.RawContacts;
+import android.provider.Settings;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
import android.text.Spannable;
@@ -109,6 +110,7 @@
import com.android.internal.app.UnlaunchableAppActivity;
import com.android.internal.util.ArrayUtils;
import com.android.internal.widget.LockPatternUtils;
+import com.android.settings.core.FeatureFlags;
import com.android.settings.dashboard.profileselector.ProfileFragmentBridge;
import com.android.settings.dashboard.profileselector.ProfileSelectFragment;
import com.android.settings.password.ChooseLockSettingsHelper;
@@ -159,6 +161,9 @@
/** Whether or not app hibernation is enabled on the device **/
public static final String PROPERTY_APP_HIBERNATION_ENABLED = "app_hibernation_enabled";
+ /** Whether or not Settings Shared Axis transition is enabled */
+ public static final String SETTINGS_SHARED_AXIS_ENABLED = "settings_shared_axis_enabled";
+
/**
* Finds a matching activity for a preference's intent. If a matching
* activity is not found, it will remove the preference.
@@ -1196,4 +1201,12 @@
public static boolean isProviderModelEnabled(Context context) {
return FeatureFlagUtils.isEnabled(context, FeatureFlagUtils.SETTINGS_PROVIDER_MODEL);
}
+
+ public static boolean isPageTransitionEnabled(Context context) {
+ final boolean isSilkyHome = FeatureFlagUtils.isEnabled(context, FeatureFlags.SILKY_HOME);
+ final boolean isTransitionEnabled = Settings.Global.getInt(context.getContentResolver(),
+ SETTINGS_SHARED_AXIS_ENABLED, 0) == 1;
+
+ return isSilkyHome && isTransitionEnabled;
+ }
}
diff --git a/src/com/android/settings/core/SettingsBaseActivity.java b/src/com/android/settings/core/SettingsBaseActivity.java
index cf3cd50..6215dc2 100644
--- a/src/com/android/settings/core/SettingsBaseActivity.java
+++ b/src/com/android/settings/core/SettingsBaseActivity.java
@@ -18,6 +18,7 @@
import android.annotation.LayoutRes;
import android.annotation.Nullable;
import android.app.ActivityManager;
+import android.app.ActivityOptions;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
@@ -27,6 +28,7 @@
import android.content.res.TypedArray;
import android.os.AsyncTask;
import android.os.Bundle;
+import android.os.UserHandle;
import android.text.TextUtils;
import android.util.ArraySet;
import android.util.FeatureFlagUtils;
@@ -43,8 +45,10 @@
import com.android.settings.R;
import com.android.settings.SubSettings;
+import com.android.settings.Utils;
import com.android.settings.dashboard.CategoryManager;
import com.android.settingslib.drawer.Tile;
+import com.android.settingslib.transition.SettingsTransitionHelper;
import com.google.android.material.appbar.CollapsingToolbarLayout;
import com.google.android.setupcompat.util.WizardManagerHelper;
@@ -60,6 +64,7 @@
protected static final boolean DEBUG_TIMING = false;
private static final String TAG = "SettingsBaseActivity";
private static final String DATA_SCHEME_PKG = "package";
+ private static final int DEFAULT_REQUEST = -1;
// Serves as a temporary list of tiles to ignore until we heard back from the PM that they
// are disabled.
@@ -73,6 +78,13 @@
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
+ if (Utils.isPageTransitionEnabled(this)) {
+ // Enable Activity transitions
+ getWindow().requestFeature(Window.FEATURE_ACTIVITY_TRANSITIONS);
+ SettingsTransitionHelper.applyForwardTransition(this);
+ SettingsTransitionHelper.applyBackwardTransition(this);
+ }
+
super.onCreate(savedInstanceState);
if (isLockTaskModePinned() && !isSettingsRunOnTop()) {
Log.w(TAG, "Devices lock task mode pinned.");
@@ -123,6 +135,57 @@
}
@Override
+ public void startActivity(Intent intent) {
+ if (!Utils.isPageTransitionEnabled(this)) {
+ super.startActivity(intent);
+ return;
+ }
+ super.startActivity(intent, getActivityOptionsBundle());
+ }
+
+ @Override
+ public void startActivity(Intent intent, @androidx.annotation.Nullable Bundle options) {
+ if (!Utils.isPageTransitionEnabled(this) || options != null) {
+ super.startActivity(intent, options);
+ return;
+ }
+ super.startActivity(intent, getActivityOptionsBundle());
+ }
+
+ @Override
+ public void startActivityForResult(Intent intent, int requestCode) {
+ // startActivity() will eventually calls startActivityForResult() with requestCode -1.
+ // Adding this condition to avoid multiple calls.
+ if (!Utils.isPageTransitionEnabled(this) || requestCode == DEFAULT_REQUEST) {
+ super.startActivityForResult(intent, requestCode);
+ return;
+ }
+ super.startActivityForResult(intent, requestCode, getActivityOptionsBundle());
+ }
+
+ @Override
+ public void startActivityForResult(Intent intent, int requestCode,
+ @androidx.annotation.Nullable Bundle options) {
+ if (!Utils.isPageTransitionEnabled(this) || requestCode == DEFAULT_REQUEST
+ || options != null) {
+ super.startActivityForResult(intent, requestCode, options);
+ return;
+ }
+ super.startActivityForResult(intent, requestCode, getActivityOptionsBundle());
+ }
+
+ @Override
+ public void startActivityForResultAsUser(Intent intent, int requestCode,
+ UserHandle userHandle) {
+ if (!Utils.isPageTransitionEnabled(this) || requestCode == DEFAULT_REQUEST) {
+ super.startActivityForResultAsUser(intent, requestCode, userHandle);
+ return;
+ }
+ super.startActivityForResultAsUser(intent, requestCode, getActivityOptionsBundle(),
+ userHandle);
+ }
+
+ @Override
protected void onResume() {
super.onResume();
final IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_ADDED);
@@ -267,10 +330,16 @@
}
}
+ private Bundle getActivityOptionsBundle() {
+ final Toolbar toolbar = findViewById(R.id.action_bar);
+ return ActivityOptions.makeSceneTransitionAnimation(this, toolbar,
+ "shared_element_view").toBundle();
+ }
+
public interface CategoryListener {
/**
* @param categories the changed categories that have to be refreshed, or null to force
- * refreshing all.
+ * refreshing all.
*/
void onCategoriesChanged(@Nullable Set<String> categories);
}
diff --git a/src/com/android/settings/core/SubSettingLauncher.java b/src/com/android/settings/core/SubSettingLauncher.java
index 5d9a528..545fcda 100644
--- a/src/com/android/settings/core/SubSettingLauncher.java
+++ b/src/com/android/settings/core/SubSettingLauncher.java
@@ -17,18 +17,22 @@
package com.android.settings.core;
import android.annotation.StringRes;
+import android.app.Activity;
+import android.app.ActivityOptions;
import android.content.Context;
import android.content.Intent;
-import android.os.Build;
import android.os.Bundle;
import android.os.UserHandle;
import android.text.TextUtils;
+import android.widget.Toolbar;
import androidx.annotation.VisibleForTesting;
import androidx.fragment.app.Fragment;
+import com.android.settings.R;
import com.android.settings.SettingsActivity;
import com.android.settings.SubSettings;
+import com.android.settings.Utils;
import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
public class SubSettingLauncher {
@@ -183,7 +187,16 @@
resultListener.getActivity().startActivityForResultAsUser(intent, requestCode, userHandle);
}
- private void launchForResult(Fragment listener, Intent intent, int requestCode) {
+ @VisibleForTesting
+ void launchForResult(Fragment listener, Intent intent, int requestCode) {
+ if (Utils.isPageTransitionEnabled(mContext)) {
+ final Activity activity = listener.getActivity();
+ final Toolbar toolbar = activity.findViewById(R.id.action_bar);
+ final Bundle bundle = ActivityOptions.makeSceneTransitionAnimation(activity, toolbar,
+ "shared_element_view").toBundle();
+ listener.startActivityForResult(intent, requestCode, bundle);
+ return;
+ }
listener.startActivityForResult(intent, requestCode);
}
@@ -192,6 +205,7 @@
intent.replaceExtras(mLaunchRequest.extras);
}
}
+
/**
* Simple container that has information about how to launch a subsetting.
*/
diff --git a/src/com/android/settings/homepage/SettingsHomepageActivity.java b/src/com/android/settings/homepage/SettingsHomepageActivity.java
index 881e39c..cd980f3 100644
--- a/src/com/android/settings/homepage/SettingsHomepageActivity.java
+++ b/src/com/android/settings/homepage/SettingsHomepageActivity.java
@@ -18,11 +18,14 @@
import android.animation.LayoutTransition;
import android.app.ActivityManager;
+import android.app.ActivityOptions;
import android.app.settings.SettingsEnums;
+import android.content.Intent;
import android.os.Bundle;
import android.util.FeatureFlagUtils;
import android.util.Log;
import android.view.View;
+import android.view.Window;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.Toolbar;
@@ -33,11 +36,13 @@
import androidx.fragment.app.FragmentTransaction;
import com.android.settings.R;
+import com.android.settings.Utils;
import com.android.settings.accounts.AvatarViewMixin;
import com.android.settings.core.FeatureFlags;
import com.android.settings.core.HideNonSystemOverlayMixin;
import com.android.settings.homepage.contextualcards.ContextualCardsFragment;
import com.android.settings.overlay.FeatureFactory;
+import com.android.settingslib.transition.SettingsTransitionHelper;
public class SettingsHomepageActivity extends FragmentActivity {
@@ -64,6 +69,12 @@
@Override
protected void onCreate(Bundle savedInstanceState) {
+ if (Utils.isPageTransitionEnabled(this)) {
+ // Enable Activity transitions
+ getWindow().requestFeature(Window.FEATURE_ACTIVITY_TRANSITIONS);
+ SettingsTransitionHelper.applyForwardTransition(this);
+ SettingsTransitionHelper.applyBackwardTransition(this);
+ }
super.onCreate(savedInstanceState);
setContentView(R.layout.settings_homepage_container);
@@ -101,6 +112,16 @@
.getLayoutTransition().enableTransitionType(LayoutTransition.CHANGING);
}
+ @Override
+ public void startActivity(Intent intent) {
+ if (Utils.isPageTransitionEnabled(this)) {
+ final Bundle bundle = ActivityOptions.makeSceneTransitionAnimation(this).toBundle();
+ super.startActivity(intent, bundle);
+ return;
+ }
+ super.startActivity(intent);
+ }
+
private void showSuggestionFragment() {
final Class<? extends Fragment> fragment = FeatureFactory.getFactory(this)
.getSuggestionFeatureProvider(this).getContextualSuggestionFragment();
diff --git a/tests/robotests/src/com/android/settings/core/SubSettingLauncherTest.java b/tests/robotests/src/com/android/settings/core/SubSettingLauncherTest.java
index 8f9c331..7a83cd0 100644
--- a/tests/robotests/src/com/android/settings/core/SubSettingLauncherTest.java
+++ b/tests/robotests/src/com/android/settings/core/SubSettingLauncherTest.java
@@ -28,12 +28,14 @@
import android.content.Context;
import android.content.Intent;
+import android.os.Bundle;
import android.os.UserHandle;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentActivity;
import com.android.settings.SettingsActivity;
+import com.android.settings.testutils.shadow.ShadowUtils;
import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
import org.junit.Before;
@@ -42,10 +44,12 @@
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import org.robolectric.annotation.Config;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
@RunWith(RobolectricTestRunner.class)
+@Config(shadows = ShadowUtils.class)
public class SubSettingLauncherTest {
@Mock
@@ -109,10 +113,13 @@
@Test
public void launch_hasRequestListener_shouldStartActivityForResult() {
+ ShadowUtils.setIsPageTransitionEnabled(true);
final int requestCode = 123123;
when(mFragment.getActivity()).thenReturn(mActivity);
final SubSettingLauncher launcher = spy(new SubSettingLauncher(mContext));
+ doNothing().when(launcher).launchForResult(any(Fragment.class), any(Intent.class),
+ anyInt());
launcher.setTitleText("123")
.setDestination(SubSettingLauncherTest.class.getName())
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
@@ -120,7 +127,8 @@
.setResultListener(mFragment, requestCode)
.launch();
- verify(mFragment).startActivityForResult(any(Intent.class), eq(requestCode));
+ verify(launcher)
+ .launchForResult(eq(mFragment), any(Intent.class), eq(requestCode));
}
@Test
diff --git a/tests/robotests/src/com/android/settings/homepage/SettingsHomepageActivityTest.java b/tests/robotests/src/com/android/settings/homepage/SettingsHomepageActivityTest.java
index c7a2650..620f6d7 100644
--- a/tests/robotests/src/com/android/settings/homepage/SettingsHomepageActivityTest.java
+++ b/tests/robotests/src/com/android/settings/homepage/SettingsHomepageActivityTest.java
@@ -39,6 +39,7 @@
import com.android.settings.dashboard.suggestions.SuggestionFeatureProviderImpl;
import com.android.settings.homepage.contextualcards.slices.BatteryFixSliceTest;
import com.android.settings.testutils.shadow.ShadowUserManager;
+import com.android.settings.testutils.shadow.ShadowUtils;
import org.junit.Before;
import org.junit.Test;
@@ -58,12 +59,13 @@
@RunWith(RobolectricTestRunner.class)
@Config(shadows = {ShadowUserManager.class,
- SettingsHomepageActivityTest.ShadowSuggestionFeatureProviderImpl.class})
+ SettingsHomepageActivityTest.ShadowSuggestionFeatureProviderImpl.class, ShadowUtils.class})
public class SettingsHomepageActivityTest {
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
+ ShadowUtils.setIsPageTransitionEnabled(false);
}
@Test
diff --git a/tests/robotests/src/com/android/settings/testutils/shadow/ShadowUtils.java b/tests/robotests/src/com/android/settings/testutils/shadow/ShadowUtils.java
index 5a32f34..750640b 100644
--- a/tests/robotests/src/com/android/settings/testutils/shadow/ShadowUtils.java
+++ b/tests/robotests/src/com/android/settings/testutils/shadow/ShadowUtils.java
@@ -47,6 +47,7 @@
private static boolean sIsVoiceCapable;
private static ArraySet<String> sResultLinks = new ArraySet<>();
private static boolean sIsBatteryPresent;
+ private static boolean sIsPageTransitionEnabled;
@Implementation
protected static int enforceSameOwner(Context context, int userId) {
@@ -69,6 +70,7 @@
sIsVoiceCapable = false;
sResultLinks = new ArraySet<>();
sIsBatteryPresent = true;
+ sIsPageTransitionEnabled = true;
}
public static void setIsDemoUser(boolean isDemoUser) {
@@ -166,4 +168,13 @@
public static void setIsBatteryPresent(boolean isBatteryPresent) {
sIsBatteryPresent = isBatteryPresent;
}
+
+ @Implementation
+ protected static boolean isPageTransitionEnabled(Context context) {
+ return sIsPageTransitionEnabled;
+ }
+
+ public static void setIsPageTransitionEnabled(boolean isPageTransitionEnabled) {
+ sIsPageTransitionEnabled = isPageTransitionEnabled;
+ }
}