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();