Merge "Fixed the robo test failed in FoldLockBehaviorSettingsTest" into main
diff --git a/res/xml/data_usage_list.xml b/res/xml/data_usage_list.xml
index 28f09c6..791fc86 100644
--- a/res/xml/data_usage_list.xml
+++ b/res/xml/data_usage_list.xml
@@ -14,7 +14,8 @@
      limitations under the License.
 -->
 
-<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
+<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:settings="http://schemas.android.com/apk/res-auto">
 
     <PreferenceCategory
         android:key="usage_amount"
@@ -32,6 +33,7 @@
 
     <PreferenceCategory
         android:key="apps_group"
-        android:layout="@layout/preference_category_no_label" />
+        android:layout="@layout/preference_category_no_label"
+        settings:controller="com.android.settings.datausage.DataUsageListAppsController" />
 
 </PreferenceScreen>
diff --git a/src/com/android/settings/datausage/AppDataUsagePreference.java b/src/com/android/settings/datausage/AppDataUsagePreference.java
index 2805819..d8c7392 100644
--- a/src/com/android/settings/datausage/AppDataUsagePreference.java
+++ b/src/com/android/settings/datausage/AppDataUsagePreference.java
@@ -38,6 +38,7 @@
     public AppDataUsagePreference(Context context, AppItem item, int percent,
             UidDetailProvider provider) {
         super(context);
+        setKey("app_data_usage_" + item.key);
         mItem = item;
         mPercent = percent;
 
diff --git a/src/com/android/settings/datausage/DataUsageList.java b/src/com/android/settings/datausage/DataUsageList.java
index b030219..e4bad12 100644
--- a/src/com/android/settings/datausage/DataUsageList.java
+++ b/src/com/android/settings/datausage/DataUsageList.java
@@ -15,9 +15,7 @@
 package com.android.settings.datausage;
 
 import android.app.Activity;
-import android.app.ActivityManager;
 import android.app.settings.SettingsEnums;
-import android.app.usage.NetworkStats;
 import android.content.Context;
 import android.content.Intent;
 import android.graphics.Color;
@@ -46,25 +44,19 @@
 import androidx.loader.app.LoaderManager.LoaderCallbacks;
 import androidx.loader.content.Loader;
 import androidx.preference.Preference;
-import androidx.preference.PreferenceGroup;
 
 import com.android.settings.R;
 import com.android.settings.core.SubSettingLauncher;
 import com.android.settings.datausage.CycleAdapter.SpinnerInterface;
-import com.android.settings.datausage.lib.AppDataUsageRepository;
 import com.android.settings.network.MobileDataEnabledListener;
 import com.android.settings.network.MobileNetworkRepository;
 import com.android.settings.network.ProxySubscriptionManager;
 import com.android.settings.widget.LoadingViewController;
-import com.android.settingslib.AppItem;
 import com.android.settingslib.mobile.dataservice.SubscriptionInfoEntity;
 import com.android.settingslib.net.NetworkCycleChartData;
 import com.android.settingslib.net.NetworkCycleChartDataLoader;
-import com.android.settingslib.net.NetworkStatsSummaryLoader;
-import com.android.settingslib.net.UidDetailProvider;
 import com.android.settingslib.utils.ThreadUtils;
 
-import java.util.ArrayList;
 import java.util.List;
 import java.util.Objects;
 import java.util.Optional;
@@ -85,14 +77,11 @@
 
     private static final String KEY_USAGE_AMOUNT = "usage_amount";
     private static final String KEY_CHART_DATA = "chart_data";
-    private static final String KEY_APPS_GROUP = "apps_group";
     private static final String KEY_TEMPLATE = "template";
     private static final String KEY_APP = "app";
 
     @VisibleForTesting
     static final int LOADER_CHART_DATA = 2;
-    @VisibleForTesting
-    static final int LOADER_SUMMARY = 3;
 
     @VisibleForTesting
     MobileDataEnabledListener mDataStateListener;
@@ -113,18 +102,15 @@
     @Nullable
     private List<NetworkCycleChartData> mCycleData;
 
-    // Caches the cycles for startAppDataUsage usage, which need be cleared when resumed.
-    private ArrayList<Long> mCycles;
     // Spinner will keep the selected cycle even after paused, this only keeps the displayed cycle,
     // which need be cleared when resumed.
     private CycleAdapter.CycleItem mLastDisplayedCycle;
-    private UidDetailProvider mUidDetailProvider;
     private CycleAdapter mCycleAdapter;
     private Preference mUsageAmount;
-    private PreferenceGroup mApps;
     private View mHeader;
     private MobileNetworkRepository mMobileNetworkRepository;
     private SubscriptionInfoEntity mSubscriptionInfoEntity;
+    private DataUsageListAppsController mDataUsageListAppsController;
 
     @Override
     public int getMetricsCategory() {
@@ -148,14 +134,19 @@
             return;
         }
 
-        mUidDetailProvider = new UidDetailProvider(activity);
         mUsageAmount = findPreference(KEY_USAGE_AMOUNT);
         mChart = findPreference(KEY_CHART_DATA);
-        mApps = findPreference(KEY_APPS_GROUP);
 
         processArgument();
+        if (mTemplate == null) {
+            Log.e(TAG, "No template; leaving");
+            finish();
+            return;
+        }
         updateSubscriptionInfoEntity();
         mDataStateListener = new MobileDataEnabledListener(activity, this);
+        mDataUsageListAppsController = use(DataUsageListAppsController.class);
+        mDataUsageListAppsController.init(mTemplate);
     }
 
     @Override
@@ -216,7 +207,6 @@
         super.onResume();
         mLoadingViewController.showLoadingViewDelayed();
         mDataStateListener.start(mSubId);
-        mCycles = null;
         mLastDisplayedCycle = null;
 
         // kick off loader for network history
@@ -234,16 +224,6 @@
         mDataStateListener.stop();
 
         getLoaderManager().destroyLoader(LOADER_CHART_DATA);
-        getLoaderManager().destroyLoader(LOADER_SUMMARY);
-    }
-
-    @Override
-    public void onDestroy() {
-        if (mUidDetailProvider != null) {
-            mUidDetailProvider.clearCache();
-            mUidDetailProvider = null;
-        }
-        super.onDestroy();
     }
 
     @Override
@@ -352,6 +332,7 @@
         if (mCycleData != null) {
             mCycleAdapter.updateCycleList(mCycleData);
         }
+        mDataUsageListAppsController.setCycleData(mCycleData);
         updateSelectedCycle();
     }
 
@@ -402,67 +383,18 @@
         if (LOGD) Log.d(TAG, "updateDetailData()");
 
         // kick off loader for detailed stats
-        getLoaderManager().restartLoader(LOADER_SUMMARY, null /* args */,
-                mNetworkStatsDetailCallbacks);
+        mDataUsageListAppsController.update(
+                mSubscriptionInfoEntity == null ? null : mSubscriptionInfoEntity.carrierId,
+                mChart.getInspectStart(),
+                mChart.getInspectEnd()
+        );
 
         final long totalBytes = mCycleData != null && !mCycleData.isEmpty()
-            ? mCycleData.get(mCycleSpinner.getSelectedItemPosition()).getTotalUsage() : 0;
+                ? mCycleData.get(mCycleSpinner.getSelectedItemPosition()).getTotalUsage() : 0;
         final CharSequence totalPhrase = DataUsageUtils.formatDataUsage(getActivity(), totalBytes);
         mUsageAmount.setTitle(getString(R.string.data_used_template, totalPhrase));
     }
 
-    /**
-     * Bind the given buckets.
-     */
-    private void bindStats(List<AppDataUsageRepository.Bucket> buckets) {
-        mApps.removeAll();
-        AppDataUsageRepository repository = new AppDataUsageRepository(
-                requireContext(),
-                ActivityManager.getCurrentUser(),
-                mSubscriptionInfoEntity == null ? null : mSubscriptionInfoEntity.carrierId,
-                appItem -> mUidDetailProvider.getUidDetail(appItem.key, true).packageName
-        );
-        for (var itemPercentPair : repository.getAppPercent(buckets)) {
-            final AppDataUsagePreference preference = new AppDataUsagePreference(getContext(),
-                    itemPercentPair.getFirst(), itemPercentPair.getSecond(), mUidDetailProvider);
-            preference.setOnPreferenceClickListener(p -> {
-                AppDataUsagePreference pref = (AppDataUsagePreference) p;
-                startAppDataUsage(pref.getItem());
-                return true;
-            });
-            mApps.addPreference(preference);
-        }
-    }
-
-    @VisibleForTesting
-    void startAppDataUsage(AppItem item) {
-        if (mCycleData == null) {
-            return;
-        }
-        final Bundle args = new Bundle();
-        args.putParcelable(AppDataUsage.ARG_APP_ITEM, item);
-        args.putParcelable(AppDataUsage.ARG_NETWORK_TEMPLATE, mTemplate);
-        if (mCycles == null) {
-            mCycles = new ArrayList<>();
-            for (NetworkCycleChartData data : mCycleData) {
-                if (mCycles.isEmpty()) {
-                    mCycles.add(data.getEndTime());
-                }
-                mCycles.add(data.getStartTime());
-            }
-        }
-        args.putSerializable(AppDataUsage.ARG_NETWORK_CYCLES, mCycles);
-        args.putLong(AppDataUsage.ARG_SELECTED_CYCLE,
-            mCycleData.get(mCycleSpinner.getSelectedItemPosition()).getEndTime());
-
-        new SubSettingLauncher(getContext())
-                .setDestination(AppDataUsage.class.getName())
-                .setTitleRes(R.string.data_usage_app_summary_title)
-                .setArguments(args)
-                .setSourceMetricsCategory(getMetricsCategory())
-                .launch();
-    }
-
     private final OnItemSelectedListener mCycleListener = new OnItemSelectedListener() {
         @Override
         public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
@@ -502,44 +434,6 @@
                 }
             };
 
-    private final LoaderCallbacks<NetworkStats> mNetworkStatsDetailCallbacks =
-            new LoaderCallbacks<>() {
-                @Override
-                @NonNull
-                public Loader<NetworkStats> onCreateLoader(int id, Bundle args) {
-                    return new NetworkStatsSummaryLoader.Builder(getContext())
-                            .setStartTime(mChart.getInspectStart())
-                            .setEndTime(mChart.getInspectEnd())
-                            .setNetworkTemplate(mTemplate)
-                            .build();
-                }
-
-                @Override
-                public void onLoadFinished(
-                        @NonNull Loader<NetworkStats> loader, NetworkStats data) {
-                    bindStats(AppDataUsageRepository.Companion.convertToBuckets(data));
-                    updateEmptyVisible();
-                }
-
-                @Override
-                public void onLoaderReset(@NonNull Loader<NetworkStats> loader) {
-                    mApps.removeAll();
-                    updateEmptyVisible();
-                }
-
-                private void updateEmptyVisible() {
-                    if ((mApps.getPreferenceCount() != 0)
-                            != (getPreferenceScreen().getPreferenceCount() != 0)) {
-                        if (mApps.getPreferenceCount() != 0) {
-                            getPreferenceScreen().addPreference(mUsageAmount);
-                            getPreferenceScreen().addPreference(mApps);
-                        } else {
-                            getPreferenceScreen().removeAll();
-                        }
-                    }
-                }
-            };
-
     private static boolean isGuestUser(Context context) {
         if (context == null) return false;
         final UserManager userManager = context.getSystemService(UserManager.class);
diff --git a/src/com/android/settings/datausage/DataUsageListAppsController.kt b/src/com/android/settings/datausage/DataUsageListAppsController.kt
new file mode 100644
index 0000000..cc55e1a
--- /dev/null
+++ b/src/com/android/settings/datausage/DataUsageListAppsController.kt
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2023 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.datausage
+
+import android.app.ActivityManager
+import android.content.Context
+import android.net.NetworkTemplate
+import android.os.Bundle
+import androidx.annotation.VisibleForTesting
+import androidx.lifecycle.LifecycleCoroutineScope
+import androidx.lifecycle.LifecycleOwner
+import androidx.lifecycle.lifecycleScope
+import androidx.preference.PreferenceGroup
+import androidx.preference.PreferenceScreen
+import com.android.settings.R
+import com.android.settings.core.BasePreferenceController
+import com.android.settings.core.SubSettingLauncher
+import com.android.settings.datausage.lib.AppDataUsageRepository
+import com.android.settingslib.AppItem
+import com.android.settingslib.net.NetworkCycleChartData
+import com.android.settingslib.net.UidDetailProvider
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.withContext
+
+class DataUsageListAppsController(context: Context, preferenceKey: String) :
+    BasePreferenceController(context, preferenceKey) {
+
+    private val uidDetailProvider = UidDetailProvider(context)
+    private lateinit var template: NetworkTemplate
+    private lateinit var repository: AppDataUsageRepository
+    private lateinit var preference: PreferenceGroup
+    private lateinit var lifecycleScope: LifecycleCoroutineScope
+
+    private var cycleData: List<NetworkCycleChartData>? = null
+
+    fun init(template: NetworkTemplate) {
+        this.template = template
+        repository = AppDataUsageRepository(
+            context = mContext,
+            currentUserId = ActivityManager.getCurrentUser(),
+            template = template,
+        ) { appItem: AppItem -> uidDetailProvider.getUidDetail(appItem.key, true).packageName }
+    }
+
+    override fun getAvailabilityStatus() = AVAILABLE
+
+    override fun displayPreference(screen: PreferenceScreen) {
+        super.displayPreference(screen)
+        preference = screen.findPreference(preferenceKey)!!
+    }
+
+    override fun onViewCreated(viewLifecycleOwner: LifecycleOwner) {
+        lifecycleScope = viewLifecycleOwner.lifecycleScope
+    }
+
+    fun setCycleData(cycleData: List<NetworkCycleChartData>?) {
+        this.cycleData = cycleData
+    }
+
+    fun update(carrierId: Int?, startTime: Long, endTime: Long) = lifecycleScope.launch {
+        val apps = withContext(Dispatchers.Default) {
+            repository.getAppPercent(carrierId, startTime, endTime).map { (appItem, percent) ->
+                AppDataUsagePreference(mContext, appItem, percent, uidDetailProvider).apply {
+                    setOnPreferenceClickListener {
+                        startAppDataUsage(appItem, endTime)
+                        true
+                    }
+                }
+            }
+        }
+        preference.removeAll()
+        for (app in apps) {
+            preference.addPreference(app)
+        }
+    }
+
+    @VisibleForTesting
+    fun startAppDataUsage(item: AppItem, endTime: Long) {
+        val cycleData = cycleData ?: return
+        val args = Bundle().apply {
+            putParcelable(AppDataUsage.ARG_APP_ITEM, item)
+            putParcelable(AppDataUsage.ARG_NETWORK_TEMPLATE, template)
+            val cycles = ArrayList<Long>().apply {
+                for (data in cycleData) {
+                    if (isEmpty()) add(data.endTime)
+                    add(data.startTime)
+                }
+            }
+            putSerializable(AppDataUsage.ARG_NETWORK_CYCLES, cycles)
+            putLong(AppDataUsage.ARG_SELECTED_CYCLE, endTime)
+        }
+        SubSettingLauncher(mContext).apply {
+            setDestination(AppDataUsage::class.java.name)
+            setTitleRes(R.string.data_usage_app_summary_title)
+            setArguments(args)
+            setSourceMetricsCategory(metricsCategory)
+        }.launch()
+    }
+}
diff --git a/src/com/android/settings/datausage/lib/AppDataUsageRepository.kt b/src/com/android/settings/datausage/lib/AppDataUsageRepository.kt
index 3813af5..074a555 100644
--- a/src/com/android/settings/datausage/lib/AppDataUsageRepository.kt
+++ b/src/com/android/settings/datausage/lib/AppDataUsageRepository.kt
@@ -17,11 +17,15 @@
 package com.android.settings.datausage.lib
 
 import android.app.usage.NetworkStats
+import android.app.usage.NetworkStatsManager
 import android.content.Context
 import android.net.NetworkPolicyManager
+import android.net.NetworkTemplate
 import android.os.Process
 import android.os.UserHandle
+import android.util.Log
 import android.util.SparseArray
+import androidx.annotation.VisibleForTesting
 import com.android.settings.R
 import com.android.settingslib.AppItem
 import com.android.settingslib.net.UidDetailProvider
@@ -30,15 +34,18 @@
 class AppDataUsageRepository(
     private val context: Context,
     private val currentUserId: Int,
-    private val carrierId: Int?,
-    private val getPackageName: (AppItem) -> String,
+    private val template: NetworkTemplate,
+    private val getPackageName: (AppItem) -> String?,
 ) {
-    data class Bucket(
-        val uid: Int,
-        val bytes: Long,
-    )
+    private val networkStatsManager = context.getSystemService(NetworkStatsManager::class.java)!!
 
-    fun getAppPercent(buckets: List<Bucket>): List<Pair<AppItem, Int>> {
+    fun getAppPercent(carrierId: Int?, startTime: Long, endTime: Long): List<Pair<AppItem, Int>> {
+        val networkStats = querySummary(startTime, endTime) ?: return emptyList()
+        return getAppPercent(carrierId, convertToBuckets(networkStats))
+    }
+
+    @VisibleForTesting
+    fun getAppPercent(carrierId: Int?, buckets: List<Bucket>): List<Pair<AppItem, Int>> {
         val items = ArrayList<AppItem>()
         val knownItems = SparseArray<AppItem>()
         val profiles = context.userManager.userProfiles
@@ -61,7 +68,7 @@
             item.restricted = true
         }
 
-        val filteredItems = filterItems(items).sorted()
+        val filteredItems = filterItems(carrierId, items).sorted()
         val largest: Long = filteredItems.maxOfOrNull { it.total } ?: 0
         return filteredItems.map { item ->
             val percentTotal = if (largest > 0) (item.total * 100 / largest).toInt() else 0
@@ -69,7 +76,14 @@
         }
     }
 
-    private fun filterItems(items: List<AppItem>): List<AppItem> {
+    private fun querySummary(startTime: Long, endTime: Long): NetworkStats? = try {
+        networkStatsManager.querySummary(template, startTime, endTime)
+    } catch (e: RuntimeException) {
+        Log.e(TAG, "Exception querying network detail.", e)
+        null
+    }
+
+    private fun filterItems(carrierId: Int?, items: List<AppItem>): List<AppItem> {
         // When there is no specified SubscriptionInfo, Wi-Fi data usage will be displayed.
         // In this case, the carrier service package also needs to be hidden.
         if (carrierId != null && carrierId !in context.resources.getIntArray(
@@ -178,7 +192,15 @@
     }
 
     companion object {
-        fun convertToBuckets(stats: NetworkStats): List<Bucket> {
+        private const val TAG = "AppDataUsageRepository"
+
+        @VisibleForTesting
+        data class Bucket(
+            val uid: Int,
+            val bytes: Long,
+        )
+
+        private fun convertToBuckets(stats: NetworkStats): List<Bucket> {
             val buckets = mutableListOf<Bucket>()
             stats.use {
                 val bucket = NetworkStats.Bucket()
diff --git a/src/com/android/settings/network/apn/ApnEditPageProvider.kt b/src/com/android/settings/network/apn/ApnEditPageProvider.kt
new file mode 100644
index 0000000..c62cc78
--- /dev/null
+++ b/src/com/android/settings/network/apn/ApnEditPageProvider.kt
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2023 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.network.apn
+
+import android.content.Context
+import android.net.Uri
+import android.os.Bundle
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.platform.LocalContext
+import androidx.compose.ui.res.stringResource
+import androidx.navigation.NavType
+import androidx.navigation.navArgument
+import com.android.settings.R
+import com.android.settingslib.spa.framework.common.SettingsPageProvider
+import com.android.settingslib.spa.widget.scaffold.RegularScaffold
+import java.util.Base64
+
+const val URI_TYPE = "uriType"
+const val URI = "uri"
+const val SUB_ID = "subId"
+const val MVNO_TYPE = "mvnoType"
+const val MVNO_MATCH_DATA = "mvnoMatchData"
+const val EDIT_URL = "editUrl"
+
+object ApnEditPageProvider : SettingsPageProvider {
+
+    override val name = "Apn"
+    const val TAG = "ApnPageProvider"
+
+    override val parameter = listOf(
+        navArgument(URI_TYPE) { type = NavType.StringType },
+        navArgument(URI) { type = NavType.StringType },
+        navArgument(SUB_ID) { type = NavType.IntType },
+        navArgument(MVNO_TYPE) { type = NavType.StringType },
+        navArgument(MVNO_MATCH_DATA) { type = NavType.StringType },
+    )
+
+    @Composable
+    override fun Page(arguments: Bundle?) {
+        val context = LocalContext.current
+        ApnPage(context)
+    }
+
+    fun getRoute(
+        uriType: String,
+        uri: Uri,
+        subId: Int,
+        mMvnoType: String,
+        mMvnoMatchData: String
+    ): String = "${name}/$uriType/${
+        Base64.getUrlEncoder().encodeToString(uri.toString().toByteArray())
+    }/$subId/$mMvnoType/$mMvnoMatchData"
+}
+
+@Composable
+fun ApnPage(context: Context) {
+    RegularScaffold(
+        title = stringResource(id = R.string.apn_edit),
+    ) {
+    }
+}
\ No newline at end of file
diff --git a/src/com/android/settings/network/apn/ApnPreference.java b/src/com/android/settings/network/apn/ApnPreference.java
index f277db0..07d371a 100755
--- a/src/com/android/settings/network/apn/ApnPreference.java
+++ b/src/com/android/settings/network/apn/ApnPreference.java
@@ -16,6 +16,8 @@
 
 package com.android.settings.network.apn;
 
+import static com.android.settings.network.apn.ApnEditPageProviderKt.EDIT_URL;
+
 import android.content.ContentUris;
 import android.content.Context;
 import android.content.Intent;
@@ -34,15 +36,21 @@
 import androidx.preference.PreferenceViewHolder;
 
 import com.android.settings.R;
+import com.android.settings.flags.Flags;
+import com.android.settings.spa.SpaActivity;
 
 /**
  * Preference of APN UI entry
  */
-public class ApnPreference extends Preference implements CompoundButton.OnCheckedChangeListener,
-        View.OnClickListener {
-    private static final  String TAG = "ApnPreference";
-
+public class ApnPreference extends Preference
+        implements CompoundButton.OnCheckedChangeListener, View.OnClickListener {
+    private static final String TAG = "ApnPreference";
+    private static String sSelectedKey = null;
+    private static CompoundButton sCurrentChecked = null;
     private int mSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
+    private boolean mProtectFromCheckedChange = false;
+    private boolean mSelectable = true;
+    private boolean mHideDetails = false;
 
     /**
      * Constructor of Preference
@@ -65,12 +73,6 @@
         this(context, null);
     }
 
-    private static String sSelectedKey = null;
-    private static CompoundButton sCurrentChecked = null;
-    private boolean mProtectFromCheckedChange = false;
-    private boolean mSelectable = true;
-    private boolean mHideDetails = false;
-
     @Override
     public void onBindViewHolder(PreferenceViewHolder view) {
         super.onBindViewHolder(view);
@@ -147,25 +149,32 @@
         }
 
         if (mHideDetails) {
-            Toast.makeText(context, context.getString(
-                    R.string.cannot_change_apn_toast), Toast.LENGTH_LONG).show();
+            Toast.makeText(context, context.getString(R.string.cannot_change_apn_toast),
+                    Toast.LENGTH_LONG).show();
             return;
         }
-        final Uri url = ContentUris.withAppendedId(Telephony.Carriers.CONTENT_URI, pos);
-        final Intent editIntent = new Intent(Intent.ACTION_EDIT, url);
-        editIntent.putExtra(ApnSettings.SUB_ID, mSubId);
-        editIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
-        context.startActivity(editIntent);
-    }
 
-    public void setSelectable(boolean selectable) {
-        mSelectable = selectable;
+        final Uri url = ContentUris.withAppendedId(Telephony.Carriers.CONTENT_URI, pos);
+
+        if (Flags.newApnPageEnabled()) {
+            String route = ApnEditPageProvider.INSTANCE.getRoute(EDIT_URL, url, mSubId, "_", "_");
+            SpaActivity.startSpaActivity(context, route);
+        } else {
+            final Intent editIntent = new Intent(Intent.ACTION_EDIT, url);
+            editIntent.putExtra(ApnSettings.SUB_ID, mSubId);
+            editIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
+            context.startActivity(editIntent);
+        }
     }
 
     public boolean getSelectable() {
         return mSelectable;
     }
 
+    public void setSelectable(boolean selectable) {
+        mSelectable = selectable;
+    }
+
     public void setSubId(int subId) {
         mSubId = subId;
     }
diff --git a/src/com/android/settings/spa/SettingsSpaEnvironment.kt b/src/com/android/settings/spa/SettingsSpaEnvironment.kt
index f08a2de..40cc9a2 100644
--- a/src/com/android/settings/spa/SettingsSpaEnvironment.kt
+++ b/src/com/android/settings/spa/SettingsSpaEnvironment.kt
@@ -18,6 +18,7 @@
 
 import android.content.Context
 import android.util.FeatureFlagUtils
+import com.android.settings.network.apn.ApnEditPageProvider
 import com.android.settings.spa.about.AboutPhonePageProvider
 import com.android.settings.spa.app.AllAppListPageProvider
 import com.android.settings.spa.app.AppsMainPageProvider
@@ -34,8 +35,8 @@
 import com.android.settings.spa.app.specialaccess.NfcTagAppsSettingsProvider
 import com.android.settings.spa.app.specialaccess.PictureInPictureListProvider
 import com.android.settings.spa.app.specialaccess.SpecialAppAccessPageProvider
-import com.android.settings.spa.app.specialaccess.WifiControlAppListProvider
 import com.android.settings.spa.app.specialaccess.UseFullScreenIntentAppListProvider
+import com.android.settings.spa.app.specialaccess.WifiControlAppListProvider
 import com.android.settings.spa.app.storage.StorageAppListPageProvider
 import com.android.settings.spa.core.instrumentation.SpaLogProvider
 import com.android.settings.spa.development.UsageStatsPageProvider
@@ -95,6 +96,7 @@
                 AboutPhonePageProvider,
                 StorageAppListPageProvider.Apps,
                 StorageAppListPageProvider.Games,
+                ApnEditPageProvider,
                 ) + togglePermissionAppListTemplate.createPageProviders(),
             rootPages = listOf(
                 HomePageProvider.createSettingsPage()
diff --git a/tests/robotests/src/com/android/settings/datausage/DataUsageListTest.java b/tests/robotests/src/com/android/settings/datausage/DataUsageListTest.java
index 4640efe..b16d336 100644
--- a/tests/robotests/src/com/android/settings/datausage/DataUsageListTest.java
+++ b/tests/robotests/src/com/android/settings/datausage/DataUsageListTest.java
@@ -17,7 +17,6 @@
 package com.android.settings.datausage;
 
 import static com.google.common.truth.Truth.assertThat;
-
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.Mockito.doNothing;
 import static org.mockito.Mockito.doReturn;
@@ -44,19 +43,15 @@
 import androidx.preference.PreferenceManager;
 
 import com.android.settings.R;
-import com.android.settings.SettingsActivity;
 import com.android.settings.network.MobileDataEnabledListener;
 import com.android.settings.testutils.FakeFeatureFactory;
 import com.android.settings.widget.LoadingViewController;
-import com.android.settingslib.AppItem;
 import com.android.settingslib.NetworkPolicyEditor;
 import com.android.settingslib.core.instrumentation.VisibilityLoggerMixin;
-import com.android.settingslib.net.NetworkCycleChartData;
 
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
-import org.mockito.ArgumentCaptor;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 import org.robolectric.Robolectric;
@@ -67,9 +62,6 @@
 import org.robolectric.annotation.Implements;
 import org.robolectric.util.ReflectionHelpers;
 
-import java.util.ArrayList;
-import java.util.List;
-
 @RunWith(RobolectricTestRunner.class)
 public class DataUsageListTest {
 
@@ -196,34 +188,6 @@
     }
 
     @Test
-    public void startAppDataUsage_shouldAddCyclesInfoToLaunchArguments() {
-        final long startTime = 1521583200000L;
-        final long endTime = 1521676800000L;
-        final List<NetworkCycleChartData> data = new ArrayList<>();
-        final NetworkCycleChartData.Builder builder = new NetworkCycleChartData.Builder();
-        builder.setStartTime(startTime)
-                .setEndTime(endTime);
-        data.add(builder.build());
-        ReflectionHelpers.setField(mDataUsageList, "mCycleData", data);
-        final Spinner spinner = mock(Spinner.class);
-        when(spinner.getSelectedItemPosition()).thenReturn(0);
-        ReflectionHelpers.setField(mDataUsageList, "mCycleSpinner", spinner);
-        final ArgumentCaptor<Intent> intent = ArgumentCaptor.forClass(Intent.class);
-
-        mDataUsageList.startAppDataUsage(new AppItem());
-
-        verify(mActivity).startActivity(intent.capture());
-        final Bundle arguments =
-                intent.getValue().getBundleExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT_ARGUMENTS);
-        assertThat(arguments.getLong(AppDataUsage.ARG_SELECTED_CYCLE)).isEqualTo(endTime);
-        final ArrayList<Long> cycles =
-                (ArrayList) arguments.getSerializable(AppDataUsage.ARG_NETWORK_CYCLES);
-        assertThat(cycles).hasSize(2);
-        assertThat(cycles.get(0)).isEqualTo(endTime);
-        assertThat(cycles.get(1)).isEqualTo(startTime);
-    }
-
-    @Test
     public void onViewCreated_shouldHideCycleSpinner() {
         final View view = new View(mActivity);
         final View header = getHeader();
@@ -255,7 +219,6 @@
         mDataUsageList.onPause();
 
         verify(mLoaderManager).destroyLoader(DataUsageList.LOADER_CHART_DATA);
-        verify(mLoaderManager).destroyLoader(DataUsageList.LOADER_SUMMARY);
     }
 
     private View getHeader() {
diff --git a/tests/spa_unit/src/com/android/settings/datausage/DataUsageListAppsControllerTest.kt b/tests/spa_unit/src/com/android/settings/datausage/DataUsageListAppsControllerTest.kt
new file mode 100644
index 0000000..af5dc89
--- /dev/null
+++ b/tests/spa_unit/src/com/android/settings/datausage/DataUsageListAppsControllerTest.kt
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2023 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.datausage
+
+import android.content.Context
+import android.content.Intent
+import android.net.NetworkTemplate
+import androidx.test.core.app.ApplicationProvider
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.android.settings.SettingsActivity
+import com.android.settingslib.AppItem
+import com.android.settingslib.net.NetworkCycleChartData
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.kotlin.any
+import org.mockito.kotlin.argumentCaptor
+import org.mockito.kotlin.doNothing
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.spy
+import org.mockito.kotlin.verify
+import org.mockito.kotlin.whenever
+
+@RunWith(AndroidJUnit4::class)
+class DataUsageListAppsControllerTest {
+
+    private val context: Context = spy(ApplicationProvider.getApplicationContext()) {
+        doNothing().whenever(mock).startActivity(any())
+    }
+
+    private val controller = DataUsageListAppsController(context, "test_key")
+
+    @Before
+    fun setUp() {
+        controller.init(mock<NetworkTemplate>())
+        val data = NetworkCycleChartData.Builder().apply {
+            setStartTime(START_TIME)
+            setEndTime(END_TIME)
+        }.build()
+        controller.setCycleData(listOf(data))
+    }
+
+    @Test
+    fun startAppDataUsage_shouldAddCyclesInfoToLaunchArguments() {
+        controller.startAppDataUsage(AppItem(), END_TIME)
+
+        val intent = argumentCaptor<Intent> {
+            verify(context).startActivity(capture())
+        }.firstValue
+        val arguments = intent.getBundleExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT_ARGUMENTS)!!
+        assertThat(arguments.getLong(AppDataUsage.ARG_SELECTED_CYCLE)).isEqualTo(END_TIME)
+        assertThat(
+            arguments.getSerializable(AppDataUsage.ARG_NETWORK_CYCLES, ArrayList::class.java)
+        ).containsExactly(END_TIME, START_TIME).inOrder()
+    }
+
+    private companion object {
+        const val START_TIME = 1521583200000L
+        const val END_TIME = 1521676800000L
+    }
+}
diff --git a/tests/spa_unit/src/com/android/settings/datausage/lib/AppDataUsageRepositoryTest.kt b/tests/spa_unit/src/com/android/settings/datausage/lib/AppDataUsageRepositoryTest.kt
index 016d6d2..531e6e7 100644
--- a/tests/spa_unit/src/com/android/settings/datausage/lib/AppDataUsageRepositoryTest.kt
+++ b/tests/spa_unit/src/com/android/settings/datausage/lib/AppDataUsageRepositoryTest.kt
@@ -20,12 +20,13 @@
 import android.content.pm.UserInfo
 import android.content.res.Resources
 import android.net.NetworkPolicyManager
+import android.net.NetworkTemplate
 import android.os.UserHandle
 import android.os.UserManager
 import androidx.test.core.app.ApplicationProvider
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import com.android.settings.R
-import com.android.settings.datausage.lib.AppDataUsageRepository.Bucket
+import com.android.settings.datausage.lib.AppDataUsageRepository.Companion.Bucket
 import com.android.settingslib.AppItem
 import com.android.settingslib.spaprivileged.framework.common.userManager
 import com.google.common.truth.Truth.assertThat
@@ -72,15 +73,15 @@
         val repository = AppDataUsageRepository(
             context = context,
             currentUserId = USER_ID,
-            carrierId = null,
-            getPackageName = { "" },
+            template = Template,
+            getPackageName = { null },
         )
         val buckets = listOf(
             Bucket(uid = APP_ID_1, bytes = 1),
             Bucket(uid = APP_ID_2, bytes = 2),
         )
 
-        val appPercentList = repository.getAppPercent(buckets)
+        val appPercentList = repository.getAppPercent(null, buckets)
 
         assertThat(appPercentList).hasSize(2)
         appPercentList[0].first.apply {
@@ -102,15 +103,15 @@
         val repository = AppDataUsageRepository(
             context = context,
             currentUserId = USER_ID,
-            carrierId = HIDING_CARRIER_ID,
-            getPackageName = { if (it.key == APP_ID_1) HIDING_PACKAGE_NAME else "" },
+            template = Template,
+            getPackageName = { if (it.key == APP_ID_1) HIDING_PACKAGE_NAME else null },
         )
         val buckets = listOf(
             Bucket(uid = APP_ID_1, bytes = 1),
             Bucket(uid = APP_ID_2, bytes = 2),
         )
 
-        val appPercentList = repository.getAppPercent(buckets)
+        val appPercentList = repository.getAppPercent(HIDING_CARRIER_ID, buckets)
 
         assertThat(appPercentList).hasSize(1)
         appPercentList[0].first.apply {
@@ -127,5 +128,7 @@
         const val APP_ID_2 = 110002
         const val HIDING_CARRIER_ID = 4
         const val HIDING_PACKAGE_NAME = "hiding.package.name"
+
+        val Template: NetworkTemplate = mock<NetworkTemplate>()
     }
 }