From b33f74192c998a8712ff34da257b4b415cbebdc1 Mon Sep 17 00:00:00 2001 From: Antony Sargent Date: Fri, 7 Dec 2018 15:40:00 -0800 Subject: [PATCH] Add a helper class for listening to subscription and airplane mode changes For the new multi subscriptions UX, in several places we need to listen for both subscription changes (eg a sim card was inserted or removed), as well as airplane mode changing state. This combines those in one helper class that can be reused. Bug: 116349402 Test: make RunSettingsRoboTests Change-Id: Ibcbb669ae291a917f89735dbda043742efaa58c9 --- .../network/SubscriptionsChangeListener.java | 97 +++++++++++++ .../SubscriptionsChangeListenerTest.java | 129 ++++++++++++++++++ 2 files changed, 226 insertions(+) create mode 100644 src/com/android/settings/network/SubscriptionsChangeListener.java create mode 100644 tests/robotests/src/com/android/settings/network/SubscriptionsChangeListenerTest.java diff --git a/src/com/android/settings/network/SubscriptionsChangeListener.java b/src/com/android/settings/network/SubscriptionsChangeListener.java new file mode 100644 index 00000000000..c3bb22bbb62 --- /dev/null +++ b/src/com/android/settings/network/SubscriptionsChangeListener.java @@ -0,0 +1,97 @@ +/* + * Copyright (C) 2018 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 android.content.BroadcastReceiver; +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.provider.Settings; +import android.telephony.SubscriptionManager; +import android.telephony.SubscriptionManager.OnSubscriptionsChangedListener; + +import com.android.internal.telephony.TelephonyIntents; + +/** Helper class for listening to changes in availability of telephony subscriptions */ +public class SubscriptionsChangeListener extends ContentObserver { + + public interface SubscriptionsChangeListenerClient { + void onAirplaneModeChanged(boolean airplaneModeEnabled); + void onSubscriptionsChanged(); + } + + private Context mContext; + private SubscriptionsChangeListenerClient mClient; + private SubscriptionManager mSubscriptionManager; + private OnSubscriptionsChangedListener mSubscriptionsChangedListener; + private Uri mAirplaneModeSettingUri; + private BroadcastReceiver mBroadcastReceiver; + + public SubscriptionsChangeListener(Context context, SubscriptionsChangeListenerClient client) { + super(new Handler()); + mContext = context; + mClient = client; + mSubscriptionManager = mContext.getSystemService(SubscriptionManager.class); + mSubscriptionsChangedListener = new OnSubscriptionsChangedListener() { + @Override + public void onSubscriptionsChanged() { + subscriptionsChangedCallback(); + } + }; + mAirplaneModeSettingUri = Settings.Global.getUriFor(Settings.Global.AIRPLANE_MODE_ON); + mBroadcastReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + subscriptionsChangedCallback(); + } + }; + } + + public void start() { + mSubscriptionManager.addOnSubscriptionsChangedListener(mSubscriptionsChangedListener); + mContext.getContentResolver() + .registerContentObserver(mAirplaneModeSettingUri, false, this); + final IntentFilter radioTechnologyChangedFilter = new IntentFilter( + TelephonyIntents.ACTION_RADIO_TECHNOLOGY_CHANGED); + mContext.registerReceiver(mBroadcastReceiver, radioTechnologyChangedFilter); + } + + public void stop() { + mSubscriptionManager.removeOnSubscriptionsChangedListener(mSubscriptionsChangedListener); + mContext.getContentResolver().unregisterContentObserver(this); + mContext.unregisterReceiver(mBroadcastReceiver); + } + + public boolean isAirplaneModeOn() { + return Settings.Global.getInt(mContext.getContentResolver(), + Settings.Global.AIRPLANE_MODE_ON, 0) != 0; + } + + private void subscriptionsChangedCallback() { + mClient.onSubscriptionsChanged(); + } + + @Override + public void onChange(boolean selfChange, Uri uri) { + if (uri.equals(mAirplaneModeSettingUri)) { + mClient.onAirplaneModeChanged(isAirplaneModeOn()); + } + } +} diff --git a/tests/robotests/src/com/android/settings/network/SubscriptionsChangeListenerTest.java b/tests/robotests/src/com/android/settings/network/SubscriptionsChangeListenerTest.java new file mode 100644 index 00000000000..88ea2eac522 --- /dev/null +++ b/tests/robotests/src/com/android/settings/network/SubscriptionsChangeListenerTest.java @@ -0,0 +1,129 @@ +/* + * Copyright (C) 2018 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 com.google.common.truth.Truth.assertThat; + +import static org.mockito.AdditionalMatchers.eq; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.content.BroadcastReceiver; +import android.content.ContentResolver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.database.ContentObserver; +import android.net.Uri; +import android.provider.Settings; +import android.telephony.SubscriptionManager; + +import com.android.settings.network.SubscriptionsChangeListener.SubscriptionsChangeListenerClient; +import com.android.settings.testutils.SettingsRobolectricTestRunner; + +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.RuntimeEnvironment; + +@RunWith(SettingsRobolectricTestRunner.class) +public class SubscriptionsChangeListenerTest { + + @Mock + private SubscriptionsChangeListenerClient mClient; + @Mock + private SubscriptionManager mSubscriptionManager; + + private Context mContext; + private SubscriptionsChangeListener mListener; + private Uri mAirplaneModeUri; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + mContext = spy(RuntimeEnvironment.application); + when(mContext.getSystemService(SubscriptionManager.class)).thenReturn(mSubscriptionManager); + + mAirplaneModeUri = Settings.Global.getUriFor(Settings.Global.AIRPLANE_MODE_ON); + } + + private void initListener(boolean alsoStart) { + mListener = new SubscriptionsChangeListener(mContext, mClient); + if (alsoStart) { + mListener.start(); + } + } + + @Test + public void whenStartNotCalled_noListeningWasSetup() { + final ContentResolver contentResolver = mock(ContentResolver.class); + when(mContext.getContentResolver()).thenReturn(contentResolver); + initListener(false); + verify(contentResolver, never()).registerContentObserver(any(Uri.class), anyBoolean(), + any(ContentObserver.class)); + verify(mSubscriptionManager, never()).addOnSubscriptionsChangedListener(any()); + verify(mContext, never()).registerReceiver(any(), any()); + } + + @Test + public void onSubscriptionsChangedEvent_subscriptionManagerFires_eventDeliveredToUs() { + initListener(true); + final ArgumentCaptor captor = + ArgumentCaptor.forClass(SubscriptionManager.OnSubscriptionsChangedListener.class); + verify(mSubscriptionManager).addOnSubscriptionsChangedListener(captor.capture()); + captor.getValue().onSubscriptionsChanged(); + verify(mClient).onSubscriptionsChanged(); + } + + @Test + public void onSubscriptionsChangedEvent_radioTechnologyChangedBroadcast_eventDeliveredToUs() { + initListener(true); + final ArgumentCaptor broadcastReceiverCaptor = + ArgumentCaptor.forClass(BroadcastReceiver.class); + verify(mContext).registerReceiver(broadcastReceiverCaptor.capture(), any()); + broadcastReceiverCaptor.getValue().onReceive(mContext, null); + verify(mClient).onSubscriptionsChanged(); + } + + @Test + public void onAirplaneModeChangedEvent_becameTrue_eventFires() { + initListener(true); + Settings.Global.putInt(mContext.getContentResolver(), Settings.Global.AIRPLANE_MODE_ON, 1); + mListener.onChange(false, mAirplaneModeUri); + verify(mClient).onAirplaneModeChanged(true); + assertThat(mListener.isAirplaneModeOn()).isTrue(); + } + + @Test + public void onAirplaneModeChangedEvent_becameFalse_eventFires() { + initListener(true); + Settings.Global.putInt(mContext.getContentResolver(), Settings.Global.AIRPLANE_MODE_ON, 0); + mListener.onChange(false, mAirplaneModeUri); + verify(mClient).onAirplaneModeChanged(false); + assertThat(mListener.isAirplaneModeOn()).isFalse(); + } + +}