diff --git a/res/values/strings.xml b/res/values/strings.xml index 4176b866f0c..6eaa627e644 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -2340,6 +2340,17 @@ Learn more about Private DNS features + + Setting managed by carrier + + Activate Wi\u2011Fi Calling + + Turn on Wi\u2011Fi calling + + Wi\u2011Fi calling is not supported for %1$s + + Carrier Display diff --git a/src/com/android/settings/slices/SettingsSliceProvider.java b/src/com/android/settings/slices/SettingsSliceProvider.java index af165daf124..8f07447cd80 100644 --- a/src/com/android/settings/slices/SettingsSliceProvider.java +++ b/src/com/android/settings/slices/SettingsSliceProvider.java @@ -18,6 +18,8 @@ package com.android.settings.slices; import static android.Manifest.permission.READ_SEARCH_INDEXABLES; +import static com.android.settings.wifi.calling.WifiCallingSliceHelper.PATH_WIFI_CALLING; + import android.app.PendingIntent; import android.app.slice.SliceManager; import android.content.ContentResolver; @@ -34,7 +36,13 @@ import android.text.TextUtils; import android.util.Log; import android.util.Pair; +import androidx.slice.Slice; +import androidx.slice.SliceProvider; +import androidx.slice.builders.ListBuilder; +import androidx.slice.builders.SliceAction; + import com.android.settings.R; +import com.android.settings.overlay.FeatureFactory; import com.android.settingslib.utils.ThreadUtils; import java.net.URISyntaxException; @@ -45,11 +53,6 @@ import java.util.Map; import java.util.WeakHashMap; import java.util.concurrent.ConcurrentHashMap; -import androidx.slice.Slice; -import androidx.slice.SliceProvider; -import androidx.slice.builders.ListBuilder; -import androidx.slice.builders.SliceAction; - /** * A {@link SliceProvider} for Settings to enabled inline results in system apps. * @@ -160,6 +163,11 @@ public class SettingsSliceProvider extends SliceProvider { switch (path) { case "/" + PATH_WIFI: return createWifiSlice(sliceUri); + case "/" + PATH_WIFI_CALLING: + return FeatureFactory.getFactory(getContext()) + .getSlicesFeatureProvider() + .getNewWifiCallingSliceHelper(getContext()) + .createWifiCallingSlice(sliceUri); } SliceData cachedSliceData = mSliceWeakDataCache.get(sliceUri); diff --git a/src/com/android/settings/slices/SliceBroadcastReceiver.java b/src/com/android/settings/slices/SliceBroadcastReceiver.java index 47a7f5a45bd..04097340ed3 100644 --- a/src/com/android/settings/slices/SliceBroadcastReceiver.java +++ b/src/com/android/settings/slices/SliceBroadcastReceiver.java @@ -21,6 +21,7 @@ import static com.android.settings.slices.SettingsSliceProvider.ACTION_TOGGLE_CH import static com.android.settings.slices.SettingsSliceProvider.ACTION_WIFI_CHANGED; import static com.android.settings.slices.SettingsSliceProvider.EXTRA_SLICE_KEY; import static com.android.settings.slices.SettingsSliceProvider.EXTRA_SLICE_PLATFORM_DEFINED; +import static com.android.settings.wifi.calling.WifiCallingSliceHelper.ACTION_WIFI_CALLING_CHANGED; import android.app.slice.Slice; import android.content.BroadcastReceiver; @@ -79,6 +80,12 @@ public class SliceBroadcastReceiver extends BroadcastReceiver { context.getContentResolver().notifyChange(uri, null); }, 1000); break; + case ACTION_WIFI_CALLING_CHANGED: + FeatureFactory.getFactory(context) + .getSlicesFeatureProvider() + .getNewWifiCallingSliceHelper(context) + .handleWifiCallingChanged(intent); + break; } } diff --git a/src/com/android/settings/slices/SlicesFeatureProvider.java b/src/com/android/settings/slices/SlicesFeatureProvider.java index e5bba617e48..8dd6547b398 100644 --- a/src/com/android/settings/slices/SlicesFeatureProvider.java +++ b/src/com/android/settings/slices/SlicesFeatureProvider.java @@ -2,6 +2,8 @@ package com.android.settings.slices; import android.content.Context; +import com.android.settings.wifi.calling.WifiCallingSliceHelper; + /** * Manages Slices in Settings. */ @@ -24,4 +26,9 @@ public interface SlicesFeatureProvider { * If the data is already indexed, the data will not change. */ void indexSliceData(Context context); -} \ No newline at end of file + + /** + * Gets new WifiCallingSliceHelper object + */ + WifiCallingSliceHelper getNewWifiCallingSliceHelper(Context context); +} diff --git a/src/com/android/settings/slices/SlicesFeatureProviderImpl.java b/src/com/android/settings/slices/SlicesFeatureProviderImpl.java index 8e5bc067150..16684bfb022 100644 --- a/src/com/android/settings/slices/SlicesFeatureProviderImpl.java +++ b/src/com/android/settings/slices/SlicesFeatureProviderImpl.java @@ -2,6 +2,7 @@ package com.android.settings.slices; import android.content.Context; +import com.android.settings.wifi.calling.WifiCallingSliceHelper; import com.android.settingslib.utils.ThreadUtils; /** @@ -39,4 +40,9 @@ public class SlicesFeatureProviderImpl implements SlicesFeatureProvider { SlicesIndexer indexer = getSliceIndexer(context); indexer.indexSliceData(); } -} \ No newline at end of file + + @Override + public WifiCallingSliceHelper getNewWifiCallingSliceHelper(Context context) { + return new WifiCallingSliceHelper(context); + } +} diff --git a/src/com/android/settings/wifi/calling/WifiCallingSliceHelper.java b/src/com/android/settings/wifi/calling/WifiCallingSliceHelper.java new file mode 100644 index 00000000000..7213148e97f --- /dev/null +++ b/src/com/android/settings/wifi/calling/WifiCallingSliceHelper.java @@ -0,0 +1,363 @@ +/* + * 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.wifi.calling; + +import static android.app.slice.Slice.EXTRA_TOGGLE_STATE; + +import android.app.PendingIntent; +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.net.Uri; +import android.os.PersistableBundle; +import android.support.v4.graphics.drawable.IconCompat; +import android.telephony.CarrierConfigManager; +import android.telephony.SubscriptionManager; +import android.telephony.TelephonyManager; +import android.text.TextUtils; +import android.util.Log; + +import androidx.slice.Slice; +import androidx.slice.builders.ListBuilder; +import androidx.slice.builders.SliceAction; + +import com.android.ims.ImsManager; +import com.android.internal.annotations.VisibleForTesting; +import com.android.settings.R; +import com.android.settings.slices.SliceBroadcastReceiver; +import com.android.settings.slices.SliceBuilderUtils; + +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.FutureTask; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + + +/** + * Helper class to control slices for wifi calling settings. + */ +public class WifiCallingSliceHelper { + + private static final String TAG = "WifiCallingSliceHelper"; + + /** + * Settings slice path to wifi calling setting. + */ + public static final String PATH_WIFI_CALLING = "wifi_calling"; + + /** + * Action passed for changes to wifi calling slice (toggle). + */ + public static final String ACTION_WIFI_CALLING_CHANGED = + "com.android.settings.wifi.calling.action.WIFI_CALLING_CHANGED"; + + /** + * Action for Wifi calling Settings activity which + * allows setting configuration for Wifi calling + * related settings + */ + public static final String ACTION_WIFI_CALLING_SETTINGS_ACTIVITY = + "android.settings.WIFI_CALLING_SETTINGS"; + + /** + * Timeout for querying wifi calling setting from ims manager. + */ + private static final int TIMEOUT_MILLIS = 2000; + + /** + * Time for which data contained in the slice can remain fresh. + */ + private static final int SLICE_TTL_MILLIS = 60000; + + protected SubscriptionManager mSubscriptionManager; + private final Context mContext; + + @VisibleForTesting + public WifiCallingSliceHelper(Context context) { + mContext = context; + } + + /** + * Returns Slice object for wifi calling settings. + * + * If wifi calling is being turned on and if wifi calling activation is needed for the current + * carrier, this method will return Slice with instructions to go to Settings App. + * + * If wifi calling is not supported for the current carrier, this method will return slice with + * not supported message. + * + * If wifi calling setting can be changed, this method will return the slice to toggle wifi + * calling option with ACTION_WIFI_CALLING_CHANGED as endItem. + */ + public Slice createWifiCallingSlice(Uri sliceUri) { + final int subId = getDefaultVoiceSubId(); + final String carrierName = getSimCarrierName(); + + if (subId <= SubscriptionManager.INVALID_SUBSCRIPTION_ID) { + Log.d(TAG, "Invalid subscription Id"); + return getNonActionableWifiCallingSlice( + mContext.getString(R.string.wifi_calling_settings_title), + mContext.getString(R.string.wifi_calling_not_supported, carrierName), + sliceUri, SliceBuilderUtils.getSettingsIntent(mContext)); + } + + final ImsManager imsManager = getImsManager(subId); + + if (!imsManager.isWfcEnabledByPlatform() + || !imsManager.isWfcProvisionedOnDevice()) { + Log.d(TAG, "Wifi calling is either not provisioned or not enabled by Platform"); + return getNonActionableWifiCallingSlice( + mContext.getString(R.string.wifi_calling_settings_title), + mContext.getString(R.string.wifi_calling_not_supported, carrierName), + sliceUri, SliceBuilderUtils.getSettingsIntent(mContext)); + } + + try { + final boolean isWifiCallingEnabled = isWifiCallingEnabled(imsManager); + final Intent activationAppIntent = + getWifiCallingCarrierActivityIntent(subId); + + // Send this actionable wifi calling slice to toggle the setting + // only when there is no need for wifi calling activation with the server + if (activationAppIntent != null && !isWifiCallingEnabled) { + Log.d(TAG, "Needs Activation"); + // Activation needed for the next action of the user + // Give instructions to go to settings app + return getNonActionableWifiCallingSlice( + mContext.getString(R.string.wifi_calling_settings_title), + mContext.getString( + R.string.wifi_calling_settings_activation_instructions), + sliceUri, getActivityIntent(ACTION_WIFI_CALLING_SETTINGS_ACTIVITY)); + } + return getWifiCallingSlice(sliceUri, mContext, isWifiCallingEnabled); + } catch (InterruptedException | TimeoutException | ExecutionException e) { + Log.e(TAG, "Unable to read the current WiFi calling status", e); + return getNonActionableWifiCallingSlice( + mContext.getString(R.string.wifi_calling_settings_title), + mContext.getString(R.string.wifi_calling_turn_on), + sliceUri, getActivityIntent(ACTION_WIFI_CALLING_SETTINGS_ACTIVITY)); + } + } + + private boolean isWifiCallingEnabled(ImsManager imsManager) + throws InterruptedException, ExecutionException, TimeoutException { + final FutureTask isWifiOnTask = new FutureTask<>(new Callable() { + @Override + public Boolean call() { + return imsManager.isWfcEnabledByUser(); + } + }); + final ExecutorService executor = Executors.newSingleThreadExecutor(); + executor.execute(isWifiOnTask); + + Boolean isWifiEnabledByUser = false; + isWifiEnabledByUser = isWifiOnTask.get(TIMEOUT_MILLIS, TimeUnit.MILLISECONDS); + + return isWifiEnabledByUser && imsManager.isNonTtyOrTtyOnVolteEnabled(); + } + + /** + * Builds a toggle slice where the intent takes you to the wifi calling page and the toggle + * enables/disables wifi calling. + */ + private Slice getWifiCallingSlice(Uri sliceUri, Context mContext, + boolean isWifiCallingEnabled) { + + final IconCompat icon = IconCompat.createWithResource(mContext, R.drawable.wifi_signal); + final String title = mContext.getString(R.string.wifi_calling_settings_title); + return new ListBuilder(mContext, sliceUri, SLICE_TTL_MILLIS) + .setColor(R.color.material_blue_500) + .addRow(b -> b + .setTitle(title) + .addEndItem( + new SliceAction( + getBroadcastIntent(ACTION_WIFI_CALLING_CHANGED), + null /* actionTitle */, isWifiCallingEnabled)) + .setPrimaryAction(new SliceAction( + getActivityIntent(ACTION_WIFI_CALLING_SETTINGS_ACTIVITY), + icon, + title))) + .build(); + } + + protected ImsManager getImsManager(int subId) { + return ImsManager.getInstance(mContext, SubscriptionManager.getPhoneId(subId)); + } + + private Integer getWfcMode(ImsManager imsManager) + throws InterruptedException, ExecutionException, TimeoutException { + FutureTask wfcModeTask = new FutureTask<>(new Callable() { + @Override + public Integer call() { + return imsManager.getWfcMode(false); + } + }); + ExecutorService executor = Executors.newSingleThreadExecutor(); + executor.execute(wfcModeTask); + return wfcModeTask.get(TIMEOUT_MILLIS, TimeUnit.MILLISECONDS); + } + + /** + * Handles wifi calling setting change from wifi calling slice and posts notification. Should be + * called when intent action is ACTION_WIFI_CALLING_CHANGED. Executed in @WorkerThread + * + * @param intent action performed + */ + public void handleWifiCallingChanged(Intent intent) { + final int subId = getDefaultVoiceSubId(); + + if (subId > SubscriptionManager.INVALID_SUBSCRIPTION_ID) { + final ImsManager imsManager = getImsManager(subId); + if (imsManager.isWfcEnabledByPlatform() + || imsManager.isWfcProvisionedOnDevice()) { + final boolean currentValue = imsManager.isWfcEnabledByUser() + && imsManager.isNonTtyOrTtyOnVolteEnabled(); + final boolean newValue = intent.getBooleanExtra(EXTRA_TOGGLE_STATE, + currentValue); + final Intent activationAppIntent = + getWifiCallingCarrierActivityIntent(subId); + if (!newValue || activationAppIntent == null) { + // If either the action is to turn off wifi calling setting + // or there is no activation involved - Update the setting + if (newValue != currentValue) { + imsManager.setWfcSetting(newValue); + } + } + } + } + // notify change in slice in any case to get re-queried. This would result in displaying + // appropriate message with the updated setting. + final Uri uri = SliceBuilderUtils.getUri(PATH_WIFI_CALLING, false /*isPlatformSlice*/); + mContext.getContentResolver().notifyChange(uri, null); + } + + /** + * Returns Slice with the title and subtitle provided as arguments with wifi signal Icon. + * + * @param title Title of the slice + * @param subtitle Subtitle of the slice + * @param sliceUri slice uri + * @return Slice with title and subtitle + */ + // TODO(b/79548264) asses different scenarios and return null instead of non-actionable slice + private Slice getNonActionableWifiCallingSlice(String title, String subtitle, Uri sliceUri, + PendingIntent primaryActionIntent) { + final IconCompat icon = IconCompat.createWithResource(mContext, R.drawable.wifi_signal); + return new ListBuilder(mContext, sliceUri, SLICE_TTL_MILLIS) + .setColor(R.color.material_blue_500) + .addRow(b -> b + .setTitle(title) + .setSubtitle(subtitle) + .setPrimaryAction(new SliceAction( + primaryActionIntent, icon, + title))) + .build(); + } + + /** + * Returns {@code true} when the key is enabled for the carrier, and {@code false} otherwise. + */ + private boolean isCarrierConfigManagerKeyEnabled(Context mContext, String key, + int subId, boolean defaultValue) { + final CarrierConfigManager configManager = getCarrierConfigManager(mContext); + boolean ret = false; + if (configManager != null) { + final PersistableBundle bundle = configManager.getConfigForSubId(subId); + if (bundle != null) { + ret = bundle.getBoolean(key, defaultValue); + } + } + return ret; + } + + protected CarrierConfigManager getCarrierConfigManager(Context mContext) { + return mContext.getSystemService(CarrierConfigManager.class); + } + + /** + * Returns the current default voice subId obtained from SubscriptionManager + */ + protected int getDefaultVoiceSubId() { + if (mSubscriptionManager == null) { + mSubscriptionManager = mContext.getSystemService(SubscriptionManager.class); + } + return SubscriptionManager.getDefaultVoiceSubscriptionId(); + } + + /** + * Returns Intent of the activation app required to activate wifi calling or null if there is no + * need for activation. + */ + protected Intent getWifiCallingCarrierActivityIntent(int subId) { + final CarrierConfigManager configManager = getCarrierConfigManager(mContext); + if (configManager == null) { + return null; + } + + final PersistableBundle bundle = configManager.getConfigForSubId(subId); + if (bundle == null) { + return null; + } + + final String carrierApp = bundle.getString( + CarrierConfigManager.KEY_WFC_EMERGENCY_ADDRESS_CARRIER_APP_STRING); + if (TextUtils.isEmpty(carrierApp)) { + return null; + } + + final ComponentName componentName = ComponentName.unflattenFromString(carrierApp); + if (componentName == null) { + return null; + } + + final Intent intent = new Intent(); + intent.setComponent(componentName); + return intent; + } + + private PendingIntent getBroadcastIntent(String action) { + final Intent intent = new Intent(action); + intent.setClass(mContext, SliceBroadcastReceiver.class); + return PendingIntent.getBroadcast(mContext, 0 /* requestCode */, intent, + PendingIntent.FLAG_CANCEL_CURRENT); + } + + /** + * Returns PendingIntent to start activity specified by action + */ + private PendingIntent getActivityIntent(String action) { + final Intent intent = new Intent(action); + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + return PendingIntent.getActivity(mContext, 0 /* requestCode */, intent, 0 /* flags */); + } + + /** + * Returns carrier id name of the current Subscription + */ + private String getSimCarrierName() { + final TelephonyManager telephonyManager = mContext.getSystemService(TelephonyManager.class); + final CharSequence carrierName = telephonyManager.getSimCarrierIdName(); + if (carrierName == null) { + return mContext.getString(R.string.carrier); + } + return carrierName.toString(); + } + +} diff --git a/tests/robotests/src/com/android/settings/wifi/calling/WifiCallingSliceHelperTest.java b/tests/robotests/src/com/android/settings/wifi/calling/WifiCallingSliceHelperTest.java new file mode 100644 index 00000000000..ac3ff3ff635 --- /dev/null +++ b/tests/robotests/src/com/android/settings/wifi/calling/WifiCallingSliceHelperTest.java @@ -0,0 +1,316 @@ +/* + * 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.wifi.calling; + +import static android.app.slice.Slice.EXTRA_TOGGLE_STATE; +import static android.app.slice.Slice.HINT_TITLE; +import static android.app.slice.SliceItem.FORMAT_TEXT; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.app.PendingIntent; +import android.content.Context; +import android.content.Intent; +import android.content.res.Resources; +import android.net.Uri; +import android.telephony.CarrierConfigManager; + +import androidx.slice.Slice; +import androidx.slice.SliceItem; +import androidx.slice.SliceMetadata; +import androidx.slice.SliceProvider; +import androidx.slice.core.SliceAction; +import androidx.slice.core.SliceQuery; +import androidx.slice.widget.SliceLiveData; + +import com.android.ims.ImsManager; +import com.android.settings.R; +import com.android.settings.slices.SettingsSliceProvider; +import com.android.settings.slices.SliceBroadcastReceiver; +import com.android.settings.slices.SliceBuilderUtils; +import com.android.settings.slices.SliceData; +import com.android.settings.slices.SlicesFeatureProvider; +import com.android.settings.testutils.FakeFeatureFactory; +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; + +import java.util.List; + +@RunWith(SettingsRobolectricTestRunner.class) +public class WifiCallingSliceHelperTest { + + private Context mContext; + @Mock + private CarrierConfigManager mMockCarrierConfigManager; + + @Mock + private ImsManager mMockImsManager; + + private final Uri mWfcURI = Uri.parse("content://com.android.settings.slices/wifi_calling"); + + private FakeWifiCallingSliceHelper mWfcSliceHelper; + private SettingsSliceProvider mProvider; + private SliceBroadcastReceiver mReceiver; + private FakeFeatureFactory mFeatureFactory; + private SlicesFeatureProvider mSlicesFeatureProvider; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + mContext = spy(RuntimeEnvironment.application); + + //setup for SettingsSliceProvider tests + mProvider = spy(new SettingsSliceProvider()); + doReturn(mContext).when(mProvider).getContext(); + + //setup for SliceBroadcastReceiver test + mReceiver = spy(new SliceBroadcastReceiver()); + + mFeatureFactory = FakeFeatureFactory.setupForTest(); + mSlicesFeatureProvider = mFeatureFactory.getSlicesFeatureProvider(); + + // Prevent crash in SliceMetadata. + Resources resources = spy(mContext.getResources()); + doReturn(60).when(resources).getDimensionPixelSize(anyInt()); + doReturn(resources).when(mContext).getResources(); + + mWfcSliceHelper = new FakeWifiCallingSliceHelper(mContext); + + // Set-up specs for SliceMetadata. + SliceProvider.setSpecs(SliceLiveData.SUPPORTED_SPECS); + } + + @Test + public void test_CreateWifiCallingSlice_invalidSubId() { + mWfcSliceHelper.setDefaultVoiceSubId(-1); + + final Slice slice = mWfcSliceHelper.createWifiCallingSlice(mWfcURI); + + testWifiCallingSettingsUnavailableSlice(slice, null, + SliceBuilderUtils.getSettingsIntent(mContext)); + } + + @Test + public void test_CreateWifiCallingSlice_wfcNotSupported() { + doReturn(false).when(mMockImsManager).isWfcEnabledByPlatform(); + + final Slice slice = mWfcSliceHelper.createWifiCallingSlice(mWfcURI); + + assertThat(mWfcSliceHelper.getDefaultVoiceSubId()).isEqualTo(1); + testWifiCallingSettingsUnavailableSlice(slice, null, + SliceBuilderUtils.getSettingsIntent(mContext)); + } + + @Test + public void test_CreateWifiCallingSlice_needsActivation() { + /* In cases where activation is needed and the user action + would be turning on the wifi calling (i.e. if wifi calling is + turned off) we need to guide the user to wifi calling settings + activity so the user can perform the activation there.(PrimaryAction) + */ + doReturn(true).when(mMockImsManager).isWfcEnabledByPlatform(); + doReturn(true).when(mMockImsManager).isWfcProvisionedOnDevice(); + doReturn(false).when(mMockImsManager).isWfcEnabledByUser(); + doReturn(false).when(mMockImsManager).isNonTtyOrTtyOnVolteEnabled(); + doReturn(null).when(mMockCarrierConfigManager).getConfigForSubId(1); + mWfcSliceHelper.setActivationAppIntent(new Intent()); // dummy Intent + + final Slice slice = mWfcSliceHelper.createWifiCallingSlice(mWfcURI); + + assertThat(mWfcSliceHelper.getDefaultVoiceSubId()).isEqualTo(1); + testWifiCallingSettingsUnavailableSlice(slice, null, + getActivityIntent(WifiCallingSliceHelper.ACTION_WIFI_CALLING_SETTINGS_ACTIVITY)); + } + + @Test + public void test_CreateWifiCallingSlice_success() { + doReturn(true).when(mMockImsManager).isWfcEnabledByPlatform(); + doReturn(true).when(mMockImsManager).isWfcProvisionedOnDevice(); + doReturn(true).when(mMockImsManager).isWfcEnabledByUser(); + doReturn(true).when(mMockImsManager).isNonTtyOrTtyOnVolteEnabled(); + doReturn(null).when(mMockCarrierConfigManager).getConfigForSubId(1); + + final Slice slice = mWfcSliceHelper.createWifiCallingSlice(mWfcURI); + + assertThat(mWfcSliceHelper.getDefaultVoiceSubId()).isEqualTo(1); + testWifiCallingSettingsToggleSlice(slice, null); + } + + @Test + public void test_SettingSliceProvider_getsRightSliceWifiCalling() { + doReturn(true).when(mMockImsManager).isWfcEnabledByPlatform(); + doReturn(true).when(mMockImsManager).isWfcProvisionedOnDevice(); + doReturn(true).when(mMockImsManager).isWfcEnabledByUser(); + doReturn(true).when(mMockImsManager).isNonTtyOrTtyOnVolteEnabled(); + doReturn(null).when(mMockCarrierConfigManager).getConfigForSubId(1); + doReturn(mWfcSliceHelper).when(mSlicesFeatureProvider) + .getNewWifiCallingSliceHelper(mContext); + + final Slice slice = mProvider.onBindSlice(mWfcURI); + + assertThat(mWfcSliceHelper.getDefaultVoiceSubId()).isEqualTo(1); + testWifiCallingSettingsToggleSlice(slice, null); + } + + @Test + public void test_SliceBroadcastReceiver_toggleOffWifiCalling() { + doReturn(true).when(mMockImsManager).isWfcEnabledByPlatform(); + doReturn(true).when(mMockImsManager).isWfcProvisionedOnDevice(); + doReturn(false).when(mMockImsManager).isWfcEnabledByUser(); + doReturn(true).when(mMockImsManager).isNonTtyOrTtyOnVolteEnabled(); + doReturn(mWfcSliceHelper).when(mSlicesFeatureProvider) + .getNewWifiCallingSliceHelper(mContext); + mWfcSliceHelper.setActivationAppIntent(null); + + ArgumentCaptor mWfcSettingCaptor = ArgumentCaptor.forClass(Boolean.class); + + // turn on Wifi calling setting + Intent intent = new Intent(WifiCallingSliceHelper.ACTION_WIFI_CALLING_CHANGED); + intent.putExtra(EXTRA_TOGGLE_STATE, true); + + // change the setting + mReceiver.onReceive(mContext, intent); + + verify((mMockImsManager)).setWfcSetting(mWfcSettingCaptor.capture()); + + // assert the change + assertThat(mWfcSettingCaptor.getValue()).isTrue(); + } + + private void testWifiCallingSettingsUnavailableSlice(Slice slice, + SliceData sliceData, PendingIntent expectedPrimaryAction) { + final SliceMetadata metadata = SliceMetadata.from(mContext, slice); + + //Check there is no toggle action + final List toggles = metadata.getToggles(); + assertThat(toggles).isEmpty(); + + // Check whether the primary action is to open wifi calling settings activity + final PendingIntent primaryPendingIntent = + metadata.getPrimaryAction().getAction(); + assertThat(primaryPendingIntent).isEqualTo(expectedPrimaryAction); + + // Check the title + final List sliceItems = slice.getItems(); + assertTitle(sliceItems, mContext.getString(R.string.wifi_calling_settings_title)); + } + + private void testWifiCallingSettingsToggleSlice(Slice slice, + SliceData sliceData) { + final SliceMetadata metadata = SliceMetadata.from(mContext, slice); + + final List toggles = metadata.getToggles(); + assertThat(toggles).hasSize(1); + + final SliceAction mainToggleAction = toggles.get(0); + + // Check intent in Toggle Action + final PendingIntent togglePendingIntent = mainToggleAction.getAction(); + final PendingIntent expectedToggleIntent = getBroadcastIntent( + WifiCallingSliceHelper.ACTION_WIFI_CALLING_CHANGED); + assertThat(togglePendingIntent).isEqualTo(expectedToggleIntent); + + // Check primary intent + final PendingIntent primaryPendingIntent = metadata.getPrimaryAction().getAction(); + final PendingIntent expectedPendingIntent = + getActivityIntent(WifiCallingSliceHelper.ACTION_WIFI_CALLING_SETTINGS_ACTIVITY); + assertThat(primaryPendingIntent).isEqualTo(expectedPendingIntent); + + // Check the title + final List sliceItems = slice.getItems(); + assertTitle(sliceItems, mContext.getString(R.string.wifi_calling_settings_title)); + } + + private PendingIntent getBroadcastIntent(String action) { + final Intent intent = new Intent(action); + intent.setClass(mContext, SliceBroadcastReceiver.class); + return PendingIntent.getBroadcast(mContext, 0 /* requestCode */, intent, + PendingIntent.FLAG_CANCEL_CURRENT); + } + + private PendingIntent getActivityIntent(String action) { + final Intent intent = new Intent(action); + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + return PendingIntent.getActivity(mContext, 0 /* requestCode */, intent, 0 /* flags */); + } + + private void assertTitle(List sliceItems, String title) { + boolean hasTitle = false; + for (SliceItem item : sliceItems) { + List titleItems = SliceQuery.findAll(item, FORMAT_TEXT, HINT_TITLE, + null /* non-hints */); + if (titleItems == null) { + continue; + } + + hasTitle = true; + for (SliceItem subTitleItem : titleItems) { + assertThat(subTitleItem.getText()).isEqualTo(title); + } + } + assertThat(hasTitle).isTrue(); + } + private class FakeWifiCallingSliceHelper extends WifiCallingSliceHelper { + int mSubId = 1; + + private Intent mActivationAppIntent; + FakeWifiCallingSliceHelper(Context context) { + super(context); + mActivationAppIntent = null; + } + + @Override + protected CarrierConfigManager getCarrierConfigManager(Context mContext) { + return mMockCarrierConfigManager; + } + + @Override + protected ImsManager getImsManager(int subId) { + return mMockImsManager; + } + + protected int getDefaultVoiceSubId() { + return mSubId; + } + + protected void setDefaultVoiceSubId(int id) { + mSubId = id; + } + + @Override + protected Intent getWifiCallingCarrierActivityIntent(int subId) { + return mActivationAppIntent; + } + + public void setActivationAppIntent(Intent intent) { + mActivationAppIntent = intent; + } + } +}