Merge "Fix null pointer in SubscriptionUtil" into qt-dev
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index b7fd299..9dd4732 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -102,13 +102,15 @@
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.MEDIA_CONTENT_CONTROL" />
+ <protected-broadcast android:name="com.android.settings.DELETE_SIM_PROFILE_RESULT"/>
+
<application android:label="@string/settings_label"
android:icon="@drawable/ic_launcher_settings"
android:theme="@style/Theme.Settings"
android:hardwareAccelerated="true"
android:requiredForAllUsers="true"
android:supportsRtl="true"
- android:allowBackup="false"
+ android:backupAgent="com.android.settings.backup.SettingsBackupHelper"
android:usesCleartextTraffic="true"
android:defaultToDeviceProtectedStorage="true"
android:directBootAware="true"
@@ -136,6 +138,7 @@
<intent-filter android:priority="1">
<action android:name="android.settings.NETWORK_OPERATOR_SETTINGS" />
<action android:name="android.settings.DATA_ROAMING_SETTINGS" />
+ <action android:name="android.settings.MMS_MESSAGE_SETTING" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
@@ -2622,6 +2625,7 @@
<receiver android:name=".sim.SimSelectNotification">
<intent-filter>
<action android:name="android.telephony.action.PRIMARY_SUBSCRIPTION_LIST_CHANGED"/>
+ <action android:name="android.settings.ENABLE_MMS_DATA_REQUEST"/>
</intent-filter>
</receiver>
diff --git a/res/drawable/ic_settings_gestures.xml b/res/drawable/ic_settings_gestures.xml
index 4fe0bf3..c75e7c3 100644
--- a/res/drawable/ic_settings_gestures.xml
+++ b/res/drawable/ic_settings_gestures.xml
@@ -20,9 +20,6 @@
android:viewportHeight="32.0"
android:tint="?android:attr/colorControlNormal">
<path
- android:fillColor="#FF000000"
- android:pathData="M20,20.2V24H6V5.3h8.7l2.4,-4H7.6c-2.2,0 -4,1.8 -4,4v21.3c0,2.2 1.8,4 4,4h10.7c2.2,0 4,-1.8 4,-4V14.9L20,20.2zM15.6,28h-5.3v-1.3h5.3V28z"/>
- <path
- android:fillColor="#FF000000"
- android:pathData="M28.5,7l1.1,-2.4L32,3.5l-2.4,-1.1L28.5,0l-1.1,2.4L25,3.5l2.4,1.1L28.5,7zM21,7.4l-2.2,-4.8l-2.2,4.8l-4.8,2.2l4.8,2.2l2.2,4.8l2.2,-4.8l4.8,-2.2L21,7.4zM28.5,12.2l-1.1,2.4L25,15.6l2.4,1.1l1.1,2.4l1.1,-2.4l2.4,-1.1l-2.4,-1.1L28.5,12.2z"/>
+ android:fillColor="@android:color/white"
+ android:pathData="M4.59 6.89c0.7-0.71 1.4-1.35 1.71-1.22 0.5 0.2 0 1.03-0.3 1.52-0.25 0.42 -2.86 3.89-2.86 6.31 0 1.28 0.48 2.34 1.34 2.98 0.75 0.56 1.74 0.73 2.64 0.46 1.07-0.31 1.95-1.4 3.06-2.77 1.21-1.49 2.83-3.44 4.08-3.44 1.63 0 1.65 1.01 1.76 1.79-3.78 0.64 -5.38 3.67-5.38 5.37 0 1.7 1.44 3.09 3.21 3.09 1.63 0 4.29-1.33 4.69-6.1H21v-2.5h-2.47c-0.15-1.65-1.09-4.2-4.03-4.2-2.25 0-4.18 1.91-4.94 2.84-0.58 0.73 -2.06 2.48-2.29 2.72-0.25 0.3 -0.68 0.84 -1.11 0.84 -0.45 0-0.72-0.83-0.36-1.92 0.35 -1.09 1.4-2.86 1.85-3.52 0.78 -1.14 1.3-1.92 1.3-3.28C8.95 3.69 7.31 3 6.44 3 5.12 3 3.97 4 3.72 4.25c-0.36 0.36 -0.66 0.66 -0.88 0.93 l1.75 1.71zm9.29 11.66c-0.31 0-0.74-0.26-0.74-0.72 0-0.6 0.73 -2.2 2.87-2.76-0.3 2.69-1.43 3.48-2.13 3.48z" />
</vector>
\ No newline at end of file
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 5609006..98892e4 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -7330,6 +7330,9 @@
<!-- Battery Saver: Search terms for battery saver schedule preference. Feel free to add additional terms when translating if appropriate [CHAR_LIMIT=NONE] -->
<string name="keywords_battery_saver_schedule">routine, schedule, battery saver, power saver, battery, automatic, percent</string>
+ <!-- List of synonyms for the Add an account setting [CHAR_LIMIT=NONE] -->
+ <string name="keywords_add_an_account">work profile</string>
+
<!-- Option title for the default sound, context based on screen -->
<string name="default_sound">Default sound</string>
diff --git a/res/xml/add_account_settings.xml b/res/xml/add_account_settings.xml
index 6b36f8d..01e7674 100644
--- a/res/xml/add_account_settings.xml
+++ b/res/xml/add_account_settings.xml
@@ -19,4 +19,5 @@
xmlns:settings="http://schemas.android.com/apk/res-auto"
android:key="add_account_screen"
android:title="@string/header_add_an_account"
- settings:controller="com.android.settings.accounts.ChooseAccountPreferenceController" />
+ settings:controller="com.android.settings.accounts.ChooseAccountPreferenceController"
+ settings:keywords="@string/keywords_add_an_account"/>
diff --git a/res/xml/mobile_network_settings_v2.xml b/res/xml/mobile_network_settings_v2.xml
index cc377bd..1ff8860 100644
--- a/res/xml/mobile_network_settings_v2.xml
+++ b/res/xml/mobile_network_settings_v2.xml
@@ -191,4 +191,10 @@
</PreferenceCategory>
+ <Preference
+ android:key="erase_sim"
+ android:persistent="false"
+ android:title="@string/mobile_network_erase_sim"
+ settings:controller="com.android.settings.network.telephony.DeleteSimProfilePreferenceController" />
+
</PreferenceScreen>
diff --git a/src/com/android/settings/backup/SettingsBackupHelper.java b/src/com/android/settings/backup/SettingsBackupHelper.java
new file mode 100644
index 0000000..92612b0
--- /dev/null
+++ b/src/com/android/settings/backup/SettingsBackupHelper.java
@@ -0,0 +1,89 @@
+/**
+ * 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.backup;
+
+import android.app.backup.BackupAgentHelper;
+import android.app.backup.BackupDataInputStream;
+import android.app.backup.BackupDataOutput;
+import android.app.backup.BackupHelper;
+import android.os.ParcelFileDescriptor;
+
+import com.android.settings.shortcut.CreateShortcutPreferenceController;
+
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+
+/**
+ * Backup agent for Settings APK
+ */
+public class SettingsBackupHelper extends BackupAgentHelper {
+
+ @Override
+ public void onCreate() {
+ super.onCreate();
+ addHelper("no-op", new NoOpHelper());
+ }
+
+ @Override
+ public void onRestoreFinished() {
+ super.onRestoreFinished();
+ CreateShortcutPreferenceController.updateRestoredShortcuts(this);
+ }
+
+ /**
+ * Backup helper which does not do anything. Having at least one helper ensures that the
+ * transport is not empty and onRestoreFinished is called eventually.
+ */
+ private static class NoOpHelper implements BackupHelper {
+
+ private final int VERSION_CODE = 1;
+
+ @Override
+ public void performBackup(ParcelFileDescriptor oldState, BackupDataOutput data,
+ ParcelFileDescriptor newState) {
+
+ try (FileOutputStream out = new FileOutputStream(newState.getFileDescriptor())) {
+ if (getVersionCode(oldState) != VERSION_CODE) {
+ data.writeEntityHeader("dummy", 1);
+ data.writeEntityData(new byte[1], 1);
+ }
+
+ // Write new version code
+ out.write(VERSION_CODE);
+ out.flush();
+ } catch (IOException e) { }
+ }
+
+ @Override
+ public void restoreEntity(BackupDataInputStream data) { }
+
+ @Override
+ public void writeNewStateDescription(ParcelFileDescriptor newState) { }
+
+ private int getVersionCode(ParcelFileDescriptor state) {
+ if (state == null) {
+ return 0;
+ }
+ try (FileInputStream in = new FileInputStream(state.getFileDescriptor())) {
+ return in.read();
+ } catch (IOException e) {
+ return 0;
+ }
+ }
+ }
+}
diff --git a/src/com/android/settings/bluetooth/BluetoothDeviceRenamePreferenceController.java b/src/com/android/settings/bluetooth/BluetoothDeviceRenamePreferenceController.java
index 1c75669..d42a1be 100644
--- a/src/com/android/settings/bluetooth/BluetoothDeviceRenamePreferenceController.java
+++ b/src/com/android/settings/bluetooth/BluetoothDeviceRenamePreferenceController.java
@@ -66,7 +66,7 @@
if (TextUtils.equals(getPreferenceKey(), preference.getKey()) && mFragment != null) {
mMetricsFeatureProvider.action(mContext,
SettingsEnums.ACTION_BLUETOOTH_RENAME);
- LocalDeviceNameDialogFragment.newInstance()
+ new LocalDeviceNameDialogFragment()
.show(mFragment.getFragmentManager(), LocalDeviceNameDialogFragment.TAG);
return true;
}
diff --git a/src/com/android/settings/bluetooth/LocalDeviceNameDialogFragment.java b/src/com/android/settings/bluetooth/LocalDeviceNameDialogFragment.java
index 5cf2ccc..c15dd04 100644
--- a/src/com/android/settings/bluetooth/LocalDeviceNameDialogFragment.java
+++ b/src/com/android/settings/bluetooth/LocalDeviceNameDialogFragment.java
@@ -31,10 +31,6 @@
public static final String TAG = "LocalAdapterName";
private BluetoothAdapter mBluetoothAdapter;
- public static LocalDeviceNameDialogFragment newInstance() {
- return new LocalDeviceNameDialogFragment();
- }
-
private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
diff --git a/src/com/android/settings/homepage/contextualcards/slices/BatteryFixSlice.java b/src/com/android/settings/homepage/contextualcards/slices/BatteryFixSlice.java
index 761755c..d1051fe 100644
--- a/src/com/android/settings/homepage/contextualcards/slices/BatteryFixSlice.java
+++ b/src/com/android/settings/homepage/contextualcards/slices/BatteryFixSlice.java
@@ -30,6 +30,7 @@
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.util.ArrayMap;
+import android.view.View;
import androidx.annotation.VisibleForTesting;
import androidx.annotation.WorkerThread;
@@ -112,9 +113,12 @@
continue;
}
final Drawable drawable = mContext.getDrawable(batteryTip.getIconId());
- drawable.setColorFilter(new PorterDuffColorFilter(
- mContext.getResources().getColor(batteryTip.getIconTintColorId()),
- PorterDuff.Mode.SRC_IN));
+ final int iconTintColorId = batteryTip.getIconTintColorId();
+ if (iconTintColorId != View.NO_ID) {
+ drawable.setColorFilter(new PorterDuffColorFilter(
+ mContext.getResources().getColor(iconTintColorId),
+ PorterDuff.Mode.SRC_IN));
+ }
final IconCompat icon = Utils.createIconWithDrawable(drawable);
final SliceAction primaryAction = SliceAction.createDeeplink(getPrimaryAction(),
diff --git a/src/com/android/settings/inputmethod/AvailableVirtualKeyboardFragment.java b/src/com/android/settings/inputmethod/AvailableVirtualKeyboardFragment.java
index c8b5b6d..c37d2b5 100644
--- a/src/com/android/settings/inputmethod/AvailableVirtualKeyboardFragment.java
+++ b/src/com/android/settings/inputmethod/AvailableVirtualKeyboardFragment.java
@@ -16,20 +16,11 @@
package com.android.settings.inputmethod;
-import android.annotation.DrawableRes;
-import android.annotation.NonNull;
-import android.annotation.Nullable;
import android.app.Activity;
import android.app.admin.DevicePolicyManager;
import android.app.settings.SettingsEnums;
import android.content.Context;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageManager;
-import android.content.pm.ServiceInfo;
import android.content.res.Configuration;
-import android.graphics.Color;
-import android.graphics.drawable.ColorDrawable;
-import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.provider.SearchIndexableResource;
import android.view.inputmethod.InputMethodInfo;
@@ -93,59 +84,12 @@
return SettingsEnums.ENABLE_VIRTUAL_KEYBOARDS;
}
- @Nullable
- private static Drawable loadDrawable(@NonNull final PackageManager packageManager,
- @NonNull final String packageName, @DrawableRes final int resId,
- @NonNull final ApplicationInfo applicationInfo) {
- if (resId == 0) {
- return null;
- }
- try {
- return packageManager.getDrawable(packageName, resId, applicationInfo);
- } catch (Exception e) {
- return null;
- }
- }
-
- @NonNull
- private static Drawable getInputMethodIcon(@NonNull final PackageManager packageManager,
- @NonNull final InputMethodInfo imi) {
- final ServiceInfo si = imi.getServiceInfo();
- final ApplicationInfo ai = si != null ? si.applicationInfo : null;
- final String packageName = imi.getPackageName();
- if (si == null || ai == null || packageName == null) {
- return new ColorDrawable(Color.TRANSPARENT);
- }
- // We do not use ServiceInfo#loadLogo() and ServiceInfo#loadIcon here since those methods
- // internally have some fallback rules, which we want to do manually.
- Drawable drawable = loadDrawable(packageManager, packageName, si.logo, ai);
- if (drawable != null) {
- return drawable;
- }
- drawable = loadDrawable(packageManager, packageName, si.icon, ai);
- if (drawable != null) {
- return drawable;
- }
- // We do not use ApplicationInfo#loadLogo() and ApplicationInfo#loadIcon here since those
- // methods internally have some fallback rules, which we want to do manually.
- drawable = loadDrawable(packageManager, packageName, ai.logo, ai);
- if (drawable != null) {
- return drawable;
- }
- drawable = loadDrawable(packageManager, packageName, ai.icon, ai);
- if (drawable != null) {
- return drawable;
- }
- return new ColorDrawable(Color.TRANSPARENT);
- }
-
private void updateInputMethodPreferenceViews() {
mInputMethodSettingValues.refreshAllInputMethodAndSubtypes();
// Clear existing "InputMethodPreference"s
mInputMethodPreferenceList.clear();
List<String> permittedList = mDpm.getPermittedInputMethodsForCurrentUser();
final Context context = getPrefContext();
- final PackageManager packageManager = getActivity().getPackageManager();
final List<InputMethodInfo> imis = mInputMethodSettingValues.getInputMethodList();
final int numImis = (imis == null ? 0 : imis.size());
for (int i = 0; i < numImis; ++i) {
@@ -154,7 +98,7 @@
|| permittedList.contains(imi.getPackageName());
final InputMethodPreference pref = new InputMethodPreference(
context, imi, true, isAllowedByOrganization, this);
- pref.setIcon(getInputMethodIcon(packageManager, imi));
+ pref.setIcon(imi.loadIcon(context.getPackageManager()));
mInputMethodPreferenceList.add(pref);
}
final Collator collator = Collator.getInstance();
diff --git a/src/com/android/settings/inputmethod/VirtualKeyboardFragment.java b/src/com/android/settings/inputmethod/VirtualKeyboardFragment.java
index cb7831e..ef07d11 100644
--- a/src/com/android/settings/inputmethod/VirtualKeyboardFragment.java
+++ b/src/com/android/settings/inputmethod/VirtualKeyboardFragment.java
@@ -20,8 +20,6 @@
import android.app.admin.DevicePolicyManager;
import android.app.settings.SettingsEnums;
import android.content.Context;
-import android.graphics.Color;
-import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.provider.SearchIndexableResource;
@@ -48,7 +46,6 @@
public final class VirtualKeyboardFragment extends SettingsPreferenceFragment implements Indexable {
private static final String ADD_VIRTUAL_KEYBOARD_SCREEN = "add_virtual_keyboard_screen";
- private static final Drawable NO_ICON = new ColorDrawable(Color.TRANSPARENT);
private final ArrayList<InputMethodPreference> mInputMethodPreferenceList = new ArrayList<>();
private InputMethodManager mImm;
@@ -89,14 +86,7 @@
final InputMethodInfo imi = imis.get(i);
final boolean isAllowedByOrganization = permittedList == null
|| permittedList.contains(imi.getPackageName());
- Drawable icon;
- try {
- // TODO: Consider other ways to retrieve an icon to show here.
- icon = getActivity().getPackageManager().getApplicationIcon(imi.getPackageName());
- } catch (Exception e) {
- // TODO: Consider handling the error differently perhaps by showing default icons.
- icon = NO_ICON;
- }
+ final Drawable icon = imi.loadIcon(context.getPackageManager());
final InputMethodPreference pref = new InputMethodPreference(
context,
imi,
diff --git a/src/com/android/settings/network/telephony/DeleteSimProfileConfirmationDialog.java b/src/com/android/settings/network/telephony/DeleteSimProfileConfirmationDialog.java
new file mode 100644
index 0000000..52c5cc1
--- /dev/null
+++ b/src/com/android/settings/network/telephony/DeleteSimProfileConfirmationDialog.java
@@ -0,0 +1,83 @@
+/*
+ * 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 android.app.Dialog;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.os.Bundle;
+import android.telephony.SubscriptionInfo;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.annotation.VisibleForTesting;
+import androidx.appcompat.app.AlertDialog;
+
+import com.android.settings.R;
+import com.android.settings.core.instrumentation.InstrumentedDialogFragment;
+
+public class DeleteSimProfileConfirmationDialog extends InstrumentedDialogFragment implements
+ DialogInterface.OnClickListener {
+ public static final String TAG = "confirm_delete_sim";
+ public static final String KEY_SUBSCRIPTION_INFO = "subscription_info";
+ private SubscriptionInfo mInfo;
+
+ public static DeleteSimProfileConfirmationDialog newInstance(SubscriptionInfo info) {
+ final DeleteSimProfileConfirmationDialog dialog =
+ new DeleteSimProfileConfirmationDialog();
+ final Bundle args = new Bundle();
+ args.putParcelable(KEY_SUBSCRIPTION_INFO, info);
+ dialog.setArguments(args);
+ return dialog;
+ }
+
+ @NonNull
+ @Override
+ public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) {
+ mInfo = getArguments().getParcelable(KEY_SUBSCRIPTION_INFO);
+ Context context = getContext();
+ final String message = context.getString(R.string.mobile_network_erase_sim_dialog_body,
+ mInfo.getCarrierName(), mInfo.getCarrierName());
+ return new AlertDialog.Builder(context)
+ .setTitle(R.string.mobile_network_erase_sim_dialog_title)
+ .setMessage(message)
+ .setNegativeButton(R.string.cancel, null)
+ .setPositiveButton(R.string.mobile_network_erase_sim_dialog_ok, this)
+ .create();
+ }
+
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ if (which == DialogInterface.BUTTON_POSITIVE) {
+ beginDeletionWithProgress();
+ }
+ }
+
+ @VisibleForTesting
+ void beginDeletionWithProgress() {
+ final DeleteSimProfileProgressDialog progress =
+ DeleteSimProfileProgressDialog.newInstance(mInfo.getSubscriptionId());
+ progress.setTargetFragment(getTargetFragment(), 0);
+ progress.show(getFragmentManager(), DeleteSimProfileProgressDialog.TAG);
+ }
+
+ @Override
+ public int getMetricsCategory() {
+ // TODO(b/131519375) - use a real id here once it's been created in the metrics proto
+ return 0;
+ }
+}
diff --git a/src/com/android/settings/network/telephony/DeleteSimProfilePreferenceController.java b/src/com/android/settings/network/telephony/DeleteSimProfilePreferenceController.java
new file mode 100644
index 0000000..22ff2b6
--- /dev/null
+++ b/src/com/android/settings/network/telephony/DeleteSimProfilePreferenceController.java
@@ -0,0 +1,74 @@
+/*
+ * 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 android.content.Context;
+import android.telephony.SubscriptionInfo;
+
+import androidx.fragment.app.Fragment;
+import androidx.preference.Preference;
+import androidx.preference.PreferenceScreen;
+
+import com.android.settings.core.BasePreferenceController;
+import com.android.settings.network.SubscriptionUtil;
+
+/** This controls a preference allowing the user to delete the profile for an eSIM. */
+public class DeleteSimProfilePreferenceController extends BasePreferenceController {
+
+ private SubscriptionInfo mSubscriptionInfo;
+ private Fragment mParentFragment;
+
+ public DeleteSimProfilePreferenceController(Context context, String preferenceKey) {
+ super(context, preferenceKey);
+ }
+
+ public void init(int subscriptionId, Fragment parentFragment) {
+ mParentFragment = parentFragment;
+
+ for (SubscriptionInfo info : SubscriptionUtil.getAvailableSubscriptions(
+ mContext)) {
+ if (info.getSubscriptionId() == subscriptionId && info.isEmbedded()) {
+ mSubscriptionInfo = info;
+ break;
+ }
+ }
+ }
+
+ @Override
+ public void displayPreference(PreferenceScreen screen) {
+ super.displayPreference(screen);
+ final Preference pref = screen.findPreference(getPreferenceKey());
+ pref.setOnPreferenceClickListener(p -> {
+ final DeleteSimProfileConfirmationDialog dialogFragment =
+ DeleteSimProfileConfirmationDialog.newInstance(mSubscriptionInfo);
+ dialogFragment.setTargetFragment(mParentFragment, 0);
+ dialogFragment.show(mParentFragment.getFragmentManager(),
+ DeleteSimProfileConfirmationDialog.TAG);
+ return true;
+ });
+ }
+
+ @Override
+ public int getAvailabilityStatus() {
+ if (mSubscriptionInfo != null) {
+ return AVAILABLE;
+ } else {
+ return CONDITIONALLY_UNAVAILABLE;
+ }
+ }
+
+}
diff --git a/src/com/android/settings/network/telephony/DeleteSimProfileProgressDialog.java b/src/com/android/settings/network/telephony/DeleteSimProfileProgressDialog.java
new file mode 100644
index 0000000..15f4b22
--- /dev/null
+++ b/src/com/android/settings/network/telephony/DeleteSimProfileProgressDialog.java
@@ -0,0 +1,120 @@
+/*
+ * 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 android.app.Activity;
+import android.app.Dialog;
+import android.app.PendingIntent;
+import android.app.ProgressDialog;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.Bundle;
+import android.telephony.euicc.EuiccManager;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.annotation.VisibleForTesting;
+
+import com.android.settings.R;
+import com.android.settings.core.instrumentation.InstrumentedDialogFragment;
+
+public class DeleteSimProfileProgressDialog extends InstrumentedDialogFragment {
+ public static final String TAG = "delete_sim_progress";
+
+ // Note that this must be listed in AndroidManfiest.xml in a <protected-broadcast> tag
+ @VisibleForTesting
+ static final String PENDING_INTENT =
+ "com.android.settings.DELETE_SIM_PROFILE_RESULT";
+ private static final int PENDING_INTENT_REQUEST_CODE = 1;
+ private static final String KEY_SUBSCRIPTION_ID = "subscription_id";
+ @VisibleForTesting
+ static final String KEY_DELETE_STARTED = "delete_started";
+
+ private boolean mDeleteStarted;
+ private BroadcastReceiver mReceiver;
+
+ public static DeleteSimProfileProgressDialog newInstance(int subscriptionId) {
+ final DeleteSimProfileProgressDialog dialog = new DeleteSimProfileProgressDialog();
+ final Bundle args = new Bundle();
+ args.putInt(KEY_SUBSCRIPTION_ID, subscriptionId);
+ dialog.setArguments(args);
+ return dialog;
+ }
+
+ @Override
+ public void onSaveInstanceState(@NonNull Bundle outState) {
+ super.onSaveInstanceState(outState);
+ outState.putBoolean(KEY_DELETE_STARTED, mDeleteStarted);
+ }
+
+ @NonNull
+ @Override
+ public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) {
+ if (savedInstanceState != null) {
+ mDeleteStarted = savedInstanceState.getBoolean(KEY_DELETE_STARTED, false);
+ }
+ final Context context = getContext();
+ final ProgressDialog progressDialog = new ProgressDialog(context);
+ progressDialog.setMessage(
+ context.getString(R.string.mobile_network_erase_sim_dialog_progress));
+
+ mReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ dismiss();
+ final Activity activity = getActivity();
+ if (activity != null && !activity.isFinishing()) {
+ activity.finish();
+ }
+ }
+ };
+ context.registerReceiver(mReceiver, new IntentFilter(PENDING_INTENT));
+
+ if (!mDeleteStarted) {
+ final PendingIntent pendingIntent = PendingIntent.getBroadcast(context,
+ PENDING_INTENT_REQUEST_CODE, new Intent(PENDING_INTENT),
+ PendingIntent.FLAG_ONE_SHOT);
+
+ final EuiccManager euiccManager = context.getSystemService(EuiccManager.class);
+ final int subId = getArguments().getInt(KEY_SUBSCRIPTION_ID);
+ euiccManager.deleteSubscription(subId, pendingIntent);
+ mDeleteStarted = true;
+ }
+
+ return progressDialog;
+ }
+
+ @Override
+ public void onDismiss(@NonNull DialogInterface dialog) {
+ if (mReceiver != null) {
+ final Context context = getContext();
+ if (context != null) {
+ context.unregisterReceiver(mReceiver);
+ }
+ mReceiver = null;
+ }
+ }
+
+ @Override
+ public int getMetricsCategory() {
+ // TODO(b/131519375) - use a real id here once it's been created in the metrics proto
+ return 0;
+ }
+}
diff --git a/src/com/android/settings/network/telephony/MobileNetworkSettings.java b/src/com/android/settings/network/telephony/MobileNetworkSettings.java
index eb00b9f..e18971d 100644
--- a/src/com/android/settings/network/telephony/MobileNetworkSettings.java
+++ b/src/com/android/settings/network/telephony/MobileNetworkSettings.java
@@ -138,6 +138,7 @@
use(BillingCyclePreferenceController.class).init(mSubId);
use(MmsMessagePreferenceController.class).init(mSubId);
use(DisabledSubscriptionController.class).init(getLifecycle(), mSubId);
+ use(DeleteSimProfilePreferenceController.class).init(mSubId, this);
}
use(MobileDataPreferenceController.class).init(getFragmentManager(), mSubId);
use(RoamingPreferenceController.class).init(getFragmentManager(), mSubId);
diff --git a/src/com/android/settings/shortcut/CreateShortcutPreferenceController.java b/src/com/android/settings/shortcut/CreateShortcutPreferenceController.java
index 9b9de5f..6b95b92 100644
--- a/src/com/android/settings/shortcut/CreateShortcutPreferenceController.java
+++ b/src/com/android/settings/shortcut/CreateShortcutPreferenceController.java
@@ -38,11 +38,6 @@
import android.view.View;
import android.widget.ImageView;
-import androidx.annotation.VisibleForTesting;
-import androidx.preference.Preference;
-import androidx.preference.PreferenceCategory;
-import androidx.preference.PreferenceGroup;
-
import com.android.settings.R;
import com.android.settings.Settings.TetherSettingsActivity;
import com.android.settings.core.BasePreferenceController;
@@ -54,6 +49,11 @@
import java.util.Comparator;
import java.util.List;
+import androidx.annotation.VisibleForTesting;
+import androidx.preference.Preference;
+import androidx.preference.PreferenceCategory;
+import androidx.preference.PreferenceGroup;
+
/**
* {@link BasePreferenceController} that populates a list of widgets that Settings app support.
*/
@@ -143,24 +143,7 @@
@VisibleForTesting
Intent createResultIntent(Intent shortcutIntent, ResolveInfo resolveInfo,
CharSequence label) {
- final ActivityInfo activityInfo = resolveInfo.activityInfo;
-
- final Icon maskableIcon;
- if (activityInfo.icon != 0 && activityInfo.applicationInfo != null) {
- maskableIcon = Icon.createWithAdaptiveBitmap(createIcon(
- activityInfo.applicationInfo, activityInfo.icon,
- R.layout.shortcut_badge_maskable,
- mContext.getResources().getDimensionPixelSize(R.dimen.shortcut_size_maskable)));
- } else {
- maskableIcon = Icon.createWithResource(mContext, R.drawable.ic_launcher_settings);
- }
- final String shortcutId = SHORTCUT_ID_PREFIX +
- shortcutIntent.getComponent().flattenToShortString();
- ShortcutInfo info = new ShortcutInfo.Builder(mContext, shortcutId)
- .setShortLabel(label)
- .setIntent(shortcutIntent)
- .setIcon(maskableIcon)
- .build();
+ ShortcutInfo info = createShortcutInfo(mContext, shortcutIntent, resolveInfo, label);
Intent intent = mShortcutManager.createShortcutResultIntent(info);
if (intent == null) {
intent = new Intent();
@@ -170,8 +153,10 @@
.putExtra(Intent.EXTRA_SHORTCUT_INTENT, shortcutIntent)
.putExtra(Intent.EXTRA_SHORTCUT_NAME, label);
+ final ActivityInfo activityInfo = resolveInfo.activityInfo;
if (activityInfo.icon != 0) {
intent.putExtra(Intent.EXTRA_SHORTCUT_ICON, createIcon(
+ mContext,
activityInfo.applicationInfo,
activityInfo.icon,
R.layout.shortcut_badge,
@@ -217,15 +202,40 @@
info.activityInfo.name);
}
- private Intent buildShortcutIntent(ResolveInfo info) {
+ private static Intent buildShortcutIntent(ResolveInfo info) {
return new Intent(SHORTCUT_PROBE)
.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP)
.setClassName(info.activityInfo.packageName, info.activityInfo.name);
}
- private Bitmap createIcon(ApplicationInfo app, int resource, int layoutRes, int size) {
- final Context context = new ContextThemeWrapper(mContext, android.R.style.Theme_Material);
- final View view = LayoutInflater.from(context).inflate(layoutRes, null);
+ private static ShortcutInfo createShortcutInfo(Context context, Intent shortcutIntent,
+ ResolveInfo resolveInfo, CharSequence label) {
+ final ActivityInfo activityInfo = resolveInfo.activityInfo;
+
+ final Icon maskableIcon;
+ if (activityInfo.icon != 0 && activityInfo.applicationInfo != null) {
+ maskableIcon = Icon.createWithAdaptiveBitmap(createIcon(
+ context,
+ activityInfo.applicationInfo, activityInfo.icon,
+ R.layout.shortcut_badge_maskable,
+ context.getResources().getDimensionPixelSize(R.dimen.shortcut_size_maskable)));
+ } else {
+ maskableIcon = Icon.createWithResource(context, R.drawable.ic_launcher_settings);
+ }
+ final String shortcutId = SHORTCUT_ID_PREFIX +
+ shortcutIntent.getComponent().flattenToShortString();
+ return new ShortcutInfo.Builder(context, shortcutId)
+ .setShortLabel(label)
+ .setIntent(shortcutIntent)
+ .setIcon(maskableIcon)
+ .build();
+ }
+
+ private static Bitmap createIcon(Context context, ApplicationInfo app, int resource,
+ int layoutRes, int size) {
+ final Context themedContext = new ContextThemeWrapper(context,
+ android.R.style.Theme_Material);
+ final View view = LayoutInflater.from(themedContext).inflate(layoutRes, null);
final int spec = View.MeasureSpec.makeMeasureSpec(size, View.MeasureSpec.EXACTLY);
view.measure(spec, spec);
final Bitmap bitmap = Bitmap.createBitmap(view.getMeasuredWidth(), view.getMeasuredHeight(),
@@ -234,14 +244,15 @@
Drawable iconDrawable;
try {
- iconDrawable = mPackageManager.getResourcesForApplication(app).getDrawable(resource);
+ iconDrawable = context.getPackageManager().getResourcesForApplication(app)
+ .getDrawable(resource);
if (iconDrawable instanceof LayerDrawable) {
iconDrawable = ((LayerDrawable) iconDrawable).getDrawable(1);
}
((ImageView) view.findViewById(android.R.id.icon)).setImageDrawable(iconDrawable);
} catch (PackageManager.NameNotFoundException e) {
Log.w(TAG, "Cannot load icon from app " + app + ", returning a default icon");
- Icon icon = Icon.createWithResource(mContext, R.drawable.ic_launcher_settings);
+ Icon icon = Icon.createWithResource(context, R.drawable.ic_launcher_settings);
((ImageView) view.findViewById(android.R.id.icon)).setImageIcon(icon);
}
@@ -250,12 +261,24 @@
return bitmap;
}
- private static final Comparator<ResolveInfo> SHORTCUT_COMPARATOR =
- new Comparator<ResolveInfo>() {
+ public static void updateRestoredShortcuts(Context context) {
+ ShortcutManager sm = context.getSystemService(ShortcutManager.class);
+ List<ShortcutInfo> updatedShortcuts = new ArrayList<>();
+ for (ShortcutInfo si : sm.getPinnedShortcuts()) {
+ if (si.getId().startsWith(SHORTCUT_ID_PREFIX)) {
+ ResolveInfo ri = context.getPackageManager().resolveActivity(si.getIntent(), 0);
- @Override
- public int compare(ResolveInfo i1, ResolveInfo i2) {
- return i1.priority - i2.priority;
+ if (ri != null) {
+ updatedShortcuts.add(createShortcutInfo(context, buildShortcutIntent(ri), ri,
+ si.getShortLabel()));
}
- };
+ }
+ }
+ if (!updatedShortcuts.isEmpty()) {
+ sm.updateShortcuts(updatedShortcuts);
+ }
+ }
+
+ private static final Comparator<ResolveInfo> SHORTCUT_COMPARATOR =
+ (i1, i2) -> i1.priority - i2.priority;
}
diff --git a/src/com/android/settings/sim/SimSelectNotification.java b/src/com/android/settings/sim/SimSelectNotification.java
index 3179e6a..911d0e8 100644
--- a/src/com/android/settings/sim/SimSelectNotification.java
+++ b/src/com/android/settings/sim/SimSelectNotification.java
@@ -16,12 +16,18 @@
package com.android.settings.sim;
+import static android.provider.Settings.ENABLE_MMS_DATA_REQUEST_REASON_INCOMING_MMS;
+import static android.provider.Settings.ENABLE_MMS_DATA_REQUEST_REASON_OUTGOING_MMS;
+import static android.provider.Settings.EXTRA_ENABLE_MMS_DATA_REQUEST_REASON;
+import static android.provider.Settings.EXTRA_SUB_ID;
import static android.telephony.TelephonyManager.EXTRA_DEFAULT_SUBSCRIPTION_SELECT_TYPE;
import static android.telephony.TelephonyManager.EXTRA_DEFAULT_SUBSCRIPTION_SELECT_TYPE_ALL;
import static android.telephony.TelephonyManager.EXTRA_DEFAULT_SUBSCRIPTION_SELECT_TYPE_DATA;
import static android.telephony.TelephonyManager.EXTRA_DEFAULT_SUBSCRIPTION_SELECT_TYPE_NONE;
import static android.telephony.TelephonyManager.EXTRA_SUBSCRIPTION_ID;
+import static android.telephony.data.ApnSetting.TYPE_MMS;
+import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.PendingIntent;
@@ -32,28 +38,97 @@
import android.provider.Settings;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
+import android.util.Log;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.settings.R;
-import com.android.settings.Settings.SimSettingsActivity;
-
-import androidx.core.app.NotificationCompat;
+import com.android.settings.network.telephony.MobileNetworkActivity;
public class SimSelectNotification extends BroadcastReceiver {
private static final String TAG = "SimSelectNotification";
- private static final int NOTIFICATION_ID = 1;
+ @VisibleForTesting
+ public static final int SIM_SELECT_NOTIFICATION_ID = 1;
+ @VisibleForTesting
+ public static final int ENABLE_MMS_NOTIFICATION_ID = 2;
- private static final String SIM_SELECT_NOTIFICATION_CHANNEL =
+ @VisibleForTesting
+ public static final String SIM_SELECT_NOTIFICATION_CHANNEL =
"sim_select_notification_channel";
+ @VisibleForTesting
+ public static final String ENABLE_MMS_NOTIFICATION_CHANNEL =
+ "enable_mms_notification_channel";
+
@Override
public void onReceive(Context context, Intent intent) {
- if (!TelephonyManager.ACTION_PRIMARY_SUBSCRIPTION_LIST_CHANGED.equals(intent.getAction())) {
+ String action = intent.getAction();
+
+ if (action == null) {
+ Log.w(TAG, "Received unexpected intent with null action.");
return;
}
+
+ switch (action) {
+ case TelephonyManager.ACTION_PRIMARY_SUBSCRIPTION_LIST_CHANGED:
+ onPrimarySubscriptionListChanged(context, intent);
+ break;
+ case Settings.ACTION_ENABLE_MMS_DATA_REQUEST:
+ onEnableMmsDataRequest(context, intent);
+ break;
+ default:
+ Log.w(TAG, "Received unexpected intent " + intent.getAction());
+ }
+ }
+
+ private void onEnableMmsDataRequest(Context context, Intent intent) {
+ // Getting subId from extra.
+ int subId = intent.getIntExtra(EXTRA_SUB_ID, SubscriptionManager.INVALID_SUBSCRIPTION_ID);
+ if (subId == SubscriptionManager.DEFAULT_SUBSCRIPTION_ID) {
+ subId = SubscriptionManager.getDefaultSmsSubscriptionId();
+ }
+
+ SubscriptionManager subscriptionManager = ((SubscriptionManager) context.getSystemService(
+ Context.TELEPHONY_SUBSCRIPTION_SERVICE));
+ if (!subscriptionManager.isActiveSubId(subId)) {
+ Log.w(TAG, "onEnableMmsDataRequest invalid sub ID " + subId);
+ return;
+ }
+
+ // Getting request reason from extra, which will determine the notification title.
+ CharSequence notificationTitle = null;
+ int requestReason = intent.getIntExtra(EXTRA_ENABLE_MMS_DATA_REQUEST_REASON, -1);
+ if (requestReason == ENABLE_MMS_DATA_REQUEST_REASON_INCOMING_MMS) {
+ notificationTitle = context.getResources().getText(
+ R.string.enable_receiving_mms_notification_title);
+ } else if (requestReason == ENABLE_MMS_DATA_REQUEST_REASON_OUTGOING_MMS) {
+ notificationTitle = context.getResources().getText(
+ R.string.enable_sending_mms_notification_title);
+ } else {
+ Log.w(TAG, "onEnableMmsDataRequest invalid request reason " + requestReason);
+ return;
+ }
+
+ TelephonyManager tm = ((TelephonyManager) context.getSystemService(
+ Context.TELEPHONY_SERVICE)).createForSubscriptionId(subId);
+
+ if (tm.isDataEnabledForApn(TYPE_MMS)) {
+ Log.w(TAG, "onEnableMmsDataRequest MMS data already enabled on sub ID " + subId);
+ return;
+ }
+
+ CharSequence notificationSummary = context.getResources().getString(
+ R.string.enable_mms_notification_summary, tm.getSimOperatorName());
+
+ cancelEnableMmsNotification(context);
+
+ createEnableMmsNotification(context, notificationTitle, notificationSummary, subId);
+ }
+
+ private void onPrimarySubscriptionListChanged(Context context, Intent intent) {
// Cancel any previous notifications
- cancelNotification(context);
+ cancelSimSelectNotification(context);
// Create a notification to tell the user that some defaults are missing
- createNotification(context);
+ createSimSelectNotification(context);
int dialogType = intent.getIntExtra(EXTRA_DEFAULT_SUBSCRIPTION_SELECT_TYPE,
EXTRA_DEFAULT_SUBSCRIPTION_SELECT_TYPE_NONE);
@@ -76,20 +151,20 @@
}
}
- private void createNotification(Context context){
+ private void createSimSelectNotification(Context context){
final Resources resources = context.getResources();
NotificationChannel notificationChannel = new NotificationChannel(
SIM_SELECT_NOTIFICATION_CHANNEL,
- resources.getString(R.string.sim_selection_channel_title),
+ resources.getText(R.string.sim_selection_channel_title),
NotificationManager.IMPORTANCE_LOW);
- NotificationCompat.Builder builder =
- new NotificationCompat.Builder(context, SIM_SELECT_NOTIFICATION_CHANNEL)
+ Notification.Builder builder =
+ new Notification.Builder(context, SIM_SELECT_NOTIFICATION_CHANNEL)
.setSmallIcon(R.drawable.ic_sim_card_alert_white_48dp)
.setColor(context.getColor(R.color.sim_noitification))
- .setContentTitle(resources.getString(R.string.sim_notification_title))
- .setContentText(resources.getString(R.string.sim_notification_summary));
+ .setContentTitle(resources.getText(R.string.sim_notification_title))
+ .setContentText(resources.getText(R.string.sim_notification_summary));
Intent resultIntent = new Intent(Settings.ACTION_WIRELESS_SETTINGS);
resultIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
PendingIntent resultPendingIntent = PendingIntent.getActivity(context, 0, resultIntent,
@@ -98,12 +173,51 @@
NotificationManager notificationManager =
(NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
notificationManager.createNotificationChannel(notificationChannel);
- notificationManager.notify(NOTIFICATION_ID, builder.build());
+ notificationManager.notify(SIM_SELECT_NOTIFICATION_ID, builder.build());
}
- public static void cancelNotification(Context context) {
+ public static void cancelSimSelectNotification(Context context) {
NotificationManager notificationManager =
(NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
- notificationManager.cancel(NOTIFICATION_ID);
+ notificationManager.cancel(SIM_SELECT_NOTIFICATION_ID);
+ }
+
+ private void createEnableMmsNotification(Context context, CharSequence titleString,
+ CharSequence notificationSummary, int subId) {
+ final Resources resources = context.getResources();
+
+ NotificationChannel notificationChannel = new NotificationChannel(
+ ENABLE_MMS_NOTIFICATION_CHANNEL,
+ resources.getText(R.string.enable_mms_notification_channel_title),
+ NotificationManager.IMPORTANCE_HIGH);
+
+ Notification.Builder builder =
+ new Notification.Builder(context, ENABLE_MMS_NOTIFICATION_CHANNEL)
+ .setSmallIcon(R.drawable.ic_settings_24dp)
+ .setColor(context.getColor(R.color.sim_noitification))
+ .setContentTitle(titleString)
+ .setContentText(notificationSummary)
+ .setStyle(new Notification.BigTextStyle().bigText(notificationSummary));
+
+ // Create the pending intent that will lead to the subscription setting page.
+ Intent resultIntent = new Intent(Settings.ACTION_MMS_MESSAGE_SETTING);
+ resultIntent.setClass(context, MobileNetworkActivity.class);
+ resultIntent.putExtra(Settings.EXTRA_SUB_ID, subId);
+ resultIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ PendingIntent resultPendingIntent = PendingIntent.getActivity(context, 0, resultIntent,
+ PendingIntent.FLAG_CANCEL_CURRENT);
+ builder.setContentIntent(resultPendingIntent);
+
+ // Notify the notification.
+ NotificationManager notificationManager =
+ (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
+ notificationManager.createNotificationChannel(notificationChannel);
+ notificationManager.notify(ENABLE_MMS_NOTIFICATION_ID, builder.build());
+ }
+
+ private void cancelEnableMmsNotification(Context context) {
+ NotificationManager notificationManager =
+ (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
+ notificationManager.cancel(ENABLE_MMS_NOTIFICATION_ID);
}
}
diff --git a/src/com/android/settings/sim/SimSettings.java b/src/com/android/settings/sim/SimSettings.java
index e43c6a5..1222913 100644
--- a/src/com/android/settings/sim/SimSettings.java
+++ b/src/com/android/settings/sim/SimSettings.java
@@ -101,7 +101,7 @@
mSimCards = (PreferenceScreen)findPreference(SIM_CARD_CATEGORY);
mAvailableSubInfos = new ArrayList<SubscriptionInfo>(mNumSlots);
mSelectableSubInfos = new ArrayList<SubscriptionInfo>();
- SimSelectNotification.cancelNotification(getActivity());
+ SimSelectNotification.cancelSimSelectNotification(getActivity());
}
private final SubscriptionManager.OnSubscriptionsChangedListener mOnSubscriptionsChangeListener
diff --git a/src/com/android/settings/sound/AudioSwitchPreferenceController.java b/src/com/android/settings/sound/AudioSwitchPreferenceController.java
index 0da0f21..5b70d16 100644
--- a/src/com/android/settings/sound/AudioSwitchPreferenceController.java
+++ b/src/com/android/settings/sound/AudioSwitchPreferenceController.java
@@ -17,7 +17,6 @@
package com.android.settings.sound;
import static android.media.AudioManager.STREAM_DEVICES_CHANGED_ACTION;
-import static android.media.MediaRouter.ROUTE_TYPE_REMOTE_DISPLAY;
import android.bluetooth.BluetoothDevice;
import android.content.BroadcastReceiver;
@@ -29,7 +28,6 @@
import android.media.AudioDeviceInfo;
import android.media.AudioManager;
import android.media.MediaRouter;
-import android.media.MediaRouter.Callback;
import android.os.Handler;
import android.os.Looper;
import android.util.FeatureFlagUtils;
@@ -76,7 +74,6 @@
protected AudioSwitchCallback mAudioSwitchPreferenceCallback;
private final AudioManagerAudioDeviceCallback mAudioManagerAudioDeviceCallback;
- private final MediaRouterCallback mMediaRouterCallback;
private final WiredHeadsetBroadcastReceiver mReceiver;
private final Handler mHandler;
private LocalBluetoothManager mLocalBluetoothManager;
@@ -92,7 +89,6 @@
mHandler = new Handler(Looper.getMainLooper());
mAudioManagerAudioDeviceCallback = new AudioManagerAudioDeviceCallback();
mReceiver = new WiredHeadsetBroadcastReceiver();
- mMediaRouterCallback = new MediaRouterCallback();
mConnectedDevices = new ArrayList<>();
final FutureTask<LocalBluetoothManager> localBtManagerFutureTask = new FutureTask<>(
// Avoid StrictMode ThreadPolicy violation
@@ -210,12 +206,12 @@
* get A2dp devices on all states
* (STATE_DISCONNECTED, STATE_CONNECTING, STATE_CONNECTED, STATE_DISCONNECTING)
*/
- protected List<BluetoothDevice> getConnectableA2dpDevices() {
+ protected List<BluetoothDevice> getConnectedA2dpDevices() {
final A2dpProfile a2dpProfile = mProfileManager.getA2dpProfile();
if (a2dpProfile == null) {
return new ArrayList<>();
}
- return a2dpProfile.getConnectableDevices();
+ return a2dpProfile.getConnectedDevices();
}
/**
@@ -242,31 +238,6 @@
}
/**
- * get hearing aid profile devices on all states
- * (STATE_DISCONNECTED, STATE_CONNECTING, STATE_CONNECTED, STATE_DISCONNECTING)
- * exclude other devices with same hiSyncId.
- */
- protected List<BluetoothDevice> getConnectableHearingAidDevices() {
- final List<BluetoothDevice> connectedDevices = new ArrayList<>();
- final HearingAidProfile hapProfile = mProfileManager.getHearingAidProfile();
- if (hapProfile == null) {
- return connectedDevices;
- }
- final List<Long> devicesHiSyncIds = new ArrayList<>();
- final List<BluetoothDevice> devices = hapProfile.getConnectableDevices();
- for (BluetoothDevice device : devices) {
- final long hiSyncId = hapProfile.getHiSyncId(device);
- // device with same hiSyncId should not be shown in the UI.
- // So do not add it into connectedDevices.
- if (!devicesHiSyncIds.contains(hiSyncId)) {
- devicesHiSyncIds.add(hiSyncId);
- connectedDevices.add(device);
- }
- }
- return connectedDevices;
- }
-
- /**
* Find active hearing aid device
*/
protected BluetoothDevice findActiveHearingAidDevice() {
@@ -299,7 +270,6 @@
private void register() {
mLocalBluetoothManager.getEventManager().registerCallback(this);
mAudioManager.registerAudioDeviceCallback(mAudioManagerAudioDeviceCallback, mHandler);
- mMediaRouter.addCallback(ROUTE_TYPE_REMOTE_DISPLAY, mMediaRouterCallback);
// Register for misc other intent broadcasts.
IntentFilter intentFilter = new IntentFilter(Intent.ACTION_HEADSET_PLUG);
@@ -310,7 +280,6 @@
private void unregister() {
mLocalBluetoothManager.getEventManager().unregisterCallback(this);
mAudioManager.unregisterAudioDeviceCallback(mAudioManagerAudioDeviceCallback);
- mMediaRouter.removeCallback(mMediaRouterCallback);
mContext.unregisterReceiver(mReceiver);
}
@@ -338,49 +307,4 @@
}
}
}
-
- /** Callback for cast device events. */
- private class MediaRouterCallback extends Callback {
- @Override
- public void onRouteSelected(MediaRouter router, int type, MediaRouter.RouteInfo info) {
- }
-
- @Override
- public void onRouteUnselected(MediaRouter router, int type, MediaRouter.RouteInfo info) {
- }
-
- @Override
- public void onRouteAdded(MediaRouter router, MediaRouter.RouteInfo info) {
- if (info != null && !info.isDefault()) {
- // cast mode
- updateState(mPreference);
- }
- }
-
- @Override
- public void onRouteRemoved(MediaRouter router, MediaRouter.RouteInfo info) {
- }
-
- @Override
- public void onRouteChanged(MediaRouter router, MediaRouter.RouteInfo info) {
- if (info != null && !info.isDefault()) {
- // cast mode
- updateState(mPreference);
- }
- }
-
- @Override
- public void onRouteGrouped(MediaRouter router, MediaRouter.RouteInfo info,
- MediaRouter.RouteGroup group, int index) {
- }
-
- @Override
- public void onRouteUngrouped(MediaRouter router, MediaRouter.RouteInfo info,
- MediaRouter.RouteGroup group) {
- }
-
- @Override
- public void onRouteVolumeChanged(MediaRouter router, MediaRouter.RouteInfo info) {
- }
- }
}
diff --git a/src/com/android/settings/sound/MediaOutputPreferenceController.java b/src/com/android/settings/sound/MediaOutputPreferenceController.java
index 47810f7..1831ad6 100644
--- a/src/com/android/settings/sound/MediaOutputPreferenceController.java
+++ b/src/com/android/settings/sound/MediaOutputPreferenceController.java
@@ -16,9 +16,6 @@
package com.android.settings.sound;
-import static android.media.AudioManager.STREAM_MUSIC;
-import static android.media.AudioSystem.DEVICE_OUT_REMOTE_SUBMIX;
-
import android.bluetooth.BluetoothDevice;
import android.content.Context;
import android.content.Intent;
@@ -56,13 +53,6 @@
return;
}
- if (isStreamFromOutputDevice(STREAM_MUSIC, DEVICE_OUT_REMOTE_SUBMIX)) {
- // In cast mode, disable switch entry.
- mPreference.setVisible(false);
- preference.setSummary(mContext.getText(R.string.media_output_summary_unavailable));
- return;
- }
-
if (Utils.isAudioModeOngoingCall(mContext)) {
// Ongoing call status, switch entry for media will be disabled.
mPreference.setVisible(false);
@@ -71,19 +61,19 @@
return;
}
- boolean deviceConnectable = false;
+ boolean deviceConnected = false;
BluetoothDevice activeDevice = null;
// Show preference if there is connected or previously connected device
// Find active device and set its name as the preference's summary
- List<BluetoothDevice> connectableA2dpDevices = getConnectableA2dpDevices();
- List<BluetoothDevice> connectableHADevices = getConnectableHearingAidDevices();
+ List<BluetoothDevice> connectedA2dpDevices = getConnectedA2dpDevices();
+ List<BluetoothDevice> connectedHADevices = getConnectedHearingAidDevices();
if (mAudioManager.getMode() == AudioManager.MODE_NORMAL
- && ((connectableA2dpDevices != null && !connectableA2dpDevices.isEmpty())
- || (connectableHADevices != null && !connectableHADevices.isEmpty()))) {
- deviceConnectable = true;
+ && ((connectedA2dpDevices != null && !connectedA2dpDevices.isEmpty())
+ || (connectedHADevices != null && !connectedHADevices.isEmpty()))) {
+ deviceConnected = true;
activeDevice = findActiveDevice();
}
- mPreference.setVisible(deviceConnectable);
+ mPreference.setVisible(deviceConnected);
mPreference.setSummary((activeDevice == null) ?
mContext.getText(R.string.media_output_default_summary) :
activeDevice.getAliasName());
diff --git a/tests/robotests/src/com/android/settings/bluetooth/LocalDeviceNameDialogFragmentTest.java b/tests/robotests/src/com/android/settings/bluetooth/LocalDeviceNameDialogFragmentTest.java
deleted file mode 100644
index 9a6ad91..0000000
--- a/tests/robotests/src/com/android/settings/bluetooth/LocalDeviceNameDialogFragmentTest.java
+++ /dev/null
@@ -1,85 +0,0 @@
-/*
- * 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.bluetooth;
-
-import static com.google.common.truth.Truth.assertThat;
-
-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.content.Context;
-import android.view.View;
-import android.view.inputmethod.InputMethodManager;
-
-import androidx.appcompat.app.AlertDialog;
-import androidx.fragment.app.FragmentActivity;
-
-import com.android.settings.R;
-import com.android.settings.testutils.shadow.ShadowAlertDialogCompat;
-import com.android.settingslib.bluetooth.LocalBluetoothManager;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Ignore;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-import org.robolectric.RobolectricTestRunner;
-import org.robolectric.RuntimeEnvironment;
-import org.robolectric.annotation.Config;
-import org.robolectric.shadows.androidx.fragment.FragmentController;
-import org.robolectric.util.ReflectionHelpers;
-
-@RunWith(RobolectricTestRunner.class)
-@Config(shadows = ShadowAlertDialogCompat.class)
-public class LocalDeviceNameDialogFragmentTest {
-
- @Mock
- private InputMethodManager mInputMethodManager;
-
- private Context mContext;
- private LocalDeviceNameDialogFragment mFragment;
-
- @Before
- public void setUp() {
- MockitoAnnotations.initMocks(this);
- mContext = spy(RuntimeEnvironment.application);
- doReturn(mInputMethodManager).when(mContext).getSystemService(Context.INPUT_METHOD_SERVICE);
-
- mFragment = spy(LocalDeviceNameDialogFragment.newInstance());
- when(mFragment.getContext()).thenReturn(mContext);
- }
-
- @After
- public void tearDown() {
- ReflectionHelpers.setStaticField(LocalBluetoothManager.class, "sInstance", null);
- }
-
- @Test
- @Ignore("b/120505691")
- public void dialogTriggersShowSoftInput() {
- FragmentController.setupFragment(mFragment, FragmentActivity.class, 0 /* containerViewId */,
- null /* bundle */);
- AlertDialog dialog = ShadowAlertDialogCompat.getLatestAlertDialog();
- assertThat(dialog).isNotNull();
- View view = dialog.findViewById(R.id.edittext);
- verify(mInputMethodManager).showSoftInput(view, InputMethodManager.SHOW_IMPLICIT);
- }
-}
diff --git a/tests/robotests/src/com/android/settings/network/telephony/DeleteSimProfileConfirmationDialogTest.java b/tests/robotests/src/com/android/settings/network/telephony/DeleteSimProfileConfirmationDialogTest.java
new file mode 100644
index 0000000..9b6f551
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/network/telephony/DeleteSimProfileConfirmationDialogTest.java
@@ -0,0 +1,79 @@
+/*
+ * 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 org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+
+import android.app.Activity;
+import android.app.Dialog;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.telephony.SubscriptionInfo;
+
+import androidx.appcompat.app.AlertDialog;
+import androidx.fragment.app.FragmentActivity;
+
+import com.android.settings.testutils.shadow.ShadowAlertDialogCompat;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.Robolectric;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.annotation.Config;
+import org.robolectric.shadows.androidx.fragment.FragmentController;
+
+@RunWith(RobolectricTestRunner.class)
+@Config(shadows = ShadowAlertDialogCompat.class)
+public class DeleteSimProfileConfirmationDialogTest {
+ @Mock
+ private SubscriptionInfo mSubscriptionInfo;
+
+ private DeleteSimProfileConfirmationDialog mDialogFragment;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ mDialogFragment = spy(DeleteSimProfileConfirmationDialog.newInstance(mSubscriptionInfo));
+ doNothing().when(mDialogFragment).beginDeletionWithProgress();
+ }
+
+ @Test
+ public void showDialog_dialogCancelled_deleteNotCalled() {
+ FragmentController.setupFragment(mDialogFragment, FragmentActivity.class,
+ 0 /* containerViewId */,
+ null /* bundle */);
+ final AlertDialog dialog = ShadowAlertDialogCompat.getLatestAlertDialog();
+ dialog.getButton(DialogInterface.BUTTON_NEGATIVE).performClick();
+ verify(mDialogFragment, never()).beginDeletionWithProgress();
+ }
+
+ @Test
+ public void showDialog_dialogOk_deleteWasCalled() {
+ FragmentController.setupFragment(mDialogFragment, FragmentActivity.class,
+ 0 /* containerViewId */,
+ null /* bundle */);
+ final AlertDialog dialog = ShadowAlertDialogCompat.getLatestAlertDialog();
+ dialog.getButton(DialogInterface.BUTTON_POSITIVE).performClick();
+ verify(mDialogFragment).beginDeletionWithProgress();
+ }
+}
diff --git a/tests/robotests/src/com/android/settings/network/telephony/DeleteSimProfilePreferenceControllerTest.java b/tests/robotests/src/com/android/settings/network/telephony/DeleteSimProfilePreferenceControllerTest.java
new file mode 100644
index 0000000..21fd19b
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/network/telephony/DeleteSimProfilePreferenceControllerTest.java
@@ -0,0 +1,111 @@
+/*
+ * 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 com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.telephony.SubscriptionInfo;
+import android.telephony.euicc.EuiccManager;
+
+import androidx.fragment.app.Fragment;
+import androidx.preference.Preference;
+import androidx.preference.PreferenceScreen;
+
+import com.android.settings.network.SubscriptionUtil;
+
+import org.junit.After;
+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.RuntimeEnvironment;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+
+@RunWith(RobolectricTestRunner.class)
+public class DeleteSimProfilePreferenceControllerTest {
+ private static final String PREF_KEY = "delete_profile_key";
+ private static final int SUB_ID = 1234;
+ private static final int OTHER_ID = 5678;
+
+ @Mock
+ private Fragment mFragment;
+
+ @Mock
+ private SubscriptionInfo mSubscriptionInfo;
+ @Mock
+ private PreferenceScreen mScreen;
+
+ private Context mContext;
+ private Preference mPreference;
+ private DeleteSimProfilePreferenceController mController;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ mContext = spy(RuntimeEnvironment.application);
+
+ SubscriptionUtil.setAvailableSubscriptionsForTesting(Arrays.asList(mSubscriptionInfo));
+ when(mSubscriptionInfo.getSubscriptionId()).thenReturn(SUB_ID);
+ when(mSubscriptionInfo.isEmbedded()).thenReturn(true);
+
+ mPreference = new Preference(mContext);
+ mPreference.setKey(PREF_KEY);
+ when(mScreen.findPreference(PREF_KEY)).thenReturn(mPreference);
+
+ mController = new DeleteSimProfilePreferenceController(mContext, PREF_KEY);
+ }
+
+ @After
+ public void tearDown() {
+ SubscriptionUtil.setAvailableSubscriptionsForTesting(null);
+ }
+
+ @Test
+ public void getAvailabilityStatus_noSubs_notAvailable() {
+ SubscriptionUtil.setAvailableSubscriptionsForTesting(new ArrayList<>());
+ mController.init(SUB_ID, mFragment);
+ assertThat(mController.isAvailable()).isFalse();
+ }
+
+ @Test
+ public void getAvailabilityStatus_physicalSim_notAvailable() {
+ when(mSubscriptionInfo.isEmbedded()).thenReturn(false);
+ mController.init(SUB_ID, mFragment);
+ assertThat(mController.isAvailable()).isFalse();
+ }
+
+ @Test
+ public void getAvailabilityStatus_unknownSim_notAvailable() {
+ when(mSubscriptionInfo.getSubscriptionId()).thenReturn(OTHER_ID);
+ mController.init(SUB_ID, mFragment);
+ assertThat(mController.isAvailable()).isFalse();
+ }
+
+ @Test
+ public void getAvailabilityStatus_knownEsim_isAvailable() {
+ mController.init(SUB_ID, mFragment);
+ assertThat(mController.isAvailable()).isTrue();
+ }
+}
diff --git a/tests/robotests/src/com/android/settings/network/telephony/DeleteSimProfileProgressDialogTest.java b/tests/robotests/src/com/android/settings/network/telephony/DeleteSimProfileProgressDialogTest.java
new file mode 100644
index 0000000..aebcc46
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/network/telephony/DeleteSimProfileProgressDialogTest.java
@@ -0,0 +1,139 @@
+/*
+ * 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 com.android.settings.network.telephony.DeleteSimProfileProgressDialog.KEY_DELETE_STARTED;
+import static com.android.settings.network.telephony.DeleteSimProfileProgressDialog.PENDING_INTENT;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.ArgumentMatchers.notNull;
+import static org.mockito.Mockito.doNothing;
+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.app.Dialog;
+import android.app.PendingIntent;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.Bundle;
+import android.telephony.euicc.EuiccManager;
+
+import androidx.fragment.app.Fragment;
+import androidx.fragment.app.FragmentActivity;
+
+import com.android.settings.testutils.shadow.ShadowAlertDialogCompat;
+
+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.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
+
+@RunWith(RobolectricTestRunner.class)
+@Config(shadows = ShadowAlertDialogCompat.class)
+public class DeleteSimProfileProgressDialogTest {
+ private static final int SUB_ID = 111;
+
+ @Mock
+ private FragmentActivity mActivity;
+ @Mock
+ private Fragment mTargetFragment;
+ @Mock
+ private EuiccManager mEuiccManager;
+
+ private Context mContext;
+ private DeleteSimProfileProgressDialog mDialogFragment;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ mContext = spy(RuntimeEnvironment.application);
+
+ when(mContext.getSystemService(EuiccManager.class)).thenReturn(mEuiccManager);
+ mDialogFragment = spy(DeleteSimProfileProgressDialog.newInstance(SUB_ID));
+ when(mDialogFragment.getContext()).thenReturn(mContext);
+ when(mDialogFragment.getTargetFragment()).thenReturn(mTargetFragment);
+ when(mDialogFragment.getActivity()).thenReturn(mActivity);
+ }
+
+ @Test
+ public void onCreateDialog_firstShowing_deleteStartedAndRecordedInOutState() {
+ mDialogFragment.onCreateDialog(null);
+ verify(mEuiccManager).deleteSubscription(eq(SUB_ID), notNull());
+
+ final Bundle outState = new Bundle();
+ mDialogFragment.onSaveInstanceState(outState);
+ assertThat(outState.containsKey(KEY_DELETE_STARTED)).isTrue();
+ assertThat(outState.getBoolean(KEY_DELETE_STARTED)).isTrue();
+ }
+
+ @Test
+ public void showDialog_secondShowing_deleteNotStarted() {
+ final Bundle inState = new Bundle();
+ inState.putBoolean(KEY_DELETE_STARTED, true);
+ mDialogFragment.onCreateDialog(inState);
+
+ verify(mEuiccManager, never()).deleteSubscription(anyInt(), any());
+
+ final Bundle outState = new Bundle();
+ mDialogFragment.onSaveInstanceState(outState);
+ assertThat(outState.containsKey(KEY_DELETE_STARTED)).isTrue();
+ assertThat(outState.getBoolean(KEY_DELETE_STARTED)).isTrue();
+ }
+
+ @Test
+ public void showDialog_pendingIntentReceiverFired_activityFinished() {
+ mDialogFragment.onCreateDialog(null);
+
+ final ArgumentCaptor<PendingIntent> intentCaptor = ArgumentCaptor.forClass(
+ PendingIntent.class);
+ verify(mEuiccManager).deleteSubscription(eq(SUB_ID), intentCaptor.capture());
+ assertThat(intentCaptor.getValue()).isNotNull();
+
+ final ArgumentCaptor<BroadcastReceiver> receiverCaptor = ArgumentCaptor.forClass(
+ BroadcastReceiver.class);
+ verify(mContext).registerReceiver(receiverCaptor.capture(), any(IntentFilter.class));
+
+ doNothing().when(mDialogFragment).dismiss();
+ receiverCaptor.getValue().onReceive(mContext, new Intent(PENDING_INTENT));
+ verify(mDialogFragment).dismiss();
+ verify(mActivity).finish();
+ }
+
+ @Test
+ public void onDismiss_receiverUnregistered() {
+ Dialog dialog = mDialogFragment.onCreateDialog(null);
+ final ArgumentCaptor<BroadcastReceiver> receiverCaptor = ArgumentCaptor.forClass(
+ BroadcastReceiver.class);
+ verify(mContext).registerReceiver(receiverCaptor.capture(), any(IntentFilter.class));
+
+ mDialogFragment.onDismiss(dialog);
+ verify(mContext).unregisterReceiver(eq(receiverCaptor.getValue()));
+ }
+}
diff --git a/tests/robotests/src/com/android/settings/sim/SimSelectNotificationTest.java b/tests/robotests/src/com/android/settings/sim/SimSelectNotificationTest.java
new file mode 100644
index 0000000..69c0919
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/sim/SimSelectNotificationTest.java
@@ -0,0 +1,166 @@
+/*
+ * 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.sim;
+
+
+import static android.app.NotificationManager.IMPORTANCE_HIGH;
+import static android.provider.Settings.ENABLE_MMS_DATA_REQUEST_REASON_INCOMING_MMS;
+import static android.provider.Settings.ENABLE_MMS_DATA_REQUEST_REASON_OUTGOING_MMS;
+import static android.provider.Settings.EXTRA_ENABLE_MMS_DATA_REQUEST_REASON;
+import static android.provider.Settings.EXTRA_SUB_ID;
+import static android.telephony.data.ApnSetting.TYPE_MMS;
+
+import static com.android.settings.sim.SimSelectNotification.ENABLE_MMS_NOTIFICATION_CHANNEL;
+import static com.android.settings.sim.SimSelectNotification.ENABLE_MMS_NOTIFICATION_ID;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.app.Notification;
+import android.app.NotificationChannel;
+import android.app.NotificationManager;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.content.res.Resources;
+import android.provider.Settings;
+import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyManager;
+
+import com.android.settings.R;
+
+import com.android.settings.testutils.shadow.ShadowAlertDialogCompat;
+
+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.RobolectricTestRunner;
+import org.robolectric.annotation.Config;
+
+@RunWith(RobolectricTestRunner.class)
+@Config(shadows = ShadowAlertDialogCompat.class)
+public class SimSelectNotificationTest {
+ @Mock
+ private Context mContext;
+ @Mock
+ private NotificationManager mNotificationManager;
+ @Mock
+ private TelephonyManager mTelephonyManager;
+ @Mock
+ private SubscriptionManager mSubscriptionManager;
+ @Mock
+ private PackageManager mPackageManager;
+ @Mock
+ private Resources mResources;
+
+ private String mFakeOperatorName = "fake_operator_name";
+ private CharSequence mFakeNotificationChannelTitle = "fake_notification_channel_title";
+ private CharSequence mFakeNotificationTitle = "fake_notification_title";
+ private String mFakeNotificationSummary = "fake_notification_Summary";
+
+ private int mSubId = 1;
+
+ SimSelectNotification mSimSelectNotification = new SimSelectNotification();
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ when(mContext.getSystemService(Context.NOTIFICATION_SERVICE))
+ .thenReturn(mNotificationManager);
+ when(mContext.getSystemService(Context.TELEPHONY_SERVICE))
+ .thenReturn(mTelephonyManager);
+ when(mContext.getSystemService(Context.TELEPHONY_SUBSCRIPTION_SERVICE))
+ .thenReturn(mSubscriptionManager);
+ when(mContext.getApplicationInfo()).thenReturn(new ApplicationInfo());
+ when(mContext.getPackageManager()).thenReturn(mPackageManager);
+ when(mPackageManager.checkPermission(any(), any()))
+ .thenReturn(PackageManager.PERMISSION_GRANTED);
+
+ when(mTelephonyManager.createForSubscriptionId(anyInt())).thenReturn(mTelephonyManager);
+ when(mTelephonyManager.getSimOperatorName()).thenReturn(mFakeOperatorName);
+ when(mTelephonyManager.isDataEnabledForApn(TYPE_MMS)).thenReturn(false);
+ when(mSubscriptionManager.isActiveSubId(mSubId)).thenReturn(true);
+ when(mContext.getResources()).thenReturn(mResources);
+
+ when(mResources.getText(R.string.enable_sending_mms_notification_title))
+ .thenReturn(mFakeNotificationTitle);
+ when(mResources.getText(R.string.enable_mms_notification_channel_title))
+ .thenReturn(mFakeNotificationChannelTitle);
+ when(mResources.getString(R.string.enable_mms_notification_summary,
+ mFakeOperatorName)).thenReturn(mFakeNotificationSummary);
+ }
+
+ @Test
+ public void onReceiveEnableMms_notificationShouldSend() {
+ Intent intent = new Intent(Settings.ACTION_ENABLE_MMS_DATA_REQUEST);
+ intent.putExtra(EXTRA_SUB_ID, mSubId);
+ intent.putExtra(EXTRA_ENABLE_MMS_DATA_REQUEST_REASON,
+ ENABLE_MMS_DATA_REQUEST_REASON_OUTGOING_MMS);
+
+ mSimSelectNotification.onReceive(mContext, intent);
+
+ // Capture the notification channel created and verify its fields.
+ ArgumentCaptor<NotificationChannel> nc = ArgumentCaptor.forClass(NotificationChannel.class);
+ verify(mNotificationManager).createNotificationChannel(nc.capture());
+
+ assertThat(nc.getValue().getId()).isEqualTo(ENABLE_MMS_NOTIFICATION_CHANNEL);
+ assertThat(nc.getValue().getName()).isEqualTo(mFakeNotificationChannelTitle);
+ assertThat(nc.getValue().getImportance()).isEqualTo(IMPORTANCE_HIGH);
+
+ // Capture the notification it notifies and verify its fields.
+ ArgumentCaptor<Notification> notification = ArgumentCaptor.forClass(Notification.class);
+ verify(mNotificationManager).notify(
+ eq(ENABLE_MMS_NOTIFICATION_ID), notification.capture());
+ assertThat(notification.getValue().extras.getCharSequence(Notification.EXTRA_TITLE))
+ .isEqualTo(mFakeNotificationTitle);
+ assertThat(notification.getValue().extras.getCharSequence(Notification.EXTRA_BIG_TEXT))
+ .isEqualTo(mFakeNotificationSummary);
+ assertThat(notification.getValue().contentIntent).isNotNull();
+ }
+
+ @Test
+ public void onReceiveEnableMms_NoExtra_notificationShouldNotSend() {
+ Intent intent = new Intent(Settings.ACTION_ENABLE_MMS_DATA_REQUEST);
+
+ // EXTRA_SUB_ID and EXTRA_ENABLE_MMS_DATA_REQUEST_REASON are required.
+ mSimSelectNotification.onReceive(mContext, intent);
+ verify(mNotificationManager, never()).createNotificationChannel(any());
+ }
+
+ @Test
+ public void onReceiveEnableMms_MmsDataAlreadyEnabled_notificationShouldNotSend() {
+ when(mTelephonyManager.isDataEnabledForApn(TYPE_MMS)).thenReturn(true);
+ Intent intent = new Intent(Settings.ACTION_ENABLE_MMS_DATA_REQUEST);
+ intent.putExtra(EXTRA_SUB_ID, mSubId);
+ intent.putExtra(EXTRA_ENABLE_MMS_DATA_REQUEST_REASON,
+ ENABLE_MMS_DATA_REQUEST_REASON_INCOMING_MMS);
+
+ // If MMS data is already enabled, there's no need to trigger the notification.
+ mSimSelectNotification.onReceive(mContext, intent);
+ verify(mNotificationManager, never()).createNotificationChannel(any());
+ }
+}
+
diff --git a/tests/robotests/src/com/android/settings/sound/MediaOutputPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/sound/MediaOutputPreferenceControllerTest.java
index 7fcd3d2..51264c1 100644
--- a/tests/robotests/src/com/android/settings/sound/MediaOutputPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/sound/MediaOutputPreferenceControllerTest.java
@@ -109,7 +109,7 @@
private BluetoothDevice mRightBluetoothHapDevice;
private LocalBluetoothManager mLocalBluetoothManager;
private MediaOutputPreferenceController mController;
- private List<BluetoothDevice> mProfileConnectableDevices;
+ private List<BluetoothDevice> mProfileConnectedDevices;
private List<BluetoothDevice> mHearingAidActiveDevices;
@Before
@@ -150,7 +150,7 @@
mController = new MediaOutputPreferenceController(mContext, TEST_KEY);
mScreen = spy(new PreferenceScreen(mContext, null));
mPreference = new Preference(mContext);
- mProfileConnectableDevices = new ArrayList<>();
+ mProfileConnectedDevices = new ArrayList<>();
mHearingAidActiveDevices = new ArrayList<>(2);
when(mScreen.getPreferenceManager()).thenReturn(mock(PreferenceManager.class));
@@ -172,11 +172,11 @@
* Preference should be invisible
*/
@Test
- public void updateState_withoutConnectableBtDevice_preferenceInvisible() {
+ public void updateState_withoutConnectedBtDevice_preferenceInvisible() {
mShadowAudioManager.setOutputDevice(DEVICE_OUT_EARPIECE);
mAudioManager.setMode(AudioManager.MODE_NORMAL);
- mProfileConnectableDevices.clear();
- when(mA2dpProfile.getConnectableDevices()).thenReturn(mProfileConnectableDevices);
+ mProfileConnectedDevices.clear();
+ when(mA2dpProfile.getConnectedDevices()).thenReturn(mProfileConnectedDevices);
mPreference.setVisible(true);
assertThat(mPreference.isVisible()).isTrue();
@@ -185,16 +185,16 @@
}
/**
- * A2DP Bluetooth device(s) are connectable, no matter active or inactive
+ * A2DP Bluetooth device(s) are connected, no matter active or inactive
* Preference should be visible
*/
@Test
- public void updateState_withConnectableBtDevice_preferenceVisible() {
+ public void updateState_withConnectedBtDevice_preferenceVisible() {
mShadowAudioManager.setOutputDevice(DEVICE_OUT_BLUETOOTH_A2DP);
mAudioManager.setMode(AudioManager.MODE_NORMAL);
- mProfileConnectableDevices.clear();
- mProfileConnectableDevices.add(mBluetoothDevice);
- when(mA2dpProfile.getConnectableDevices()).thenReturn(mProfileConnectableDevices);
+ mProfileConnectedDevices.clear();
+ mProfileConnectedDevices.add(mBluetoothDevice);
+ when(mA2dpProfile.getConnectedDevices()).thenReturn(mProfileConnectedDevices);
assertThat(mPreference.isVisible()).isFalse();
// Without Active Bluetooth Device
@@ -208,17 +208,17 @@
}
/**
- * A2DP Bluetooth device(s) are connectable, but no device is set as activated
+ * A2DP Bluetooth device(s) are connected, but no device is set as activated
* Preference summary should be "This device"
*/
@Test
- public void updateState_withConnectableBtDevice_withoutActiveBtDevice_setDefaultSummary() {
+ public void updateState_withConnectedBtDevice_withoutActiveBtDevice_setDefaultSummary() {
mShadowAudioManager.setOutputDevice(DEVICE_OUT_EARPIECE);
mAudioManager.setMode(AudioManager.MODE_NORMAL);
- mProfileConnectableDevices.clear();
- mProfileConnectableDevices.add(mBluetoothDevice);
- mProfileConnectableDevices.add(mSecondBluetoothDevice);
- when(mA2dpProfile.getConnectableDevices()).thenReturn(mProfileConnectableDevices);
+ mProfileConnectedDevices.clear();
+ mProfileConnectedDevices.add(mBluetoothDevice);
+ mProfileConnectedDevices.add(mSecondBluetoothDevice);
+ when(mA2dpProfile.getConnectedDevices()).thenReturn(mProfileConnectedDevices);
when(mA2dpProfile.getActiveDevice()).thenReturn(null);
assertThat(mPreference.getSummary()).isNull();
@@ -235,10 +235,10 @@
public void updateState_withActiveBtDevice_setActivatedDeviceName() {
mShadowAudioManager.setOutputDevice(DEVICE_OUT_BLUETOOTH_A2DP);
mAudioManager.setMode(AudioManager.MODE_NORMAL);
- mProfileConnectableDevices.clear();
- mProfileConnectableDevices.add(mBluetoothDevice);
- mProfileConnectableDevices.add(mSecondBluetoothDevice);
- when(mA2dpProfile.getConnectableDevices()).thenReturn(mProfileConnectableDevices);
+ mProfileConnectedDevices.clear();
+ mProfileConnectedDevices.add(mBluetoothDevice);
+ mProfileConnectedDevices.add(mSecondBluetoothDevice);
+ when(mA2dpProfile.getConnectedDevices()).thenReturn(mProfileConnectedDevices);
when(mA2dpProfile.getActiveDevice()).thenReturn(mBluetoothDevice);
assertThat(mPreference.getSummary()).isNull();
@@ -248,16 +248,16 @@
/**
- * Hearing Aid device(s) are connectable, no matter active or inactive
+ * Hearing Aid device(s) are connected, no matter active or inactive
* Preference should be visible
*/
@Test
- public void updateState_withConnectableHADevice_preferenceVisible() {
+ public void updateState_withConnectedHADevice_preferenceVisible() {
mShadowAudioManager.setOutputDevice(DEVICE_OUT_HEARING_AID);
mAudioManager.setMode(AudioManager.MODE_NORMAL);
mHearingAidActiveDevices.clear();
mHearingAidActiveDevices.add(mLeftBluetoothHapDevice);
- when(mHearingAidProfile.getConnectableDevices()).thenReturn(mHearingAidActiveDevices);
+ when(mHearingAidProfile.getConnectedDevices()).thenReturn(mHearingAidActiveDevices);
assertThat(mPreference.isVisible()).isFalse();
// Without Active Hearing Aid Device
@@ -280,7 +280,7 @@
mAudioManager.setMode(AudioManager.MODE_NORMAL);
mHearingAidActiveDevices.clear();
mHearingAidActiveDevices.add(mLeftBluetoothHapDevice);
- when(mHearingAidProfile.getConnectableDevices()).thenReturn(mHearingAidActiveDevices);
+ when(mHearingAidProfile.getConnectedDevices()).thenReturn(mHearingAidActiveDevices);
when(mHearingAidProfile.getActiveDevices()).thenReturn(mHearingAidActiveDevices);
assertThat(mPreference.getSummary()).isNull();
@@ -332,22 +332,6 @@
mContext.getText(R.string.media_out_summary_ongoing_call_state));
}
- /**
- * Media stream is captured by something else (cast device):
- * Preference should be invisible
- * Preference summary should be "unavailable"
- */
- @Test
- public void updateState_mediaStreamIsCapturedByCast_shouldDisableAndSetDefaultSummary() {
- mShadowAudioManager.setOutputDevice(DEVICE_OUT_REMOTE_SUBMIX);
-
- mController.updateState(mPreference);
-
- assertThat(mPreference.isVisible()).isFalse();
- String defaultString = mContext.getString(R.string.media_output_summary_unavailable);
- assertThat(mPreference.getSummary()).isEqualTo(defaultString);
- }
-
@Test
public void findActiveDevice_onlyA2dpDeviceActive_returnA2dpDevice() {
when(mLocalBluetoothProfileManager.getHearingAidProfile()).thenReturn(null);