Add a Preference into "Network & internet" for new tethering UI
Adding a MasterSwitchPreference into "Network & internet" and create corresponding controller. The preference is hidden by the feature flag. It does NOT affect the existing UI. Bug: 146147778 Test: CodeInspectionTest, TetherPreferenceControllerTest, NetworkDashboardFragmentTest Change-Id: Ida8e088efbb22d0c983acecbc420672206392dad
This commit is contained in:
@@ -79,6 +79,18 @@
|
||||
settings:userRestriction="no_config_tethering"
|
||||
settings:useAdminDisabledSummary="true" />
|
||||
|
||||
<com.android.settings.widget.MasterSwitchPreference
|
||||
android:fragment="com.android.settings.AllInOneTetherSettings"
|
||||
android:key="all_tether_settings"
|
||||
android:title="@string/tether_settings_title_all"
|
||||
android:icon="@drawable/ic_wifi_tethering"
|
||||
android:order="6"
|
||||
android:summary="@string/summary_placeholder"
|
||||
settings:controller="com.android.settings.network.AllInOneTetherPreferenceController"
|
||||
settings:keywords="@string/keywords_hotspot_tethering"
|
||||
settings:userRestriction="no_config_tethering"
|
||||
settings:useAdminDisabledSummary="true" />
|
||||
|
||||
<com.android.settings.datausage.DataSaverPreference
|
||||
android:key="restrict_background_parent_entry"
|
||||
android:title="@string/data_saver_title"
|
||||
|
@@ -0,0 +1,151 @@
|
||||
/*
|
||||
* Copyright (C) 2020 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;
|
||||
|
||||
import static android.os.UserManager.DISALLOW_CONFIG_TETHERING;
|
||||
|
||||
import static com.android.settingslib.RestrictedLockUtilsInternal.checkIfRestrictionEnforced;
|
||||
|
||||
import android.bluetooth.BluetoothAdapter;
|
||||
import android.bluetooth.BluetoothPan;
|
||||
import android.bluetooth.BluetoothProfile;
|
||||
import android.content.Context;
|
||||
import android.os.UserHandle;
|
||||
import android.util.FeatureFlagUtils;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.annotation.VisibleForTesting;
|
||||
import androidx.lifecycle.Lifecycle;
|
||||
import androidx.lifecycle.Lifecycle.Event;
|
||||
import androidx.lifecycle.LifecycleObserver;
|
||||
import androidx.lifecycle.OnLifecycleEvent;
|
||||
import androidx.preference.PreferenceScreen;
|
||||
|
||||
import com.android.settings.TetherSettings;
|
||||
import com.android.settings.core.BasePreferenceController;
|
||||
import com.android.settings.core.FeatureFlags;
|
||||
import com.android.settings.widget.MasterSwitchController;
|
||||
import com.android.settings.widget.MasterSwitchPreference;
|
||||
import com.android.settingslib.TetherUtil;
|
||||
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
/**
|
||||
* This controller helps to manage the switch state and visibility of "Hotspot & tethering" switch
|
||||
* preference. It updates the preference summary text based on tethering state.
|
||||
*/
|
||||
public class AllInOneTetherPreferenceController extends BasePreferenceController implements
|
||||
LifecycleObserver {
|
||||
private static final String TAG = "AllInOneTetherPreferenceController";
|
||||
|
||||
private final boolean mAdminDisallowedTetherConfig;
|
||||
private final AtomicReference<BluetoothPan> mBluetoothPan;
|
||||
private final BluetoothAdapter mBluetoothAdapter;
|
||||
@VisibleForTesting
|
||||
final BluetoothProfile.ServiceListener mBtProfileServiceListener =
|
||||
new BluetoothProfile.ServiceListener() {
|
||||
@Override
|
||||
public void onServiceConnected(int profile, BluetoothProfile proxy) {
|
||||
mBluetoothPan.set((BluetoothPan) proxy);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onServiceDisconnected(int profile) {
|
||||
mBluetoothPan.set(null);
|
||||
}
|
||||
};
|
||||
|
||||
private MasterSwitchPreference mPreference;
|
||||
|
||||
@VisibleForTesting(otherwise = VisibleForTesting.NONE)
|
||||
AllInOneTetherPreferenceController() {
|
||||
super(null /*context*/, "test");
|
||||
mAdminDisallowedTetherConfig = false;
|
||||
mBluetoothPan = new AtomicReference<>();
|
||||
mBluetoothAdapter = null;
|
||||
}
|
||||
|
||||
public AllInOneTetherPreferenceController(Context context, String key) {
|
||||
super(context, key);
|
||||
mBluetoothPan = new AtomicReference<>();
|
||||
mAdminDisallowedTetherConfig = checkIfRestrictionEnforced(
|
||||
context, DISALLOW_CONFIG_TETHERING, UserHandle.myUserId()) != null;
|
||||
mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void displayPreference(PreferenceScreen screen) {
|
||||
super.displayPreference(screen);
|
||||
mPreference = screen.findPreference(mPreferenceKey);
|
||||
if (mPreference != null && !mAdminDisallowedTetherConfig) {
|
||||
// Grey out if provisioning is not available.
|
||||
mPreference.setEnabled(!TetherSettings.isProvisioningNeededButUnavailable(mContext));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getAvailabilityStatus() {
|
||||
if (!TetherUtil.isTetherAvailable(mContext)
|
||||
|| !FeatureFlagUtils.isEnabled(mContext, FeatureFlags.TETHER_ALL_IN_ONE)) {
|
||||
return CONDITIONALLY_UNAVAILABLE;
|
||||
} else {
|
||||
return AVAILABLE;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public CharSequence getSummary() {
|
||||
if (mPreference != null && mPreference.isChecked()) {
|
||||
// TODO(b/149256198) update summary accordingly.
|
||||
return "Tethering";
|
||||
}
|
||||
|
||||
return "Not sharing internet with other devices";
|
||||
}
|
||||
|
||||
@OnLifecycleEvent(Event.ON_CREATE)
|
||||
public void onCreate() {
|
||||
if (mBluetoothAdapter != null
|
||||
&& mBluetoothAdapter.getState() == BluetoothAdapter.STATE_ON) {
|
||||
mBluetoothAdapter.getProfileProxy(mContext, mBtProfileServiceListener,
|
||||
BluetoothProfile.PAN);
|
||||
}
|
||||
}
|
||||
|
||||
@OnLifecycleEvent(Event.ON_DESTROY)
|
||||
public void onDestroy() {
|
||||
final BluetoothProfile profile = mBluetoothPan.getAndSet(null);
|
||||
if (profile != null && mBluetoothAdapter != null) {
|
||||
mBluetoothAdapter.closeProfileProxy(BluetoothProfile.PAN, profile);
|
||||
}
|
||||
}
|
||||
|
||||
void init(Lifecycle lifecycle) {
|
||||
lifecycle.addObserver(this);
|
||||
}
|
||||
|
||||
void initEnabler(Lifecycle lifecycle) {
|
||||
if (mPreference != null) {
|
||||
TetherEnabler tetherEnabler = new TetherEnabler(
|
||||
mContext, new MasterSwitchController(mPreference), mBluetoothPan);
|
||||
if (lifecycle != null) {
|
||||
lifecycle.addObserver(tetherEnabler);
|
||||
}
|
||||
} else {
|
||||
Log.e(TAG, "TetherEnabler is not initialized");
|
||||
}
|
||||
}
|
||||
}
|
@@ -20,6 +20,7 @@ import static com.android.settings.network.MobilePlanPreferenceController.MANAGE
|
||||
import android.app.Dialog;
|
||||
import android.app.settings.SettingsEnums;
|
||||
import android.content.Context;
|
||||
import android.os.Bundle;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
@@ -65,6 +66,13 @@ public class NetworkDashboardFragment extends DashboardFragment implements
|
||||
|
||||
use(MultiNetworkHeaderController.class).init(getSettingsLifecycle());
|
||||
use(AirplaneModePreferenceController.class).setFragment(this);
|
||||
use(AllInOneTetherPreferenceController.class).init(getSettingsLifecycle());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
|
||||
super.onCreatePreferences(savedInstanceState, rootKey);
|
||||
use(AllInOneTetherPreferenceController.class).initEnabler(getSettingsLifecycle());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@@ -39,7 +39,6 @@ import androidx.annotation.VisibleForTesting;
|
||||
import androidx.preference.Preference;
|
||||
import androidx.preference.PreferenceScreen;
|
||||
|
||||
import com.android.settings.AllInOneTetherSettings;
|
||||
import com.android.settings.R;
|
||||
import com.android.settings.TetherSettings;
|
||||
import com.android.settings.core.FeatureFlags;
|
||||
@@ -112,16 +111,13 @@ public class TetherPreferenceController extends AbstractPreferenceController imp
|
||||
|
||||
// Grey out if provisioning is not available.
|
||||
mPreference.setEnabled(!TetherSettings.isProvisioningNeededButUnavailable(mContext));
|
||||
|
||||
if (FeatureFlagUtils.isEnabled(mContext, FeatureFlags.TETHER_ALL_IN_ONE)) {
|
||||
mPreference.setFragment(AllInOneTetherSettings.class.getName());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAvailable() {
|
||||
return TetherUtil.isTetherAvailable(mContext);
|
||||
return TetherUtil.isTetherAvailable(mContext)
|
||||
&& !FeatureFlagUtils.isEnabled(mContext, FeatureFlags.TETHER_ALL_IN_ONE);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@@ -0,0 +1,93 @@
|
||||
/*
|
||||
* Copyright (C) 2020 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;
|
||||
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.spy;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.verifyNoMoreInteractions;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import android.bluetooth.BluetoothAdapter;
|
||||
import android.bluetooth.BluetoothPan;
|
||||
import android.bluetooth.BluetoothProfile;
|
||||
import android.content.Context;
|
||||
|
||||
import com.android.settings.widget.MasterSwitchPreference;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
import org.robolectric.RobolectricTestRunner;
|
||||
import org.robolectric.util.ReflectionHelpers;
|
||||
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
@RunWith(RobolectricTestRunner.class)
|
||||
public class AllInOneTetherPreferenceControllerTest {
|
||||
|
||||
@Mock
|
||||
private Context mContext;
|
||||
@Mock
|
||||
private BluetoothAdapter mBluetoothAdapter;
|
||||
@Mock
|
||||
private MasterSwitchPreference mPreference;
|
||||
|
||||
private AllInOneTetherPreferenceController mController;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
mController = spy(AllInOneTetherPreferenceController.class);
|
||||
ReflectionHelpers.setField(mController, "mContext", mContext);
|
||||
ReflectionHelpers.setField(mController, "mBluetoothAdapter", mBluetoothAdapter);
|
||||
ReflectionHelpers.setField(mController, "mPreference", mPreference);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void lifeCycle_onCreate_shouldInitBluetoothPan() {
|
||||
when(mBluetoothAdapter.getState()).thenReturn(BluetoothAdapter.STATE_ON);
|
||||
mController.onCreate();
|
||||
|
||||
verify(mBluetoothAdapter).getState();
|
||||
verify(mBluetoothAdapter).getProfileProxy(mContext, mController.mBtProfileServiceListener,
|
||||
BluetoothProfile.PAN);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void lifeCycle_onCreate_shouldNotInitBluetoothPanWhenBluetoothOff() {
|
||||
when(mBluetoothAdapter.getState()).thenReturn(BluetoothAdapter.STATE_OFF);
|
||||
mController.onCreate();
|
||||
|
||||
verify(mBluetoothAdapter).getState();
|
||||
verifyNoMoreInteractions(mBluetoothAdapter);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void goThroughLifecycle_shouldDestroyBluetoothProfile() {
|
||||
final BluetoothPan pan = mock(BluetoothPan.class);
|
||||
final AtomicReference<BluetoothPan> panRef =
|
||||
ReflectionHelpers.getField(mController, "mBluetoothPan");
|
||||
panRef.set(pan);
|
||||
|
||||
mController.onDestroy();
|
||||
|
||||
verify(mBluetoothAdapter).closeProfileProxy(BluetoothProfile.PAN, pan);
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user