Loading AndroidManifest.xml +1 −1 Original line number Diff line number Diff line Loading @@ -2991,7 +2991,7 @@ android:label="@string/settings_panel_title" android:theme="@style/Theme.BottomDialog" android:excludeFromRecents="true" android:launchMode="singleTask" android:launchMode="singleTop" android:exported="true"> <intent-filter> <action android:name="android.settings.panel.action.INTERNET_CONNECTIVITY" /> Loading src/com/android/settings/network/telephony/MobileDataPreferenceController.java +9 −4 Original line number Diff line number Diff line Loading @@ -119,6 +119,14 @@ public class MobileDataPreferenceController extends TelephonyTogglePreferenceCon && mSubId == SubscriptionManager.getDefaultDataSubscriptionId(); } public static Uri getObservableUri(int subId) { Uri uri = Settings.Global.getUriFor(Settings.Global.MOBILE_DATA); if (TelephonyManager.getDefault().getSimCount() != 1) { uri = Settings.Global.getUriFor(Settings.Global.MOBILE_DATA + subId); } return uri; } public void init(FragmentManager fragmentManager, int subId) { mFragmentManager = fragmentManager; mSubId = subId; Loading Loading @@ -169,10 +177,7 @@ public class MobileDataPreferenceController extends TelephonyTogglePreferenceCon } public void register(Context context, int subId) { Uri uri = Settings.Global.getUriFor(Settings.Global.MOBILE_DATA); if (TelephonyManager.getDefault().getSimCount() != 1) { uri = Settings.Global.getUriFor(Settings.Global.MOBILE_DATA + subId); } final Uri uri = getObservableUri(subId); context.getContentResolver().registerContentObserver(uri, false, this); } Loading src/com/android/settings/network/telephony/MobileDataSlice.java 0 → 100644 +249 −0 Original line number Diff line number Diff line /* * Copyright (C) 2019 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.telephony; import static android.app.slice.Slice.EXTRA_TOGGLE_STATE; import android.annotation.ColorInt; import android.app.PendingIntent; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.database.ContentObserver; import android.net.Uri; import android.os.Handler; import android.os.Looper; import android.telephony.SubscriptionInfo; import android.telephony.SubscriptionManager; import android.telephony.TelephonyManager; import androidx.core.graphics.drawable.IconCompat; import androidx.slice.Slice; import androidx.slice.builders.ListBuilder; import androidx.slice.builders.SliceAction; import com.android.settings.R; import com.android.settings.Utils; import com.android.settings.network.AirplaneModePreferenceController; import com.android.settings.slices.CustomSliceRegistry; import com.android.settings.slices.CustomSliceable; import com.android.settings.slices.SliceBackgroundWorker; import com.google.common.annotations.VisibleForTesting; import java.io.IOException; /** * Custom {@link Slice} for Mobile Data. * <p> * We make a custom slice instead of using {@link MobileDataPreferenceController} because the * pref controller is generalized across any carrier, and thus does not control a specific * subscription. We attempt to reuse any telephony-specific code from the preference controller. * * </p> * */ public class MobileDataSlice implements CustomSliceable { private final Context mContext; private final SubscriptionManager mSubscriptionManager; private final TelephonyManager mTelephonyManager; public MobileDataSlice(Context context) { mContext = context; mSubscriptionManager = mContext.getSystemService(SubscriptionManager.class); mTelephonyManager = mContext.getSystemService(TelephonyManager.class); } @Override public Slice getSlice() { final IconCompat icon = IconCompat.createWithResource(mContext, R.drawable.ic_network_cell); final String title = mContext.getText(R.string.mobile_data_settings_title).toString(); final CharSequence summary = getSummary(); @ColorInt final int color = Utils.getColorAccentDefaultColor(mContext); final PendingIntent toggleAction = getBroadcastIntent(mContext); final PendingIntent primaryAction = getPrimaryAction(); final SliceAction primarySliceAction = SliceAction.createDeeplink(primaryAction, icon, ListBuilder.ICON_IMAGE, title); final SliceAction toggleSliceAction = SliceAction.createToggle(toggleAction, null /* actionTitle */, isMobileDataEnabled()); final ListBuilder listBuilder = new ListBuilder(mContext, getUri(), ListBuilder.INFINITY) .setAccentColor(color) .addRow(new ListBuilder.RowBuilder() .setTitle(title) .setSubtitle(summary) .addEndItem(toggleSliceAction) .setPrimaryAction(primarySliceAction)); return listBuilder.build(); } @Override public Uri getUri() { return CustomSliceRegistry.MOBILE_DATA_SLICE_URI; } @Override public void onNotifyChange(Intent intent) { // Don't make a change if we are in Airplane Mode. if (isAirplaneModeEnabled()) { return; } final boolean newState = intent.getBooleanExtra(EXTRA_TOGGLE_STATE, isMobileDataEnabled()); final int defaultSubId = getDefaultSubscriptionId(mSubscriptionManager); if (defaultSubId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) { return; // No subscription - do nothing. } MobileNetworkUtils.setMobileDataEnabled(mContext, defaultSubId, newState, false /* disableOtherSubscriptions */); // Do not notifyChange on Uri. The service takes longer to update the current value than it // does for the Slice to check the current value again. Let {@link WifiScanWorker} // handle it. } @Override public IntentFilter getIntentFilter() { final IntentFilter filter = new IntentFilter(); filter.addAction(Intent.ACTION_AIRPLANE_MODE_CHANGED); return filter; } @Override public Intent getIntent() { return new Intent(mContext, MobileNetworkActivity.class); } @Override public Class<? extends SliceBackgroundWorker> getBackgroundWorkerClass() { return MobileDataWorker.class; } protected static int getDefaultSubscriptionId(SubscriptionManager subscriptionManager) { final SubscriptionInfo defaultSubscription = subscriptionManager.getDefaultDataSubscriptionInfo(); if (defaultSubscription == null) { return SubscriptionManager.INVALID_SUBSCRIPTION_ID; // No default subscription } return defaultSubscription.getSubscriptionId(); } private CharSequence getSummary() { final SubscriptionInfo defaultSubscription = mSubscriptionManager.getDefaultDataSubscriptionInfo(); if (defaultSubscription == null) { return null; // no summary text } return defaultSubscription.getDisplayName(); } private PendingIntent getPrimaryAction() { final Intent intent = getIntent(); return PendingIntent.getActivity(mContext, 0 /* requestCode */, intent, 0 /* flags */); } @VisibleForTesting boolean isAirplaneModeEnabled() { // Generic key since we only want the method check - no UI. AirplaneModePreferenceController controller = new AirplaneModePreferenceController(mContext, "key" /* Key */); return controller.isChecked(); } @VisibleForTesting boolean isMobileDataEnabled() { if (mTelephonyManager == null) { return false; } return mTelephonyManager.isDataEnabled(); } /** * Listener for mobile data state changes. * * <p> * Listen to individual subscription changes since there is no framework broadcast. * * This worker registers a ContentObserver in the background and updates the MobileData * Slice when the value changes. */ public static class MobileDataWorker extends SliceBackgroundWorker<Void> { DataContentObserver mMobileDataObserver; public MobileDataWorker(Context context, Uri uri) { super(context, uri); final Handler handler = new Handler(Looper.getMainLooper()); mMobileDataObserver = new DataContentObserver(handler, this); } @Override protected void onSlicePinned() { final SubscriptionManager subscriptionManager = getContext().getSystemService(SubscriptionManager.class); mMobileDataObserver.register(getContext(), getDefaultSubscriptionId(subscriptionManager)); } @Override protected void onSliceUnpinned() { mMobileDataObserver.unRegister(getContext()); } @Override public void close() throws IOException { mMobileDataObserver = null; } public void updateSlice() { notifySliceChange(); } public class DataContentObserver extends ContentObserver { private final MobileDataWorker mSliceBackgroundWorker; public DataContentObserver(Handler handler, MobileDataWorker backgroundWorker) { super(handler); mSliceBackgroundWorker = backgroundWorker; } @Override public void onChange(boolean selfChange) { mSliceBackgroundWorker.updateSlice(); } public void register(Context context, int subId) { final Uri uri = MobileDataPreferenceController.getObservableUri(subId); context.getContentResolver().registerContentObserver(uri, false, this); } public void unRegister(Context context) { context.getContentResolver().unregisterContentObserver(this); } } } } src/com/android/settings/panel/InternetConnectivityPanel.java +1 −0 Original line number Diff line number Diff line Loading @@ -55,6 +55,7 @@ public class InternetConnectivityPanel implements PanelContent { public List<Uri> getSlices() { final List<Uri> uris = new ArrayList<>(); uris.add(CustomSliceRegistry.WIFI_SLICE_URI); uris.add(CustomSliceRegistry.MOBILE_DATA_SLICE_URI); uris.add(CustomSliceRegistry.AIRPLANE_URI); return uris; } Loading src/com/android/settings/slices/CustomSliceManager.java +2 −1 Original line number Diff line number Diff line Loading @@ -18,7 +18,6 @@ package com.android.settings.slices; import android.content.Context; import android.net.Uri; import android.text.TextUtils; import android.util.ArrayMap; import androidx.annotation.VisibleForTesting; Loading @@ -35,6 +34,7 @@ import com.android.settings.homepage.contextualcards.slices.LowStorageSlice; import com.android.settings.homepage.contextualcards.slices.NotificationChannelSlice; import com.android.settings.location.LocationSlice; import com.android.settings.media.MediaOutputSlice; import com.android.settings.network.telephony.MobileDataSlice; import com.android.settings.wifi.slice.ContextualWifiSlice; import com.android.settings.wifi.slice.WifiSlice; Loading Loading @@ -123,6 +123,7 @@ public class CustomSliceManager { mUriMap.put(CustomSliceRegistry.FLASHLIGHT_SLICE_URI, FlashlightSlice.class); mUriMap.put(CustomSliceRegistry.LOCATION_SLICE_URI, LocationSlice.class); mUriMap.put(CustomSliceRegistry.LOW_STORAGE_SLICE_URI, LowStorageSlice.class); mUriMap.put(CustomSliceRegistry.MOBILE_DATA_SLICE_URI, MobileDataSlice.class); mUriMap.put(CustomSliceRegistry.NOTIFICATION_CHANNEL_SLICE_URI, NotificationChannelSlice.class); mUriMap.put(CustomSliceRegistry.STORAGE_SLICE_URI, StorageSlice.class); Loading Loading
AndroidManifest.xml +1 −1 Original line number Diff line number Diff line Loading @@ -2991,7 +2991,7 @@ android:label="@string/settings_panel_title" android:theme="@style/Theme.BottomDialog" android:excludeFromRecents="true" android:launchMode="singleTask" android:launchMode="singleTop" android:exported="true"> <intent-filter> <action android:name="android.settings.panel.action.INTERNET_CONNECTIVITY" /> Loading
src/com/android/settings/network/telephony/MobileDataPreferenceController.java +9 −4 Original line number Diff line number Diff line Loading @@ -119,6 +119,14 @@ public class MobileDataPreferenceController extends TelephonyTogglePreferenceCon && mSubId == SubscriptionManager.getDefaultDataSubscriptionId(); } public static Uri getObservableUri(int subId) { Uri uri = Settings.Global.getUriFor(Settings.Global.MOBILE_DATA); if (TelephonyManager.getDefault().getSimCount() != 1) { uri = Settings.Global.getUriFor(Settings.Global.MOBILE_DATA + subId); } return uri; } public void init(FragmentManager fragmentManager, int subId) { mFragmentManager = fragmentManager; mSubId = subId; Loading Loading @@ -169,10 +177,7 @@ public class MobileDataPreferenceController extends TelephonyTogglePreferenceCon } public void register(Context context, int subId) { Uri uri = Settings.Global.getUriFor(Settings.Global.MOBILE_DATA); if (TelephonyManager.getDefault().getSimCount() != 1) { uri = Settings.Global.getUriFor(Settings.Global.MOBILE_DATA + subId); } final Uri uri = getObservableUri(subId); context.getContentResolver().registerContentObserver(uri, false, this); } Loading
src/com/android/settings/network/telephony/MobileDataSlice.java 0 → 100644 +249 −0 Original line number Diff line number Diff line /* * Copyright (C) 2019 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.telephony; import static android.app.slice.Slice.EXTRA_TOGGLE_STATE; import android.annotation.ColorInt; import android.app.PendingIntent; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.database.ContentObserver; import android.net.Uri; import android.os.Handler; import android.os.Looper; import android.telephony.SubscriptionInfo; import android.telephony.SubscriptionManager; import android.telephony.TelephonyManager; import androidx.core.graphics.drawable.IconCompat; import androidx.slice.Slice; import androidx.slice.builders.ListBuilder; import androidx.slice.builders.SliceAction; import com.android.settings.R; import com.android.settings.Utils; import com.android.settings.network.AirplaneModePreferenceController; import com.android.settings.slices.CustomSliceRegistry; import com.android.settings.slices.CustomSliceable; import com.android.settings.slices.SliceBackgroundWorker; import com.google.common.annotations.VisibleForTesting; import java.io.IOException; /** * Custom {@link Slice} for Mobile Data. * <p> * We make a custom slice instead of using {@link MobileDataPreferenceController} because the * pref controller is generalized across any carrier, and thus does not control a specific * subscription. We attempt to reuse any telephony-specific code from the preference controller. * * </p> * */ public class MobileDataSlice implements CustomSliceable { private final Context mContext; private final SubscriptionManager mSubscriptionManager; private final TelephonyManager mTelephonyManager; public MobileDataSlice(Context context) { mContext = context; mSubscriptionManager = mContext.getSystemService(SubscriptionManager.class); mTelephonyManager = mContext.getSystemService(TelephonyManager.class); } @Override public Slice getSlice() { final IconCompat icon = IconCompat.createWithResource(mContext, R.drawable.ic_network_cell); final String title = mContext.getText(R.string.mobile_data_settings_title).toString(); final CharSequence summary = getSummary(); @ColorInt final int color = Utils.getColorAccentDefaultColor(mContext); final PendingIntent toggleAction = getBroadcastIntent(mContext); final PendingIntent primaryAction = getPrimaryAction(); final SliceAction primarySliceAction = SliceAction.createDeeplink(primaryAction, icon, ListBuilder.ICON_IMAGE, title); final SliceAction toggleSliceAction = SliceAction.createToggle(toggleAction, null /* actionTitle */, isMobileDataEnabled()); final ListBuilder listBuilder = new ListBuilder(mContext, getUri(), ListBuilder.INFINITY) .setAccentColor(color) .addRow(new ListBuilder.RowBuilder() .setTitle(title) .setSubtitle(summary) .addEndItem(toggleSliceAction) .setPrimaryAction(primarySliceAction)); return listBuilder.build(); } @Override public Uri getUri() { return CustomSliceRegistry.MOBILE_DATA_SLICE_URI; } @Override public void onNotifyChange(Intent intent) { // Don't make a change if we are in Airplane Mode. if (isAirplaneModeEnabled()) { return; } final boolean newState = intent.getBooleanExtra(EXTRA_TOGGLE_STATE, isMobileDataEnabled()); final int defaultSubId = getDefaultSubscriptionId(mSubscriptionManager); if (defaultSubId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) { return; // No subscription - do nothing. } MobileNetworkUtils.setMobileDataEnabled(mContext, defaultSubId, newState, false /* disableOtherSubscriptions */); // Do not notifyChange on Uri. The service takes longer to update the current value than it // does for the Slice to check the current value again. Let {@link WifiScanWorker} // handle it. } @Override public IntentFilter getIntentFilter() { final IntentFilter filter = new IntentFilter(); filter.addAction(Intent.ACTION_AIRPLANE_MODE_CHANGED); return filter; } @Override public Intent getIntent() { return new Intent(mContext, MobileNetworkActivity.class); } @Override public Class<? extends SliceBackgroundWorker> getBackgroundWorkerClass() { return MobileDataWorker.class; } protected static int getDefaultSubscriptionId(SubscriptionManager subscriptionManager) { final SubscriptionInfo defaultSubscription = subscriptionManager.getDefaultDataSubscriptionInfo(); if (defaultSubscription == null) { return SubscriptionManager.INVALID_SUBSCRIPTION_ID; // No default subscription } return defaultSubscription.getSubscriptionId(); } private CharSequence getSummary() { final SubscriptionInfo defaultSubscription = mSubscriptionManager.getDefaultDataSubscriptionInfo(); if (defaultSubscription == null) { return null; // no summary text } return defaultSubscription.getDisplayName(); } private PendingIntent getPrimaryAction() { final Intent intent = getIntent(); return PendingIntent.getActivity(mContext, 0 /* requestCode */, intent, 0 /* flags */); } @VisibleForTesting boolean isAirplaneModeEnabled() { // Generic key since we only want the method check - no UI. AirplaneModePreferenceController controller = new AirplaneModePreferenceController(mContext, "key" /* Key */); return controller.isChecked(); } @VisibleForTesting boolean isMobileDataEnabled() { if (mTelephonyManager == null) { return false; } return mTelephonyManager.isDataEnabled(); } /** * Listener for mobile data state changes. * * <p> * Listen to individual subscription changes since there is no framework broadcast. * * This worker registers a ContentObserver in the background and updates the MobileData * Slice when the value changes. */ public static class MobileDataWorker extends SliceBackgroundWorker<Void> { DataContentObserver mMobileDataObserver; public MobileDataWorker(Context context, Uri uri) { super(context, uri); final Handler handler = new Handler(Looper.getMainLooper()); mMobileDataObserver = new DataContentObserver(handler, this); } @Override protected void onSlicePinned() { final SubscriptionManager subscriptionManager = getContext().getSystemService(SubscriptionManager.class); mMobileDataObserver.register(getContext(), getDefaultSubscriptionId(subscriptionManager)); } @Override protected void onSliceUnpinned() { mMobileDataObserver.unRegister(getContext()); } @Override public void close() throws IOException { mMobileDataObserver = null; } public void updateSlice() { notifySliceChange(); } public class DataContentObserver extends ContentObserver { private final MobileDataWorker mSliceBackgroundWorker; public DataContentObserver(Handler handler, MobileDataWorker backgroundWorker) { super(handler); mSliceBackgroundWorker = backgroundWorker; } @Override public void onChange(boolean selfChange) { mSliceBackgroundWorker.updateSlice(); } public void register(Context context, int subId) { final Uri uri = MobileDataPreferenceController.getObservableUri(subId); context.getContentResolver().registerContentObserver(uri, false, this); } public void unRegister(Context context) { context.getContentResolver().unregisterContentObserver(this); } } } }
src/com/android/settings/panel/InternetConnectivityPanel.java +1 −0 Original line number Diff line number Diff line Loading @@ -55,6 +55,7 @@ public class InternetConnectivityPanel implements PanelContent { public List<Uri> getSlices() { final List<Uri> uris = new ArrayList<>(); uris.add(CustomSliceRegistry.WIFI_SLICE_URI); uris.add(CustomSliceRegistry.MOBILE_DATA_SLICE_URI); uris.add(CustomSliceRegistry.AIRPLANE_URI); return uris; } Loading
src/com/android/settings/slices/CustomSliceManager.java +2 −1 Original line number Diff line number Diff line Loading @@ -18,7 +18,6 @@ package com.android.settings.slices; import android.content.Context; import android.net.Uri; import android.text.TextUtils; import android.util.ArrayMap; import androidx.annotation.VisibleForTesting; Loading @@ -35,6 +34,7 @@ import com.android.settings.homepage.contextualcards.slices.LowStorageSlice; import com.android.settings.homepage.contextualcards.slices.NotificationChannelSlice; import com.android.settings.location.LocationSlice; import com.android.settings.media.MediaOutputSlice; import com.android.settings.network.telephony.MobileDataSlice; import com.android.settings.wifi.slice.ContextualWifiSlice; import com.android.settings.wifi.slice.WifiSlice; Loading Loading @@ -123,6 +123,7 @@ public class CustomSliceManager { mUriMap.put(CustomSliceRegistry.FLASHLIGHT_SLICE_URI, FlashlightSlice.class); mUriMap.put(CustomSliceRegistry.LOCATION_SLICE_URI, LocationSlice.class); mUriMap.put(CustomSliceRegistry.LOW_STORAGE_SLICE_URI, LowStorageSlice.class); mUriMap.put(CustomSliceRegistry.MOBILE_DATA_SLICE_URI, MobileDataSlice.class); mUriMap.put(CustomSliceRegistry.NOTIFICATION_CHANNEL_SLICE_URI, NotificationChannelSlice.class); mUriMap.put(CustomSliceRegistry.STORAGE_SLICE_URI, StorageSlice.class); Loading