Snap for 8535289 from 6408e8391b to tm-qpr1-release

Change-Id: I684550d1a1f7c500e07975fc8fd3cde4935c6c0b
This commit is contained in:
Android Build Coastguard Worker
2022-05-04 03:08:45 +00:00
23 changed files with 719 additions and 259 deletions

View File

@@ -4124,7 +4124,7 @@
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
<meta-data android:name="com.android.settings.FRAGMENT_CLASS"
android:value="com.android.settings.bluetooth.BluetoothBroadcastsDialog" />
android:value="com.android.settings.bluetooth.BluetoothBroadcastDialog" />
<meta-data android:name="com.android.settings.PRIMARY_PROFILE_CONTROLLED"
android:value="true" />
</activity>

View File

@@ -1,44 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (C) 2022 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.
-->
<ripple
xmlns:android="http://schemas.android.com/apk/res/android"
android:color="@color/settingslib_ripple_color">
<item android:id="@android:id/background">
<layer-list android:paddingMode="stack"
android:paddingStart="0dp"
android:paddingEnd="48dp"
android:paddingLeft="0dp"
android:paddingRight="0dp">
<item>
<shape>
<corners android:radius="28dp"/>
<solid android:color="@android:color/system_accent1_100"/>
<size android:height="@dimen/settingslib_spinner_height"/>
</shape>
</item>
<item
android:gravity="center|end"
android:width="18dp"
android:height="18dp"
android:end="12dp"
android:drawable="@drawable/settingslib_arrow_drop_down"/>
</layer-list>
</item>
</ripple>

View File

@@ -39,7 +39,6 @@
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_gravity="center_vertical"
android:entries="@array/bytes_picker_sizes"
style="@style/DataByteEditorSpinner"/>
android:entries="@array/bytes_picker_sizes" />
</LinearLayout>

View File

@@ -14137,6 +14137,8 @@
<!-- BT LE Audio Device: Media Broadcast -->
<!-- The title of the Media Broadcast Dialog [CHAR LIMIT=none] -->
<string name="bluetooth_broadcast_dialog_title">Broadcast</string>
<!-- [CHAR LIMIT=NONE] Le audio broadcast dialog, switch to others app. -->
<string name="bluetooth_broadcast_dialog_broadcast_app">Broadcast <xliff:g id="currentApp" example="App Name 2">%1$s</xliff:g></string>
<!-- The message of the Media Broadcast Dialog for finding broadcast [CHAR LIMIT=none] -->
<string name="bluetooth_broadcast_dialog_find_message">Listen to broadcasts that are playing near you</string>
<!-- The message of the Media Broadcast Dialog for broadcast [CHAR LIMIT=none] -->

View File

@@ -952,11 +952,6 @@
<item name="contentPadding">@dimen/dream_item_content_padding</item>
</style>
<style name="DataByteEditorSpinner" parent="@style/Spinner.SettingsLib">
<item name="android:background">@drawable/data_bytes_editor_spinner_background</item>
<item name="android:dropDownVerticalOffset">36dp</item>
</style>
<style name="BroadcastActionButton" parent="@android:style/Widget.Material.Button">
<item name="android:background">@drawable/broadcast_button_outline</item>
<item name="android:textColor">?android:attr/textColorPrimary</item>

View File

@@ -649,6 +649,11 @@
android:title="@string/enable_non_resizable_multi_window"
android:summary="@string/enable_non_resizable_multi_window_summary" />
<SwitchPreference
android:key="back_navigation_animation"
android:title="@string/back_navigation_animation"
android:summary="@string/back_navigation_animation_summary" />
<Preference
android:key="reset_shortcut_manager_throttling"
android:title="@string/reset_shortcut_manager_throttling" />

View File

@@ -19,27 +19,30 @@ package com.android.settings.applications.managedomainurls;
import android.content.Context;
import android.content.pm.verify.domain.DomainVerificationManager;
import android.content.pm.verify.domain.DomainVerificationUserState;
import android.util.IconDrawableFactory;
import android.graphics.drawable.Drawable;
import androidx.preference.PreferenceViewHolder;
import com.android.settings.R;
import com.android.settings.applications.intentpicker.IntentPickerUtils;
import com.android.settingslib.applications.AppUtils;
import com.android.settingslib.applications.ApplicationsState.AppEntry;
import com.android.settingslib.utils.ThreadUtils;
import com.android.settingslib.widget.AppPreference;
public class DomainAppPreference extends AppPreference {
private Drawable mCacheIcon;
private final AppEntry mEntry;
private final DomainVerificationManager mDomainVerificationManager;
private final IconDrawableFactory mIconDrawableFactory;
public DomainAppPreference(final Context context, IconDrawableFactory iconFactory,
AppEntry entry) {
public DomainAppPreference(final Context context, AppEntry entry) {
super(context);
mIconDrawableFactory = iconFactory;
mDomainVerificationManager = context.getSystemService(DomainVerificationManager.class);
mEntry = entry;
mEntry.ensureLabel(getContext());
mCacheIcon = AppUtils.getIconFromCache(mEntry);
setState();
}
@@ -54,7 +57,12 @@ public class DomainAppPreference extends AppPreference {
private void setState() {
setTitle(mEntry.label);
setIcon(mIconDrawableFactory.getBadgedIcon(mEntry.info));
if (mCacheIcon != null) {
setIcon(mCacheIcon);
} else {
setIcon(R.drawable.empty_icon);
}
setSummary(getDomainsSummary(mEntry.info.packageName));
}
@@ -69,4 +77,18 @@ public class DomainAppPreference extends AppPreference {
packageName);
return userState == null ? false : userState.isLinkHandlingAllowed();
}
@Override
public void onBindViewHolder(PreferenceViewHolder view) {
if (mCacheIcon == null) {
ThreadUtils.postOnBackgroundThread(() -> {
final Drawable icon = AppUtils.getIcon(getContext(), mEntry);
ThreadUtils.postOnMainThread(() -> {
setIcon(icon);
mCacheIcon = icon;
});
});
}
super.onBindViewHolder(view);
}
}

View File

@@ -20,7 +20,6 @@ import android.app.Application;
import android.content.Context;
import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.IconDrawableFactory;
import androidx.preference.Preference;
import androidx.preference.PreferenceGroup;
@@ -30,6 +29,7 @@ import com.android.settings.R;
import com.android.settings.applications.AppInfoBase;
import com.android.settings.applications.intentpicker.AppLaunchSettings;
import com.android.settings.core.BasePreferenceController;
import com.android.settingslib.applications.AppUtils;
import com.android.settingslib.applications.ApplicationsState;
import com.android.settingslib.applications.ApplicationsState.AppEntry;
@@ -98,6 +98,10 @@ public class DomainAppPreferenceController extends BasePreferenceController impl
if (mContext == null) {
return;
}
// Preload top visible icons of app list.
AppUtils.preloadTopIcons(mContext, apps,
mContext.getResources().getInteger(R.integer.config_num_visible_app_icons));
rebuildAppList(mDomainAppList, apps);
}
@@ -157,13 +161,12 @@ public class DomainAppPreferenceController extends BasePreferenceController impl
cacheAllPrefs(group);
final int size = apps.size();
final Context context = group.getContext();
final IconDrawableFactory iconDrawableFactory = IconDrawableFactory.newInstance(context);
for (int i = 0; i < size; i++) {
final AppEntry entry = apps.get(i);
final String key = entry.info.packageName + "|" + entry.info.uid;
DomainAppPreference preference = (DomainAppPreference) getCachedPreference(key);
if (preference == null) {
preference = new DomainAppPreference(context, iconDrawableFactory, entry);
preference = new DomainAppPreference(context, entry);
preference.setKey(key);
group.addPreference(preference);
} else {

View File

@@ -24,6 +24,7 @@ import android.content.Context;
import com.android.settings.R;
import com.android.settings.dashboard.DashboardFragment;
import com.android.settings.search.BaseSearchIndexProvider;
import com.android.settingslib.applications.AppIconCacheManager;
import com.android.settingslib.search.SearchIndexable;
/**
@@ -58,4 +59,10 @@ public class ManageDomainUrls extends DashboardFragment {
public static final BaseSearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
new BaseSearchIndexProvider(R.xml.manage_domain_url_settings);
@Override
public void onDestroyView() {
super.onDestroyView();
AppIconCacheManager.getInstance().release();
}
}

View File

@@ -19,105 +19,93 @@ package com.android.settings.bluetooth;
import android.app.Dialog;
import android.app.settings.SettingsEnums;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.text.TextUtils;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.ListView;
import android.widget.Button;
import android.widget.TextView;
import androidx.appcompat.app.AlertDialog;
import com.android.settings.R;
import com.android.settings.core.SubSettingLauncher;
import com.android.settings.core.instrumentation.InstrumentedDialogFragment;
import java.util.ArrayList;
import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcast;
import com.android.settingslib.bluetooth.LocalBluetoothManager;
import com.android.settingslib.media.MediaOutputConstants;
/**
* This Dialog allowed users to do some actions for broadcast media or find the
* nearby broadcast sources.
*/
public class BluetoothBroadcastDialog extends InstrumentedDialogFragment {
public static final String KEY_APP_LABEL = "app_label";
public static final String KEY_DEVICE_ADDRESS =
BluetoothFindBroadcastsFragment.KEY_DEVICE_ADDRESS;
private static final String TAG = "BTBroadcastsDialog";
private static final CharSequence UNKNOWN_APP_LABEL = "unknown";
private Context mContext;
private CharSequence mCurrentAppLabel = UNKNOWN_APP_LABEL;
private String mDeviceAddress;
private LocalBluetoothManager mLocalBluetoothManager;
private AlertDialog mAlertDialog;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mContext = getActivity();
mCurrentAppLabel = getActivity().getIntent().getCharSequenceExtra(KEY_APP_LABEL);
mDeviceAddress = getActivity().getIntent().getStringExtra(KEY_DEVICE_ADDRESS);
mLocalBluetoothManager = Utils.getLocalBtManager(mContext);
setShowsDialog(true);
}
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
final Context context = getActivity();
final boolean isMediaPlaying = isMediaPlaying();
View layout = View.inflate(mContext,
com.android.settingslib.R.layout.broadcast_dialog, null);
final AlertDialog.Builder builder = new AlertDialog.Builder(context);
builder.setTitle(isMediaPlaying ? R.string.bluetooth_find_broadcast
: R.string.bluetooth_broadcast_dialog_title);
builder.setMessage(isMediaPlaying ? R.string.bluetooth_broadcast_dialog_find_message
: R.string.bluetooth_broadcast_dialog_broadcast_message);
TextView title = layout.findViewById(com.android.settingslib.R.id.dialog_title);
TextView subTitle = layout.findViewById(com.android.settingslib.R.id.dialog_subtitle);
title.setText(mContext.getString(R.string.bluetooth_broadcast_dialog_title));
subTitle.setText(
mContext.getString(R.string.bluetooth_broadcast_dialog_broadcast_message));
ArrayList<String> optionList = new ArrayList<String>();
if (!isMediaPlaying) {
optionList.add(context.getString(R.string.bluetooth_broadcast_dialog_title));
}
optionList.add(context.getString(R.string.bluetooth_find_broadcast));
optionList.add(context.getString(android.R.string.cancel));
View content = LayoutInflater.from(context).inflate(
R.layout.sim_confirm_dialog_multiple_enabled_profiles_supported, null);
if (content != null) {
Log.i(TAG, "list =" + optionList.toString());
final ArrayAdapter<String> arrayAdapterItems = new ArrayAdapter<String>(
context,
R.layout.sim_confirm_dialog_item_multiple_enabled_profiles_supported,
optionList);
final ListView lvItems = content.findViewById(R.id.carrier_list);
if (lvItems != null) {
lvItems.setVisibility(View.VISIBLE);
lvItems.setAdapter(arrayAdapterItems);
lvItems.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position,
long id) {
Log.i(TAG, "list onClick =" + position);
Log.i(TAG, "list item =" + optionList.get(position));
if (position == optionList.size() - 1) {
// The last position in the options is the Cancel button. So when
// the user clicks the button, we do nothing but dismiss the dialog.
dismiss();
} else {
if (optionList.get(position).equals(
context.getString(R.string.bluetooth_find_broadcast))) {
launchFindBroadcastsActivity();
} else {
launchMediaOutputBroadcastDialog();
}
}
}
});
}
builder.setView(content);
Button broadcastBtn = layout.findViewById(com.android.settingslib.R.id.positive_btn);
if (TextUtils.isEmpty(mCurrentAppLabel)) {
broadcastBtn.setText(mContext.getString(R.string.bluetooth_broadcast_dialog_title));
} else {
Log.i(TAG, "optionList is empty");
broadcastBtn.setText(mContext.getString(
R.string.bluetooth_broadcast_dialog_broadcast_app,
String.valueOf(mCurrentAppLabel)));
}
broadcastBtn.setOnClickListener((view) -> {
launchMediaOutputBroadcastDialog();
});
AlertDialog dialog = builder.create();
dialog.setCanceledOnTouchOutside(false);
return dialog;
Button findBroadcastBtn = layout.findViewById(com.android.settingslib.R.id.negative_btn);
findBroadcastBtn.setText(mContext.getString(R.string.bluetooth_find_broadcast));
findBroadcastBtn.setOnClickListener((view) -> {
launchFindBroadcastsActivity();
});
Button cancelBtn = layout.findViewById(com.android.settingslib.R.id.neutral_btn);
cancelBtn.setOnClickListener((view) -> {
dismiss();
getActivity().finish();
});
mAlertDialog = new AlertDialog.Builder(mContext,
com.android.settingslib.R.style.Theme_AlertDialog_SettingsLib)
.setView(layout)
.create();
return mAlertDialog;
}
private boolean isMediaPlaying() {
return true;
}
@Override
public void onStart() {
super.onStart();
@@ -130,10 +118,55 @@ public class BluetoothBroadcastDialog extends InstrumentedDialogFragment {
}
private void launchFindBroadcastsActivity() {
Bundle bundle = new Bundle();
bundle.putString(KEY_DEVICE_ADDRESS, mDeviceAddress);
new SubSettingLauncher(mContext)
.setTitleRes(R.string.bluetooth_find_broadcast_title)
.setDestination(BluetoothFindBroadcastsFragment.class.getName())
.setArguments(bundle)
.setSourceMetricsCategory(SettingsEnums.PAGE_UNKNOWN)
.launch();
dismissVolumePanel();
}
private void launchMediaOutputBroadcastDialog() {
if (startBroadcast()) {
mContext.sendBroadcast(new Intent()
.setPackage(MediaOutputConstants.SYSTEMUI_PACKAGE_NAME)
.setAction(MediaOutputConstants.ACTION_LAUNCH_MEDIA_OUTPUT_BROADCAST_DIALOG)
.putExtra(MediaOutputConstants.EXTRA_PACKAGE_NAME,
getActivity().getPackageName()));
dismissVolumePanel();
}
}
private LocalBluetoothLeBroadcast getLEAudioBroadcastProfile() {
if (mLocalBluetoothManager != null && mLocalBluetoothManager.getProfileManager() != null) {
LocalBluetoothLeBroadcast bluetoothLeBroadcast =
mLocalBluetoothManager.getProfileManager().getLeAudioBroadcastProfile();
if (bluetoothLeBroadcast != null) {
return bluetoothLeBroadcast;
}
}
Log.d(TAG, "Can not get LE Audio Broadcast Profile");
return null;
}
private boolean startBroadcast() {
LocalBluetoothLeBroadcast btLeBroadcast = getLEAudioBroadcastProfile();
if (btLeBroadcast != null) {
btLeBroadcast.startBroadcast(String.valueOf(mCurrentAppLabel), null);
return true;
}
Log.d(TAG, "Can not broadcast successfully");
return false;
}
private void dismissVolumePanel() {
// Dismiss volume panel
mContext.sendBroadcast(new Intent()
.setPackage(MediaOutputConstants.SETTINGS_PACKAGE_NAME)
.setAction(MediaOutputConstants.ACTION_CLOSE_PANEL));
}
}

View File

@@ -0,0 +1,89 @@
/*
* Copyright (C) 2017 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.development;
import android.content.Context;
import android.provider.Settings;
import androidx.preference.Preference;
import androidx.preference.SwitchPreference;
import com.android.internal.annotations.VisibleForTesting;
import com.android.settings.core.PreferenceControllerMixin;
import com.android.settingslib.development.DeveloperOptionsPreferenceController;
import java.util.Objects;
/**
* PreferenceController for enabling/disabling animation related to back button and back gestures.
*/
public class BackAnimationPreferenceController extends DeveloperOptionsPreferenceController
implements Preference.OnPreferenceChangeListener, PreferenceControllerMixin {
private static final String BACK_NAVIGATION_ANIMATION_KEY =
"back_navigation_animation";
private static final int SETTING_VALUE_OFF = 0;
private static final int SETTING_VALUE_ON = 1;
private final DevelopmentSettingsDashboardFragment mFragment;
@VisibleForTesting
BackAnimationPreferenceController(Context context) {
super(context);
mFragment = null;
}
public BackAnimationPreferenceController(Context context,
DevelopmentSettingsDashboardFragment fragment) {
super(context);
Objects.requireNonNull(fragment);
mFragment = fragment;
}
@Override
public String getPreferenceKey() {
return BACK_NAVIGATION_ANIMATION_KEY;
}
@Override
public boolean onPreferenceChange(Preference preference, Object newValue) {
final boolean isEnabled = (Boolean) newValue;
Settings.Global.putInt(mContext.getContentResolver(),
Settings.Global.ENABLE_BACK_ANIMATION,
isEnabled ? SETTING_VALUE_ON : SETTING_VALUE_OFF);
if (mFragment != null && isEnabled) {
BackAnimationPreferenceDialog.show(mFragment);
}
return true;
}
@Override
public void updateState(Preference preference) {
final int mode = Settings.Global.getInt(mContext.getContentResolver(),
Settings.Global.ENABLE_BACK_ANIMATION, SETTING_VALUE_OFF);
((SwitchPreference) mPreference).setChecked(mode != SETTING_VALUE_OFF);
}
@Override
protected void onDeveloperOptionsSwitchDisabled() {
super.onDeveloperOptionsSwitchDisabled();
Settings.Global.putInt(mContext.getContentResolver(),
Settings.Global.ENABLE_BACK_ANIMATION, SETTING_VALUE_OFF);
((SwitchPreference) mPreference).setChecked(false);
}
}

View File

@@ -0,0 +1,79 @@
/*
* Copyright (C) 2022 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.development;
import android.app.AlertDialog;
import android.app.Dialog;
import android.app.settings.SettingsEnums;
import android.content.DialogInterface;
import android.os.Bundle;
import androidx.annotation.NonNull;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentActivity;
import androidx.fragment.app.FragmentManager;
import com.android.settings.R;
import com.android.settings.core.instrumentation.InstrumentedDialogFragment;
/**
* Information dialog shown when enabling back animations
*/
public class BackAnimationPreferenceDialog extends InstrumentedDialogFragment
implements DialogInterface.OnClickListener {
public static final String TAG = "BackAnimationDlg";
private BackAnimationPreferenceDialog() {
}
/**
* Show this dialog.
*/
public static void show(@NonNull Fragment host) {
FragmentActivity activity = host.getActivity();
if (activity == null) {
return;
}
FragmentManager manager = activity.getSupportFragmentManager();
if (manager.findFragmentByTag(TAG) == null) {
BackAnimationPreferenceDialog dialog = new BackAnimationPreferenceDialog();
dialog.setTargetFragment(host, 0 /* requestCode */);
dialog.show(manager, TAG);
}
}
@Override
public int getMetricsCategory() {
return SettingsEnums.DIALOG_BACK_ANIMATIONS;
}
@Override
@NonNull
public Dialog onCreateDialog(Bundle savedInstanceState) {
return new AlertDialog.Builder(getActivity())
.setTitle(R.string.back_navigation_animation)
.setMessage(R.string.back_navigation_animation_dialog)
.setPositiveButton(android.R.string.ok, this /* onClickListener */)
.create();
}
@Override
public void onClick(DialogInterface dialog, int which) {
// Do nothing
}
}

View File

@@ -607,6 +607,7 @@ public class DevelopmentSettingsDashboardFragment extends RestrictedDashboardFra
controllers.add(new OverlaySettingsPreferenceController(context));
controllers.add(new StylusHandwritingPreferenceController(context));
controllers.add(new IngressRateLimitPreferenceController((context)));
controllers.add(new BackAnimationPreferenceController(context, fragment));
return controllers;
}

View File

@@ -161,7 +161,7 @@ public class MediaOutputIndicatorWorker extends SliceBackgroundWorker implements
return mLocalMediaManager.getCurrentConnectedDevice();
}
String getPackageName() {
public String getPackageName() {
return mPackageName;
}

View File

@@ -17,6 +17,7 @@ package com.android.settings.network;
import static android.provider.SettingsSlicesContract.KEY_AIRPLANE_MODE;
import android.app.Activity;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
@@ -54,7 +55,6 @@ public class AirplaneModePreferenceController extends TogglePreferenceController
.appendPath(SettingsSlicesContract.PATH_SETTING_ACTION)
.appendPath(SettingsSlicesContract.KEY_AIRPLANE_MODE)
.build();
private static final String EXIT_ECM_RESULT = "exit_ecm_result";
private Fragment mFragment;
private AirplaneModeEnabler mAirplaneModeEnabler;
@@ -147,7 +147,7 @@ public class AirplaneModePreferenceController extends TogglePreferenceController
public void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == REQUEST_CODE_EXIT_ECM) {
final boolean isChoiceYes = data.getBooleanExtra(EXIT_ECM_RESULT, false);
final boolean isChoiceYes = (resultCode == Activity.RESULT_OK);
// Set Airplane mode based on the return value and checkbox state
mAirplaneModeEnabler.setAirplaneModeInECM(isChoiceYes,
mAirplaneModePreference.isChecked());

View File

@@ -20,6 +20,7 @@ import static com.android.settings.network.MobilePlanPreferenceController.MANAGE
import android.app.Dialog;
import android.app.settings.SettingsEnums;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.provider.SearchIndexableResource;
import android.util.Log;
@@ -29,6 +30,7 @@ import androidx.fragment.app.Fragment;
import com.android.settings.R;
import com.android.settings.Utils;
import com.android.settings.core.OnActivityResultListener;
import com.android.settings.dashboard.DashboardFragment;
import com.android.settings.network.MobilePlanPreferenceController.MobilePlanPreferenceHost;
import com.android.settings.search.BaseSearchIndexProvider;
@@ -44,7 +46,7 @@ import java.util.List;
@SearchIndexable
public class NetworkDashboardFragment extends DashboardFragment implements
MobilePlanPreferenceHost {
MobilePlanPreferenceHost, OnActivityResultListener {
private static final String TAG = "NetworkDashboardFrag";
@@ -152,6 +154,17 @@ public class NetworkDashboardFragment extends DashboardFragment implements
return 0;
}
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
switch (requestCode) {
case AirplaneModePreferenceController.REQUEST_CODE_EXIT_ECM:
use(AirplaneModePreferenceController.class)
.onActivityResult(requestCode, resultCode, data);
break;
}
}
public static final BaseSearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
new BaseSearchIndexProvider(R.xml.network_provider_internet) {
@Override

View File

@@ -467,6 +467,9 @@ public class ApnSettings extends RestrictedSettingsFragment
}
private boolean restoreDefaultApn() {
// Callback of data connection change could be some noise during the stage of restore.
mTelephonyManager.listen(mPhoneStateListener, PhoneStateListener.LISTEN_NONE);
showDialog(DIALOG_RESTORE_DEFAULTAPN);
mRestoreDefaultApnMode = true;
@@ -517,6 +520,7 @@ public class ApnSettings extends RestrictedSettingsFragment
getResources().getString(
R.string.restore_default_apn_completed),
Toast.LENGTH_LONG).show();
restartPhoneStateListener(mSubId);
break;
}
}

View File

@@ -80,9 +80,6 @@ public class EnabledNetworkModePreferenceController extends
@Override
public int getAvailabilityStatus(int subId) {
boolean visible;
if (!isCallStateIdle()) {
return AVAILABLE_UNSEARCHABLE;
}
final PersistableBundle carrierConfig = mCarrierConfigCache.getConfigForSubId(subId);
if (subId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
@@ -97,6 +94,8 @@ public class EnabledNetworkModePreferenceController extends
visible = false;
} else if (carrierConfig.getBoolean(CarrierConfigManager.KEY_WORLD_PHONE_BOOL)) {
visible = false;
} else if (!isCallStateIdle()) {
return AVAILABLE_UNSEARCHABLE;
} else {
visible = true;
}

View File

@@ -31,9 +31,12 @@ import androidx.slice.builders.SliceAction;
import com.android.settings.R;
import com.android.settings.Utils;
import com.android.settings.bluetooth.BluetoothBroadcastDialog;
import com.android.settings.media.MediaOutputIndicatorWorker;
import com.android.settings.slices.CustomSliceRegistry;
import com.android.settings.slices.SliceBackgroundWorker;
import com.android.settingslib.bluetooth.CachedBluetoothDevice;
import com.android.settingslib.media.BluetoothMediaDevice;
import com.android.settingslib.media.MediaDevice;
import com.android.settingslib.media.MediaOutputConstants;
@@ -42,6 +45,9 @@ public class MediaVolumePreferenceController extends VolumeSeekBarPreferenceCont
private static final String KEY_MEDIA_VOLUME = "media_volume";
private MediaOutputIndicatorWorker mWorker;
private MediaDevice mMediaDevice;
private static final String ACTION_LAUNCH_BROADCAST_DIALOG =
"android.settings.MEDIA_BROADCAST_DIALOG";
public MediaVolumePreferenceController(Context context) {
super(context, KEY_MEDIA_VOLUME);
@@ -91,9 +97,9 @@ public class MediaVolumePreferenceController extends VolumeSeekBarPreferenceCont
}
private boolean isConnectedBLEDevice() {
final MediaDevice device = getWorker().getCurrentConnectedMediaDevice();
if (device != null) {
return device.isBLEDevice();
mMediaDevice = getWorker().getCurrentConnectedMediaDevice();
if (mMediaDevice != null) {
return mMediaDevice.isBLEDevice();
}
return false;
}
@@ -106,17 +112,32 @@ public class MediaVolumePreferenceController extends VolumeSeekBarPreferenceCont
}
final Intent intent = new Intent();
PendingIntent pi = null;
if (getWorker().isDeviceBroadcasting()) {
intent.setPackage(MediaOutputConstants.SYSTEMUI_PACKAGE_NAME);
intent.setAction(MediaOutputConstants.ACTION_LAUNCH_MEDIA_OUTPUT_BROADCAST_DIALOG);
intent.putExtra(MediaOutputConstants.EXTRA_PACKAGE_NAME,
getWorker().getActiveLocalMediaController().getPackageName());
pi = PendingIntent.getBroadcast(context, 0 /* requestCode */, intent,
PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_MUTABLE);
} else {
// TODO(b/229577518) : Get the intent action of the Bluetooth Broadcast Dialog
// for user to choose the action
final CachedBluetoothDevice bluetoothDevice =
((BluetoothMediaDevice) mMediaDevice).getCachedDevice();
if (bluetoothDevice == null) {
Log.d(TAG, "The bluetooth device is null");
return null;
}
intent.setAction(ACTION_LAUNCH_BROADCAST_DIALOG);
intent.putExtra(BluetoothBroadcastDialog.KEY_APP_LABEL,
Utils.getApplicationLabel(mContext, getWorker().getPackageName()));
intent.putExtra(BluetoothBroadcastDialog.KEY_DEVICE_ADDRESS,
bluetoothDevice.getAddress());
pi = PendingIntent.getActivity(context, 0 /* requestCode */, intent,
PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_MUTABLE);
}
final PendingIntent pi = PendingIntent.getBroadcast(context, 0 /* requestCode */, intent,
PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_MUTABLE);
final IconCompat icon = getBroadcastIcon(context);
return SliceAction.createDeeplink(pi, icon, ListBuilder.ICON_IMAGE, getPreferenceKey());

View File

@@ -30,6 +30,7 @@ import android.os.UserManager;
import android.util.FeatureFlagUtils;
import android.util.Log;
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
import com.android.settings.R;
@@ -41,6 +42,7 @@ import com.android.settings.widget.SettingsMainSwitchBar;
import com.android.settingslib.TetherUtil;
import com.android.settingslib.core.AbstractPreferenceController;
import com.android.settingslib.search.SearchIndexable;
import com.android.settingslib.wifi.WifiEnterpriseRestrictionUtils;
import java.util.ArrayList;
import java.util.List;
@@ -56,6 +58,8 @@ public class WifiTetherSettings extends RestrictedDashboardFragment
@VisibleForTesting
static final String KEY_WIFI_TETHER_NETWORK_NAME = "wifi_tether_network_name";
@VisibleForTesting
static final String KEY_WIFI_TETHER_SECURITY = "wifi_tether_security";
@VisibleForTesting
static final String KEY_WIFI_TETHER_NETWORK_PASSWORD = "wifi_tether_network_password";
@VisibleForTesting
static final String KEY_WIFI_TETHER_AUTO_OFF = "wifi_tether_auto_turn_off";
@@ -72,7 +76,7 @@ public class WifiTetherSettings extends RestrictedDashboardFragment
private WifiManager mWifiManager;
private boolean mRestartWifiApAfterConfigChange;
private boolean mUnavailable;
private WifiRestriction mWifiRestriction;
@VisibleForTesting
TetherChangeReceiver mTetherChangeReceiver;
@@ -82,6 +86,12 @@ public class WifiTetherSettings extends RestrictedDashboardFragment
public WifiTetherSettings() {
super(UserManager.DISALLOW_CONFIG_TETHERING);
mWifiRestriction = new WifiRestriction();
}
public WifiTetherSettings(WifiRestriction wifiRestriction) {
super(UserManager.DISALLOW_CONFIG_TETHERING);
mWifiRestriction = wifiRestriction;
}
@Override
@@ -98,9 +108,7 @@ public class WifiTetherSettings extends RestrictedDashboardFragment
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
setIfOnlyAvailableForAdmins(true);
if (isUiRestricted()) {
mUnavailable = true;
}
mUnavailable = isUiRestricted() || !mWifiRestriction.isHotspotAvailable(getContext());
}
@Override
@@ -135,6 +143,11 @@ public class WifiTetherSettings extends RestrictedDashboardFragment
@Override
public void onStart() {
super.onStart();
if (!mWifiRestriction.isHotspotAvailable(getContext())) {
getEmptyTextView().setText(R.string.not_allowed_by_ent);
getPreferenceScreen().removeAll();
return;
}
if (mUnavailable) {
if (!isUiRestrictedByOnlyAdmin()) {
getEmptyTextView().setText(R.string.tethering_settings_not_available);
@@ -228,36 +241,67 @@ public class WifiTetherSettings extends RestrictedDashboardFragment
use(WifiTetherMaximizeCompatibilityPreferenceController.class).updateDisplay();
}
public static final BaseSearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
new BaseSearchIndexProvider(R.xml.wifi_tether_settings) {
public static final SearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
new SearchIndexProvider(R.xml.wifi_tether_settings);
@Override
public List<String> getNonIndexableKeys(Context context) {
final List<String> keys = super.getNonIndexableKeys(context);
@VisibleForTesting
static class SearchIndexProvider extends BaseSearchIndexProvider {
if (!TetherUtil.isTetherAvailable(context)) {
keys.add(KEY_WIFI_TETHER_NETWORK_NAME);
keys.add(KEY_WIFI_TETHER_NETWORK_PASSWORD);
keys.add(KEY_WIFI_TETHER_AUTO_OFF);
keys.add(KEY_WIFI_TETHER_MAXIMIZE_COMPATIBILITY);
}
private final WifiRestriction mWifiRestriction;
// Remove duplicate
keys.add(KEY_WIFI_TETHER_SCREEN);
return keys;
}
SearchIndexProvider(int xmlRes) {
super(xmlRes);
mWifiRestriction = new WifiRestriction();
}
@Override
protected boolean isPageSearchEnabled(Context context) {
return !FeatureFlagUtils.isEnabled(context, FeatureFlags.TETHER_ALL_IN_ONE);
}
@VisibleForTesting
SearchIndexProvider(int xmlRes, WifiRestriction wifiRestriction) {
super(xmlRes);
mWifiRestriction = wifiRestriction;
}
@Override
public List<AbstractPreferenceController> createPreferenceControllers(
Context context) {
return buildPreferenceControllers(context, null /* listener */);
}
};
@Override
public List<String> getNonIndexableKeys(Context context) {
final List<String> keys = super.getNonIndexableKeys(context);
if (!mWifiRestriction.isTetherAvailable(context)
|| !mWifiRestriction.isHotspotAvailable(context)) {
keys.add(KEY_WIFI_TETHER_NETWORK_NAME);
keys.add(KEY_WIFI_TETHER_SECURITY);
keys.add(KEY_WIFI_TETHER_NETWORK_PASSWORD);
keys.add(KEY_WIFI_TETHER_AUTO_OFF);
keys.add(KEY_WIFI_TETHER_MAXIMIZE_COMPATIBILITY);
}
// Remove duplicate
keys.add(KEY_WIFI_TETHER_SCREEN);
return keys;
}
@Override
protected boolean isPageSearchEnabled(Context context) {
return !FeatureFlagUtils.isEnabled(context, FeatureFlags.TETHER_ALL_IN_ONE);
}
@Override
public List<AbstractPreferenceController> createPreferenceControllers(
Context context) {
return buildPreferenceControllers(context, null /* listener */);
}
}
@VisibleForTesting
static class WifiRestriction {
public boolean isTetherAvailable(@Nullable Context context) {
if (context == null) return true;
return TetherUtil.isTetherAvailable(context);
}
public boolean isHotspotAvailable(@Nullable Context context) {
if (context == null) return true;
return WifiEnterpriseRestrictionUtils.isWifiTetheringAllowed(context);
}
}
@VisibleForTesting
class TetherChangeReceiver extends BroadcastReceiver {

View File

@@ -29,7 +29,6 @@ import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.verify.domain.DomainVerificationManager;
import android.content.pm.verify.domain.DomainVerificationUserState;
import android.util.IconDrawableFactory;
import com.android.settings.R;
import com.android.settingslib.applications.ApplicationsState;
@@ -49,18 +48,14 @@ public class DomainAppPreferenceControllerTest {
private ApplicationsState.AppEntry mAppEntry;
private Context mContext;
private IconDrawableFactory mIconDrawableFactory;
@Mock
private DomainVerificationManager mDomainVerificationManager;
@Mock
private DomainVerificationUserState mDomainVerificationUserState;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mContext = spy(RuntimeEnvironment.application);
mIconDrawableFactory = IconDrawableFactory.newInstance(mContext);
mAppEntry = new ApplicationsState.AppEntry(
mContext, createApplicationInfo(mContext.getPackageName()), 0);
when(mContext.getSystemService(DomainVerificationManager.class)).thenReturn(
@@ -75,8 +70,7 @@ public class DomainAppPreferenceControllerTest {
doReturn(domainVerificationUserState).when(
mDomainVerificationManager).getDomainVerificationUserState(anyString());
doReturn(true).when(domainVerificationUserState).isLinkHandlingAllowed();
final DomainAppPreference pref = new DomainAppPreference(
mContext, mIconDrawableFactory, mAppEntry);
final DomainAppPreference pref = new DomainAppPreference(mContext, mAppEntry);
assertThat(pref.getLayoutResource()).isEqualTo(R.layout.preference_app);
}

View File

@@ -19,6 +19,7 @@ package com.android.settings.wifi.tether;
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.nullable;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn;
@@ -33,38 +34,41 @@ import android.net.ConnectivityManager;
import android.net.TetheringManager;
import android.net.wifi.WifiManager;
import android.os.Bundle;
import android.os.UserHandle;
import android.os.UserManager;
import android.util.FeatureFlagUtils;
import android.widget.TextView;
import androidx.fragment.app.FragmentActivity;
import androidx.preference.PreferenceScreen;
import androidx.test.core.app.ApplicationProvider;
import com.android.settings.core.FeatureFlags;
import com.android.settings.R;
import com.android.settings.testutils.FakeFeatureFactory;
import com.android.settings.testutils.shadow.ShadowFragment;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.mockito.Spy;
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Config;
import org.robolectric.util.ReflectionHelpers;
import java.util.ArrayList;
import java.util.List;
@RunWith(RobolectricTestRunner.class)
public class WifiTetherSettingsTest {
private static final int XML_RES = R.xml.wifi_tether_settings;
private static final String[] WIFI_REGEXS = {"wifi_regexs"};
private Context mContext;
private WifiTetherSettings mWifiTetherSettings;
@Rule
public final MockitoRule mMockitoRule = MockitoJUnit.rule();
@Spy
Context mContext = ApplicationProvider.getApplicationContext();
@Mock
private WifiManager mWifiManager;
@Mock
@@ -73,81 +77,55 @@ public class WifiTetherSettingsTest {
private UserManager mUserManager;
@Mock
private TetheringManager mTetheringManager;
@Mock
private WifiTetherSettings.WifiRestriction mWifiRestriction;
@Mock
private PreferenceScreen mPreferenceScreen;
@Mock
private TextView mEmptyTextView;
private WifiTetherSettings mWifiTetherSettings;
@Before
public void setUp() {
mContext = spy(RuntimeEnvironment.application);
MockitoAnnotations.initMocks(this);
doReturn(mWifiManager).when(mContext).getSystemService(WifiManager.class);
doReturn(mConnectivityManager)
.when(mContext).getSystemService(Context.CONNECTIVITY_SERVICE);
doReturn(mTetheringManager).when(mContext).getSystemService(Context.TETHERING_SERVICE);
doReturn(WIFI_REGEXS).when(mTetheringManager).getTetherableWifiRegexs();
doReturn(mUserManager).when(mContext).getSystemService(Context.USER_SERVICE);
when(mWifiRestriction.isTetherAvailable(mContext)).thenReturn(true);
when(mWifiRestriction.isHotspotAvailable(mContext)).thenReturn(true);
mWifiTetherSettings = new WifiTetherSettings();
}
@Test
public void wifiTetherNonIndexableKeys_tetherAvailable_keysNotReturned() {
FeatureFlagUtils.setEnabled(mContext, FeatureFlags.TETHER_ALL_IN_ONE, false);
// To let TetherUtil.isTetherAvailable return true, select one of the combinations
setupIsTetherAvailable(true);
final List<String> niks =
WifiTetherSettings.SEARCH_INDEX_DATA_PROVIDER.getNonIndexableKeys(mContext);
assertThat(niks).doesNotContain(WifiTetherSettings.KEY_WIFI_TETHER_NETWORK_NAME);
assertThat(niks).doesNotContain(WifiTetherSettings.KEY_WIFI_TETHER_NETWORK_PASSWORD);
assertThat(niks).doesNotContain(WifiTetherSettings.KEY_WIFI_TETHER_AUTO_OFF);
assertThat(niks).doesNotContain(WifiTetherSettings.KEY_WIFI_TETHER_MAXIMIZE_COMPATIBILITY);
}
@Test
public void wifiTetherNonIndexableKeys_tetherNotAvailable_keysReturned() {
// To let TetherUtil.isTetherAvailable return false, select one of the combinations
setupIsTetherAvailable(false);
final List<String> niks =
WifiTetherSettings.SEARCH_INDEX_DATA_PROVIDER.getNonIndexableKeys(mContext);
assertThat(niks).contains(WifiTetherSettings.KEY_WIFI_TETHER_NETWORK_NAME);
assertThat(niks).contains(WifiTetherSettings.KEY_WIFI_TETHER_NETWORK_PASSWORD);
assertThat(niks).contains(WifiTetherSettings.KEY_WIFI_TETHER_AUTO_OFF);
assertThat(niks).contains(WifiTetherSettings.KEY_WIFI_TETHER_MAXIMIZE_COMPATIBILITY);
}
@Test
public void createPreferenceControllers_notEmpty() {
assertThat(WifiTetherSettings.SEARCH_INDEX_DATA_PROVIDER.getPreferenceControllers(mContext))
.isNotEmpty();
mWifiTetherSettings = new WifiTetherSettings(mWifiRestriction);
}
@Test
@Config(shadows = ShadowFragment.class)
public void startFragment_notAdminUser_shouldRemoveAllPreferences() {
final WifiTetherSettings settings = spy(new WifiTetherSettings());
final FragmentActivity activity = mock(FragmentActivity.class);
when(settings.getActivity()).thenReturn(activity);
when(settings.getContext()).thenReturn(mContext);
final Resources.Theme theme = mContext.getTheme();
when(activity.getTheme()).thenReturn(theme);
when(activity.getSystemService(Context.USER_SERVICE)).thenReturn(mUserManager);
doNothing().when(settings)
.onCreatePreferences(any(Bundle.class), nullable(String.class));
final FakeFeatureFactory fakeFeatureFactory = FakeFeatureFactory.setupForTest();
ReflectionHelpers.setField(settings, "mDashboardFeatureProvider",
fakeFeatureFactory.dashboardFeatureProvider);
final TextView emptyTextView = mock(TextView.class);
ReflectionHelpers.setField(settings, "mEmptyTextView", emptyTextView);
final PreferenceScreen screen = mock(PreferenceScreen.class);
doReturn(screen).when(settings).getPreferenceScreen();
settings.onCreate(Bundle.EMPTY);
public void onStart_uiIsRestricted_removeAllPreferences() {
spyWifiTetherSettings();
settings.onStart();
mWifiTetherSettings.onStart();
verify(screen).removeAll();
verify(mPreferenceScreen).removeAll();
}
@Test
@Config(shadows = ShadowFragment.class)
public void onStart_hotspotNotAvailable_removeAllPreferences() {
spyWifiTetherSettings();
when(mWifiRestriction.isHotspotAvailable(mContext)).thenReturn(false);
mWifiTetherSettings.onStart();
verify(mPreferenceScreen).removeAll();
verify(mEmptyTextView).setText(anyInt());
}
@Test
public void createPreferenceControllers_getPreferenceControllersNotEmpty() {
assertThat(WifiTetherSettings.SEARCH_INDEX_DATA_PROVIDER.getPreferenceControllers(mContext))
.isNotEmpty();
}
@Test
@@ -159,19 +137,86 @@ public class WifiTetherSettingsTest {
.isEqualTo(1);
}
private void setupIsTetherAvailable(boolean returnValue) {
when(mConnectivityManager.isTetheringSupported()).thenReturn(true);
@Test
public void getNonIndexableKeys_tetherAvailable_keysNotReturned() {
when(mWifiRestriction.isTetherAvailable(mContext)).thenReturn(true);
when(mWifiRestriction.isHotspotAvailable(mContext)).thenReturn(true);
WifiTetherSettings.SearchIndexProvider searchIndexProvider =
new WifiTetherSettings.SearchIndexProvider(XML_RES, mWifiRestriction);
// For RestrictedLockUtils.checkIfRestrictionEnforced
final int userId = UserHandle.myUserId();
List<UserManager.EnforcingUser> enforcingUsers = new ArrayList<>();
when(mUserManager.getUserRestrictionSources(
UserManager.DISALLOW_CONFIG_TETHERING, UserHandle.of(userId)))
.thenReturn(enforcingUsers);
final List<String> keys = searchIndexProvider.getNonIndexableKeys(mContext);
// For RestrictedLockUtils.hasBaseUserRestriction
when(mUserManager.hasBaseUserRestriction(
UserManager.DISALLOW_CONFIG_TETHERING, UserHandle.of(userId)))
.thenReturn(!returnValue);
assertThat(keys).doesNotContain(WifiTetherSettings.KEY_WIFI_TETHER_NETWORK_NAME);
assertThat(keys).doesNotContain(WifiTetherSettings.KEY_WIFI_TETHER_SECURITY);
assertThat(keys).doesNotContain(WifiTetherSettings.KEY_WIFI_TETHER_NETWORK_PASSWORD);
assertThat(keys).doesNotContain(WifiTetherSettings.KEY_WIFI_TETHER_AUTO_OFF);
assertThat(keys).doesNotContain(WifiTetherSettings.KEY_WIFI_TETHER_MAXIMIZE_COMPATIBILITY);
}
@Test
public void getNonIndexableKeys_tetherNotAvailable_keysReturned() {
when(mWifiRestriction.isTetherAvailable(mContext)).thenReturn(false);
when(mWifiRestriction.isHotspotAvailable(mContext)).thenReturn(true);
WifiTetherSettings.SearchIndexProvider searchIndexProvider =
new WifiTetherSettings.SearchIndexProvider(XML_RES, mWifiRestriction);
final List<String> keys = searchIndexProvider.getNonIndexableKeys(mContext);
assertThat(keys).contains(WifiTetherSettings.KEY_WIFI_TETHER_NETWORK_NAME);
assertThat(keys).contains(WifiTetherSettings.KEY_WIFI_TETHER_SECURITY);
assertThat(keys).contains(WifiTetherSettings.KEY_WIFI_TETHER_NETWORK_PASSWORD);
assertThat(keys).contains(WifiTetherSettings.KEY_WIFI_TETHER_AUTO_OFF);
assertThat(keys).contains(WifiTetherSettings.KEY_WIFI_TETHER_MAXIMIZE_COMPATIBILITY);
}
@Test
public void getNonIndexableKeys_hotspotNotAvailable_keysReturned() {
when(mWifiRestriction.isTetherAvailable(mContext)).thenReturn(true);
when(mWifiRestriction.isHotspotAvailable(mContext)).thenReturn(false);
WifiTetherSettings.SearchIndexProvider searchIndexProvider =
new WifiTetherSettings.SearchIndexProvider(XML_RES, mWifiRestriction);
final List<String> keys = searchIndexProvider.getNonIndexableKeys(mContext);
assertThat(keys).contains(WifiTetherSettings.KEY_WIFI_TETHER_NETWORK_NAME);
assertThat(keys).contains(WifiTetherSettings.KEY_WIFI_TETHER_SECURITY);
assertThat(keys).contains(WifiTetherSettings.KEY_WIFI_TETHER_NETWORK_PASSWORD);
assertThat(keys).contains(WifiTetherSettings.KEY_WIFI_TETHER_AUTO_OFF);
assertThat(keys).contains(WifiTetherSettings.KEY_WIFI_TETHER_MAXIMIZE_COMPATIBILITY);
}
@Test
public void getNonIndexableKeys_tetherAndHotspotNotAvailable_keysReturned() {
when(mWifiRestriction.isTetherAvailable(mContext)).thenReturn(false);
when(mWifiRestriction.isHotspotAvailable(mContext)).thenReturn(false);
WifiTetherSettings.SearchIndexProvider searchIndexProvider =
new WifiTetherSettings.SearchIndexProvider(XML_RES, mWifiRestriction);
final List<String> keys = searchIndexProvider.getNonIndexableKeys(mContext);
assertThat(keys).contains(WifiTetherSettings.KEY_WIFI_TETHER_NETWORK_NAME);
assertThat(keys).contains(WifiTetherSettings.KEY_WIFI_TETHER_SECURITY);
assertThat(keys).contains(WifiTetherSettings.KEY_WIFI_TETHER_NETWORK_PASSWORD);
assertThat(keys).contains(WifiTetherSettings.KEY_WIFI_TETHER_AUTO_OFF);
assertThat(keys).contains(WifiTetherSettings.KEY_WIFI_TETHER_MAXIMIZE_COMPATIBILITY);
}
private void spyWifiTetherSettings() {
mWifiTetherSettings = spy(new WifiTetherSettings(mWifiRestriction));
final FragmentActivity activity = mock(FragmentActivity.class);
when(mWifiTetherSettings.getActivity()).thenReturn(activity);
when(mWifiTetherSettings.getContext()).thenReturn(mContext);
final Resources.Theme theme = mContext.getTheme();
when(activity.getTheme()).thenReturn(theme);
when(activity.getSystemService(Context.USER_SERVICE)).thenReturn(mUserManager);
doNothing().when(mWifiTetherSettings)
.onCreatePreferences(any(Bundle.class), nullable(String.class));
final FakeFeatureFactory fakeFeatureFactory = FakeFeatureFactory.setupForTest();
ReflectionHelpers.setField(mWifiTetherSettings, "mDashboardFeatureProvider",
fakeFeatureFactory.dashboardFeatureProvider);
ReflectionHelpers.setField(mWifiTetherSettings, "mEmptyTextView", mEmptyTextView);
doReturn(mPreferenceScreen).when(mWifiTetherSettings).getPreferenceScreen();
mWifiTetherSettings.onCreate(Bundle.EMPTY);
}
}

View File

@@ -0,0 +1,149 @@
/*
* Copyright (C) 2022 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.development;
import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import android.app.Instrumentation;
import android.content.ContentResolver;
import android.content.Context;
import android.database.ContentObserver;
import android.net.Uri;
import android.os.Handler;
import android.os.Looper;
import android.os.UserHandle;
import android.provider.Settings;
import androidx.preference.PreferenceManager;
import androidx.preference.PreferenceScreen;
import androidx.preference.SwitchPreference;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.platform.app.InstrumentationRegistry;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.MockitoAnnotations;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
@RunWith(AndroidJUnit4.class)
public class BackAnimationPreferenceControllerTest {
private static final int SETTING_VALUE_OFF = 0;
private static final int SETTING_VALUE_ON = 1;
private SwitchPreference mPreference;
private Context mContext;
private BackAnimationPreferenceController mController;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
mContext = instrumentation.getTargetContext();
mController = new BackAnimationPreferenceController(mContext);
mPreference = new SwitchPreference(mContext);
if (Looper.myLooper() == null) {
Looper.prepare();
}
Settings.Global.putInt(mContext.getContentResolver(),
Settings.Global.ENABLE_BACK_ANIMATION, -1);
final PreferenceManager preferenceManager = new PreferenceManager(mContext);
final PreferenceScreen screen = preferenceManager.createPreferenceScreen(mContext);
mPreference.setKey(mController.getPreferenceKey());
screen.addPreference(mPreference);
mController.displayPreference(screen);
}
@Test
public void onPreferenceChange_switchEnabled_shouldEnableBackAnimations() {
mController.onPreferenceChange(mPreference, true /* new value */);
final int mode = Settings.Global.getInt(mContext.getContentResolver(),
Settings.Global.ENABLE_BACK_ANIMATION, -1 /* default */);
assertThat(mode).isEqualTo(SETTING_VALUE_ON);
}
@Test
public void onPreferenceChange_switchDisabled_shouldDisableBackAnimations() {
mController.onPreferenceChange(mPreference, false /* new value */);
final int mode = Settings.Global.getInt(mContext.getContentResolver(),
Settings.Global.ENABLE_BACK_ANIMATION, -1 /* default */);
assertThat(mode).isEqualTo(SETTING_VALUE_OFF);
}
@Test
public void updateState_settingEnabled_preferenceShouldBeChecked() {
Settings.Global.putInt(mContext.getContentResolver(),
Settings.Global.ENABLE_BACK_ANIMATION, SETTING_VALUE_ON);
mController.updateState(mPreference);
assertTrue(mPreference.isChecked());
}
@Test
public void updateState_settingDisabled_preferenceShouldNotBeChecked() {
Settings.Global.putInt(mContext.getContentResolver(),
Settings.Global.ENABLE_BACK_ANIMATION, SETTING_VALUE_OFF);
mController.updateState(mPreference);
assertFalse(mPreference.isChecked());
}
@Test
public void onDeveloperOptionsSwitchDisabled_shouldDisablePreference()
throws InterruptedException {
ContentResolver contentResolver = mContext.getContentResolver();
int mode = doAndWaitForSettingChange(() -> mController.onDeveloperOptionsSwitchDisabled(),
contentResolver);
assertThat(mode).isEqualTo(SETTING_VALUE_OFF);
assertFalse(mPreference.isEnabled());
assertFalse(mPreference.isChecked());
}
private int doAndWaitForSettingChange(Runnable runnable, ContentResolver contentResolver) {
CountDownLatch countDownLatch = new CountDownLatch(1);
ContentObserver settingsObserver =
new ContentObserver(new Handler(Looper.myLooper())) {
@Override
public void onChange(boolean selfChange, Uri uri) {
countDownLatch.countDown();
}
};
contentResolver.registerContentObserver(
Settings.Global.getUriFor(Settings.Global.ENABLE_BACK_ANIMATION),
false, settingsObserver, UserHandle.USER_SYSTEM
);
runnable.run();
try {
countDownLatch.await(500, TimeUnit.MILLISECONDS);
} catch (InterruptedException e) {
Assert.fail(e.getMessage());
}
return Settings.Global.getInt(contentResolver,
Settings.Global.ENABLE_BACK_ANIMATION, -1 /* default */);
}
}