Merge "Add a Preference into "Network & internet" for new tethering UI"
This commit is contained in:
@@ -79,6 +79,18 @@
|
|||||||
settings:userRestriction="no_config_tethering"
|
settings:userRestriction="no_config_tethering"
|
||||||
settings:useAdminDisabledSummary="true" />
|
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
|
<com.android.settings.datausage.DataSaverPreference
|
||||||
android:key="restrict_background_parent_entry"
|
android:key="restrict_background_parent_entry"
|
||||||
android:title="@string/data_saver_title"
|
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.Dialog;
|
||||||
import android.app.settings.SettingsEnums;
|
import android.app.settings.SettingsEnums;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
import android.os.Bundle;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
import androidx.appcompat.app.AlertDialog;
|
import androidx.appcompat.app.AlertDialog;
|
||||||
@@ -65,6 +66,13 @@ public class NetworkDashboardFragment extends DashboardFragment implements
|
|||||||
|
|
||||||
use(MultiNetworkHeaderController.class).init(getSettingsLifecycle());
|
use(MultiNetworkHeaderController.class).init(getSettingsLifecycle());
|
||||||
use(AirplaneModePreferenceController.class).setFragment(this);
|
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
|
@Override
|
||||||
|
@@ -39,7 +39,6 @@ import androidx.annotation.VisibleForTesting;
|
|||||||
import androidx.preference.Preference;
|
import androidx.preference.Preference;
|
||||||
import androidx.preference.PreferenceScreen;
|
import androidx.preference.PreferenceScreen;
|
||||||
|
|
||||||
import com.android.settings.AllInOneTetherSettings;
|
|
||||||
import com.android.settings.R;
|
import com.android.settings.R;
|
||||||
import com.android.settings.TetherSettings;
|
import com.android.settings.TetherSettings;
|
||||||
import com.android.settings.core.FeatureFlags;
|
import com.android.settings.core.FeatureFlags;
|
||||||
@@ -112,16 +111,13 @@ public class TetherPreferenceController extends AbstractPreferenceController imp
|
|||||||
|
|
||||||
// Grey out if provisioning is not available.
|
// Grey out if provisioning is not available.
|
||||||
mPreference.setEnabled(!TetherSettings.isProvisioningNeededButUnavailable(mContext));
|
mPreference.setEnabled(!TetherSettings.isProvisioningNeededButUnavailable(mContext));
|
||||||
|
|
||||||
if (FeatureFlagUtils.isEnabled(mContext, FeatureFlags.TETHER_ALL_IN_ONE)) {
|
|
||||||
mPreference.setFragment(AllInOneTetherSettings.class.getName());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isAvailable() {
|
public boolean isAvailable() {
|
||||||
return TetherUtil.isTetherAvailable(mContext);
|
return TetherUtil.isTetherAvailable(mContext)
|
||||||
|
&& !FeatureFlagUtils.isEnabled(mContext, FeatureFlags.TETHER_ALL_IN_ONE);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@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