Snap for 11992154 from 2345b63c68 to 24Q4-release

Change-Id: I4336f08b19bf2645d539a030f96fff6fce8d7e5c
This commit is contained in:
Android Build Coastguard Worker
2024-06-19 23:22:23 +00:00
34 changed files with 968 additions and 258 deletions

View File

@@ -140,7 +140,7 @@
<uses-permission android:name="android.permission.REMAP_MODIFIER_KEYS" />
<uses-permission android:name="android.permission.ACCESS_GPU_SERVICE" />
<uses-permission android:name="android.permission.MANAGE_GAME_MODE" />
<uses-permission android:name="android.permission.RESTART_PHONE_PROCESS" />
<uses-permission android:name="android.permission.RESTART_TELEPHONY_PROCESS" />
<uses-permission android:name="android.permission.MANAGE_ENHANCED_CONFIRMATION_STATES" />
<uses-permission android:name="android.permission.ACCESS_KEYGUARD_SECURE_STORAGE" />
<uses-permission android:name="android.permission.MANAGE_NETWORK_POLICY" />

View File

@@ -0,0 +1,12 @@
package: "com.android.settings.flags"
container: "system_ext"
# NOTE: Keep alphabetized to help limit merge conflicts from multiple simultaneous editors.
flag {
name: "enable_wifi_sharing_runtime_fragment"
namespace: "prism_qr"
description: "Use WifiFeatureProvider to get the instance of WifiDppQrCodeGeneratorFragment."
bug: "329012096"
}

View File

@@ -0,0 +1,62 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2024 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.
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?android:attr/selectableItemBackground"
android:gravity="center_vertical"
android:minHeight="?android:attr/listPreferredItemHeight"
android:paddingTop="8dp"
android:paddingBottom="8dp"
android:paddingStart="?android:attr/listPreferredItemPaddingStart"
android:paddingEnd="?android:attr/listPreferredItemPaddingEnd">
<ImageView
android:id="@+id/icon"
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_gravity="center" />
<RelativeLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:paddingStart="16dp"
android:layout_weight="1">
<TextView
android:id="@+id/title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:ellipsize="marquee"
android:fadingEdge="horizontal"
android:singleLine="true"
android:textAppearance="@*android:style/TextAppearance.DeviceDefault.Body1"
android:textSize="16sp" />
<TextView
android:id="@+id/subtitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignStart="@id/title"
android:layout_below="@id/title"
android:maxLines="2"
android:textAppearance="@*android:style/TextAppearance.DeviceDefault.Subhead"
android:textColor="?android:attr/textColorSecondary"
android:textSize="14sp" />
</RelativeLayout>
</LinearLayout>

View File

@@ -7927,6 +7927,19 @@
<!-- Zen Modes: Summary for the Do not Disturb option and associated settings page. [CHAR LIMIT=240]-->
<string name="zen_mode_settings_summary">Only get notified by important people and apps</string>
<!-- Zen Modes: Option to add an automatic schedule for a mode. [CHAR_LIMIT=40] -->
<string name="zen_mode_select_schedule">Select activation type</string>
<!-- Zen Modes: Option to choose a time-based schedule for a mode. [CHAR_LIMIT=40] -->
<string name="zen_mode_select_schedule_time">Time</string>
<!-- Zen Modes: Example text for the option to choose a time-based schedule for a mode. [CHAR_LIMIT=60] -->
<string name="zen_mode_select_schedule_time_example">Ex. \"9:30 5:00 PM\"</string>
<!-- Zen Modes: Option to choose a calendar-events-based schedule for a mode. [CHAR_LIMIT=40] -->
<string name="zen_mode_select_schedule_calendar">Calendar</string>
<!-- Zen Modes: Example text for the option to choose a calendar-events-based schedule for a mode. [CHAR_LIMIT=60] -->
<string name="zen_mode_select_schedule_calendar_example">Ex. \"Personal calendar\"</string>
<!-- Subtitle for the Do not Disturb slice. [CHAR LIMIT=50]-->
<string name="zen_mode_slice_subtitle">Limit interruptions</string>

View File

@@ -271,12 +271,12 @@ public class ResetNetworkRequest {
builder.resetIms(mSubscriptionIdToResetIms);
}
// Reset phone process and RILD may impact above components, keep them at the end
if ((mResetOptions & RESET_PHONE_PROCESS) != 0) {
builder.restartPhoneProcess();
}
if ((mResetOptions & RESET_RILD) != 0) {
builder.restartRild();
}
if ((mResetOptions & RESET_PHONE_PROCESS) != 0) {
builder.restartPhoneProcess();
}
return builder;
}
}

View File

@@ -75,6 +75,10 @@ public class AudioStreamMediaService extends Service {
public void onSourceLost(int broadcastId) {
super.onSourceLost(broadcastId);
if (broadcastId == mBroadcastId) {
Log.d(TAG, "onSourceLost() : stopSelf");
if (mNotificationManager != null) {
mNotificationManager.cancel(NOTIFICATION_ID);
}
stopSelf();
}
}
@@ -86,6 +90,10 @@ public class AudioStreamMediaService extends Service {
&& mAudioStreamsHelper.getAllConnectedSources().stream()
.map(BluetoothLeBroadcastReceiveState::getBroadcastId)
.noneMatch(id -> id == mBroadcastId)) {
Log.d(TAG, "onSourceRemoved() : stopSelf");
if (mNotificationManager != null) {
mNotificationManager.cancel(NOTIFICATION_ID);
}
stopSelf();
}
}
@@ -96,6 +104,10 @@ public class AudioStreamMediaService extends Service {
@Override
public void onBluetoothStateChanged(int bluetoothState) {
if (BluetoothAdapter.STATE_OFF == bluetoothState) {
Log.d(TAG, "onBluetoothStateChanged() : stopSelf");
if (mNotificationManager != null) {
mNotificationManager.cancel(NOTIFICATION_ID);
}
stopSelf();
}
}
@@ -120,6 +132,10 @@ public class AudioStreamMediaService extends Service {
});
}
if (mDevices == null || mDevices.isEmpty()) {
Log.d(TAG, "onProfileConnectionStateChanged() : stopSelf");
if (mNotificationManager != null) {
mNotificationManager.cancel(NOTIFICATION_ID);
}
stopSelf();
}
}
@@ -246,21 +262,27 @@ public class AudioStreamMediaService extends Service {
@Override
public void onDestroy() {
Log.d(TAG, "onDestroy()");
super.onDestroy();
if (!AudioSharingUtils.isFeatureEnabled()) {
Log.d(TAG, "onDestroy() : skip due to feature not enabled");
return;
}
if (mLocalBtManager != null) {
Log.d(TAG, "onDestroy() : unregister mBluetoothCallback");
mLocalBtManager.getEventManager().unregisterCallback(mBluetoothCallback);
}
if (mLeBroadcastAssistant != null) {
Log.d(TAG, "onDestroy() : unregister mBroadcastAssistantCallback");
mLeBroadcastAssistant.unregisterServiceCallBack(mBroadcastAssistantCallback);
}
if (mVolumeControl != null) {
Log.d(TAG, "onDestroy() : unregister mVolumeControlCallback");
mVolumeControl.unregisterCallback(mVolumeControlCallback);
}
if (mLocalSession != null) {
Log.d(TAG, "onDestroy() : release mLocalSession");
mLocalSession.release();
mLocalSession = null;
}
@@ -273,6 +295,9 @@ public class AudioStreamMediaService extends Service {
mBroadcastId = intent != null ? intent.getIntExtra(BROADCAST_ID, -1) : -1;
if (mBroadcastId == -1) {
Log.w(TAG, "Invalid broadcast ID. Service will not start.");
if (mNotificationManager != null) {
mNotificationManager.cancel(NOTIFICATION_ID);
}
stopSelf();
return START_NOT_STICKY;
}
@@ -282,6 +307,9 @@ public class AudioStreamMediaService extends Service {
}
if (mDevices == null || mDevices.isEmpty()) {
Log.w(TAG, "No device. Service will not start.");
if (mNotificationManager != null) {
mNotificationManager.cancel(NOTIFICATION_ID);
}
stopSelf();
return START_NOT_STICKY;
}

View File

@@ -60,6 +60,7 @@ public final class BugReportContentProvider extends ContentProvider {
LogUtils.dumpBatteryUsageSlotDatabaseHist(context, writer);
LogUtils.dumpBatteryEventDatabaseHist(context, writer);
LogUtils.dumpBatteryStateDatabaseHist(context, writer);
LogUtils.dumpBatteryReattributeDatabaseHist(context, writer);
}
@Override

View File

@@ -19,6 +19,8 @@ package com.android.settings.fuelgauge.batteryusage.bugreport;
import android.content.Context;
import android.util.Log;
import androidx.annotation.VisibleForTesting;
import com.android.settings.fuelgauge.BatteryUtils;
import com.android.settings.fuelgauge.batteryusage.AppOptModeSharedPreferencesUtils;
import com.android.settings.fuelgauge.batteryusage.AppOptimizationModeEvent;
@@ -29,6 +31,8 @@ import com.android.settings.fuelgauge.batteryusage.db.AppUsageEventDao;
import com.android.settings.fuelgauge.batteryusage.db.AppUsageEventEntity;
import com.android.settings.fuelgauge.batteryusage.db.BatteryEventDao;
import com.android.settings.fuelgauge.batteryusage.db.BatteryEventEntity;
import com.android.settings.fuelgauge.batteryusage.db.BatteryReattributeDao;
import com.android.settings.fuelgauge.batteryusage.db.BatteryReattributeEntity;
import com.android.settings.fuelgauge.batteryusage.db.BatteryState;
import com.android.settings.fuelgauge.batteryusage.db.BatteryStateDao;
import com.android.settings.fuelgauge.batteryusage.db.BatteryStateDatabase;
@@ -127,6 +131,24 @@ public final class LogUtils {
dumpListItems(writer, entities, entity -> entity);
}
static void dumpBatteryReattributeDatabaseHist(Context context, PrintWriter writer) {
dumpBatteryReattributeDatabaseHist(
BatteryStateDatabase.getInstance(context).batteryReattributeDao(),
writer);
}
@VisibleForTesting
static void dumpBatteryReattributeDatabaseHist(
BatteryReattributeDao batteryReattributeDao, PrintWriter writer) {
writer.println("\n\tBatteryReattribute DatabaseHistory:");
final List<BatteryReattributeEntity> entities =
batteryReattributeDao.getAllAfter(
Clock.systemUTC().millis() - DUMP_TIME_OFFSET.toMillis());
if (entities != null && !entities.isEmpty()) {
dumpListItems(writer, entities, entity -> entity);
}
}
private static <T, S> void dumpListItems(
PrintWriter writer, List<T> itemList, Function<T, S> itemConverter) {
final AtomicInteger counter = new AtomicInteger(0);

View File

@@ -62,6 +62,7 @@ public class BatteryReattributeEntity {
.append("\nBatteryReattributeEntity{")
.append("\n\t" + utcToLocalTimeForLogging(timestampStart))
.append("\n\t" + utcToLocalTimeForLogging(timestampEnd))
.append("\n\t" + ConvertUtils.decodeBatteryReattribute(reattributeData))
.append("\n}");
return builder.toString();
}

View File

@@ -18,6 +18,7 @@ package com.android.settings.network;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothManager;
import android.content.ContentProviderClient;
import android.content.ContentResolver;
import android.content.Context;
import android.net.ConnectivityManager;
@@ -28,11 +29,14 @@ import android.net.wifi.WifiManager;
import android.net.wifi.p2p.WifiP2pManager;
import android.os.Looper;
import android.os.RecoverySystem;
import android.os.RemoteException;
import android.os.SystemClock;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
import android.util.Log;
import androidx.annotation.Nullable;
import com.android.internal.annotations.VisibleForTesting;
import com.android.settings.R;
import com.android.settings.ResetNetworkRequest;
@@ -257,15 +261,15 @@ public class ResetNetworkOperationBuilder {
*/
public ResetNetworkOperationBuilder restartPhoneProcess() {
Runnable runnable = () -> {
try {
mContext.getContentResolver().call(
getResetTelephonyContentProviderAuthority(),
METHOD_RESTART_PHONE_PROCESS,
/* arg= */ null,
/* extras= */ null);
Log.i(TAG, "Phone process was restarted.");
} catch (IllegalArgumentException iae) {
Log.w(TAG, "Fail to restart phone process: " + iae);
// Unstable content provider can avoid us getting killed together with phone process
try (ContentProviderClient client = getUnstableTelephonyContentProviderClient()) {
if (client != null) {
client.call(METHOD_RESTART_PHONE_PROCESS, /* arg= */ null, /* extra= */ null);
Log.i(TAG, "Phone process was restarted.");
}
} catch (RemoteException re) {
// It's normal to throw RE since phone process immediately dies
Log.i(TAG, "Phone process has been restarted: " + re);
}
};
mResetSequence.add(runnable);
@@ -279,15 +283,13 @@ public class ResetNetworkOperationBuilder {
*/
public ResetNetworkOperationBuilder restartRild() {
Runnable runnable = () -> {
try {
mContext.getContentResolver().call(
getResetTelephonyContentProviderAuthority(),
METHOD_RESTART_RILD,
/* arg= */ null,
/* extras= */ null);
Log.i(TAG, "RILD was restarted.");
} catch (IllegalArgumentException iae) {
Log.w(TAG, "Fail to restart RILD: " + iae);
try (ContentProviderClient client = getUnstableTelephonyContentProviderClient()) {
if (client != null) {
client.call(METHOD_RESTART_RILD, /* arg= */ null, /* extra= */ null);
Log.i(TAG, "RILD was restarted.");
}
} catch (RemoteException re) {
Log.w(TAG, "Fail to restart RILD: " + re);
}
};
mResetSequence.add(runnable);
@@ -322,9 +324,18 @@ public class ResetNetworkOperationBuilder {
* @return the authority of the telephony content provider that support methods
* resetPhoneProcess and resetRild.
*/
@VisibleForTesting
String getResetTelephonyContentProviderAuthority() {
private String getResetTelephonyContentProviderAuthority() {
return mContext.getResources().getString(
R.string.reset_telephony_stack_content_provider_authority);
}
/**
* @return the unstable content provider to avoid us getting killed with phone process
*/
@Nullable
@VisibleForTesting
public ContentProviderClient getUnstableTelephonyContentProviderClient() {
return mContext.getContentResolver().acquireUnstableContentProviderClient(
getResetTelephonyContentProviderAuthority());
}
}

View File

@@ -0,0 +1,40 @@
/*
* Copyright (C) 2024 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.apn
import android.provider.Telephony
import androidx.compose.runtime.Composable
import androidx.compose.ui.res.booleanResource
import androidx.compose.ui.res.stringResource
import com.android.settings.R
import com.android.settingslib.spa.widget.preference.SwitchPreference
import com.android.settingslib.spa.widget.preference.SwitchPreferenceModel
@Composable
fun ApnEditCarrierEnabled(apnData: ApnData, onCarrierEnabledChanged: (Boolean) -> Unit) {
SwitchPreference(
object : SwitchPreferenceModel {
override val title = stringResource(R.string.carrier_enabled)
val allowEdit = booleanResource(R.bool.config_allow_edit_carrier_enabled)
override val changeable = {
allowEdit && apnData.isFieldEnabled(Telephony.Carriers.CARRIER_ENABLED)
}
override val checked = { apnData.carrierEnabled }
override val onCheckedChange = onCarrierEnabledChanged
}
)
}

View File

@@ -235,19 +235,7 @@ fun ApnPage(apnDataInit: ApnData, apnDataCur: MutableState<ApnData>, uriInit: Ur
enabled = apnData.isFieldEnabled(Telephony.Carriers.ROAMING_PROTOCOL),
) { apnData = apnData.copy(apnRoaming = it) }
ApnNetworkTypeCheckBox(apnData) { apnData = apnData.copy(networkType = it) }
SwitchPreference(
object : SwitchPreferenceModel {
override val title = stringResource(R.string.carrier_enabled)
override val changeable = {
apnData.apnEnableEnabled &&
apnData.isFieldEnabled(Telephony.Carriers.CARRIER_ENABLED)
}
override val checked = { apnData.apnEnable }
override val onCheckedChange = { newChecked: Boolean ->
apnData = apnData.copy(apnEnable = newChecked)
}
}
)
ApnEditCarrierEnabled(apnData) { apnData = apnData.copy(carrierEnabled = it) }
}
}
}

View File

@@ -90,7 +90,7 @@ fun getApnDataFromUri(uri: Uri, context: Context): ApnData {
apnRoaming = context.convertProtocol2Options(
cursor.getString(Telephony.Carriers.ROAMING_PROTOCOL)
),
apnEnable = cursor.getInt(Telephony.Carriers.CARRIER_ENABLED) == 1,
carrierEnabled = cursor.getInt(Telephony.Carriers.CARRIER_ENABLED) == 1,
networkType = cursor.getLong(Telephony.Carriers.NETWORK_TYPE_BITMASK),
edited = cursor.getInt(Telephony.Carriers.EDITED_STATUS),
userEditable = cursor.getInt(Telephony.Carriers.USER_EDITABLE),

View File

@@ -44,11 +44,10 @@ data class ApnData(
val apnType: String = "",
val apnProtocol: Int = -1,
val apnRoaming: Int = -1,
val apnEnable: Boolean = true,
val carrierEnabled: Boolean = true,
val networkType: Long = 0,
val edited: Int = Telephony.Carriers.USER_EDITED,
val userEditable: Int = 1,
val apnEnableEnabled: Boolean = true,
val newApn: Boolean = false,
val subId: Int = -1,
val validEnabled: Boolean = false,
@@ -72,7 +71,7 @@ data class ApnData(
Telephony.Carriers.NETWORK_TYPE_BITMASK to networkType,
// Copy network type into lingering network type.
Telephony.Carriers.LINGERING_NETWORK_TYPE_BITMASK to networkType,
Telephony.Carriers.CARRIER_ENABLED to apnEnable,
Telephony.Carriers.CARRIER_ENABLED to carrierEnabled,
Telephony.Carriers.EDITED_STATUS to Telephony.Carriers.USER_EDITED,
)
@@ -134,10 +133,6 @@ fun getApnDataInit(arguments: Bundle, context: Context, uriInit: Uri, subId: Int
)
}
apnDataInit = apnDataInit.copy(
apnEnableEnabled =
context.resources.getBoolean(R.bool.config_allow_edit_carrier_enabled)
)
// TODO: mIsCarrierIdApn
return disableInit(apnDataInit)
}

View File

@@ -18,6 +18,12 @@ package com.android.settings.notification.modes;
import static android.app.NotificationManager.INTERRUPTION_FILTER_ALL;
import static android.app.NotificationManager.INTERRUPTION_FILTER_PRIORITY;
import static android.service.notification.SystemZenRules.getTriggerDescriptionForScheduleEvent;
import static android.service.notification.SystemZenRules.getTriggerDescriptionForScheduleTime;
import static android.service.notification.ZenModeConfig.tryParseEventConditionId;
import static android.service.notification.ZenModeConfig.tryParseScheduleConditionId;
import static com.google.common.base.Preconditions.checkState;
import static java.util.Objects.requireNonNull;
@@ -26,7 +32,10 @@ import android.app.AutomaticZenRule;
import android.app.NotificationManager;
import android.content.Context;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.service.notification.SystemZenRules;
import android.service.notification.ZenDeviceEffects;
import android.service.notification.ZenModeConfig;
import android.service.notification.ZenPolicy;
import android.util.Log;
@@ -204,6 +213,44 @@ class ZenMode {
: new ZenDeviceEffects.Builder().build();
}
public void setCustomModeConditionId(Context context, Uri conditionId) {
checkState(SystemZenRules.PACKAGE_ANDROID.equals(mRule.getPackageName()),
"Trying to change condition of non-system-owned rule %s (to %s)",
mRule, conditionId);
Uri oldCondition = mRule.getConditionId();
mRule.setConditionId(conditionId);
ZenModeConfig.ScheduleInfo scheduleInfo = tryParseScheduleConditionId(conditionId);
if (scheduleInfo != null) {
mRule.setType(AutomaticZenRule.TYPE_SCHEDULE_TIME);
mRule.setOwner(ZenModeConfig.getScheduleConditionProvider());
mRule.setTriggerDescription(
getTriggerDescriptionForScheduleTime(context, scheduleInfo));
return;
}
ZenModeConfig.EventInfo eventInfo = tryParseEventConditionId(conditionId);
if (eventInfo != null) {
mRule.setType(AutomaticZenRule.TYPE_SCHEDULE_CALENDAR);
mRule.setOwner(ZenModeConfig.getEventConditionProvider());
mRule.setTriggerDescription(getTriggerDescriptionForScheduleEvent(context, eventInfo));
return;
}
if (ZenModeConfig.isValidCustomManualConditionId(conditionId)) {
mRule.setType(AutomaticZenRule.TYPE_OTHER);
mRule.setOwner(ZenModeConfig.getCustomManualConditionProvider());
mRule.setTriggerDescription("");
return;
}
Log.wtf(TAG, String.format(
"Changed condition of rule %s (%s -> %s) but cannot recognize which kind of "
+ "condition it was!",
mRule, oldCondition, conditionId));
}
public boolean canEditName() {
return !isManualDnd();
}
@@ -224,6 +271,15 @@ class ZenMode {
return mIsActive;
}
public boolean isSystemOwned() {
return SystemZenRules.PACKAGE_ANDROID.equals(mRule.getPackageName());
}
@AutomaticZenRule.Type
public int getType() {
return mRule.getType();
}
@Override
public boolean equals(@Nullable Object obj) {
return obj instanceof ZenMode other

View File

@@ -52,7 +52,7 @@ public class ZenModeFragment extends ZenModeFragmentBase {
prefControllers.add(new ZenModeDisplayLinkPreferenceController(
context, "mode_display_settings", mBackend, mHelperBackend));
prefControllers.add(new ZenModeSetTriggerLinkPreferenceController(context,
"zen_automatic_trigger_category", mBackend));
"zen_automatic_trigger_category", this, mBackend));
return prefControllers;
}

View File

@@ -0,0 +1,149 @@
/*
* Copyright (C) 2024 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.notification.modes;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Preconditions.checkState;
import android.app.Dialog;
import android.content.Context;
import android.net.Uri;
import android.os.Bundle;
import android.service.notification.ZenModeConfig;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.ImageView;
import android.widget.TextView;
import androidx.annotation.DrawableRes;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.StringRes;
import androidx.appcompat.app.AlertDialog;
import com.android.settings.R;
import com.android.settings.core.instrumentation.InstrumentedDialogFragment;
import com.android.settings.dashboard.DashboardFragment;
import com.google.common.collect.ImmutableList;
public class ZenModeScheduleChooserDialog extends InstrumentedDialogFragment {
private static final String TAG = "ZenModeScheduleChooserDialog";
static final int OPTION_TIME = 0;
static final int OPTION_CALENDAR = 1;
private record ScheduleOption(@StringRes int nameResId, @StringRes int exampleResId,
@DrawableRes int iconResId) {}
private static final ImmutableList<ScheduleOption> SCHEDULE_OPTIONS = ImmutableList.of(
new ScheduleOption(R.string.zen_mode_select_schedule_time,
R.string.zen_mode_select_schedule_time_example,
com.android.internal.R.drawable.ic_zen_mode_type_schedule_time),
new ScheduleOption(R.string.zen_mode_select_schedule_calendar,
R.string.zen_mode_select_schedule_calendar_example,
com.android.internal.R.drawable.ic_zen_mode_type_schedule_calendar));
private OnScheduleOptionListener mOptionListener;
interface OnScheduleOptionListener {
void onScheduleSelected(Uri conditionId);
}
@Override
public int getMetricsCategory() {
// TODO: b/332937635 - Update metrics category
return 0;
}
static void show(DashboardFragment parent, OnScheduleOptionListener optionListener) {
ZenModeScheduleChooserDialog dialog = new ZenModeScheduleChooserDialog();
dialog.mOptionListener = optionListener;
dialog.setTargetFragment(parent, 0);
dialog.show(parent.getParentFragmentManager(), TAG);
}
@NonNull
@Override
public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) {
checkState(getContext() != null);
return new AlertDialog.Builder(getContext())
.setTitle(R.string.zen_mode_choose_rule_type)
.setAdapter(new OptionsAdapter(getContext()),
(dialog, which) -> onScheduleTypeSelected(which))
.setNegativeButton(R.string.cancel, null)
.create();
}
private static class OptionsAdapter extends ArrayAdapter<ScheduleOption> {
private final LayoutInflater mInflater;
OptionsAdapter(@NonNull Context context) {
super(context, R.layout.zen_mode_type_item, SCHEDULE_OPTIONS);
mInflater = LayoutInflater.from(context);
}
@NonNull
@Override
public View getView(int position, @Nullable View convertView, @NonNull ViewGroup parent) {
if (convertView == null) {
convertView = mInflater.inflate(R.layout.zen_mode_type_item, parent, false);
}
// No need for holder pattern since we have only 2 items.
ImageView imageView = checkNotNull(convertView.findViewById(R.id.icon));
TextView title = checkNotNull(convertView.findViewById(R.id.title));
TextView subtitle = checkNotNull(convertView.findViewById(R.id.subtitle));
ScheduleOption option = checkNotNull(getItem(position));
imageView.setImageResource(option.iconResId());
title.setText(option.nameResId());
subtitle.setText(option.exampleResId());
return convertView;
}
}
private void onScheduleTypeSelected(int whichOption) {
Uri conditionId = switch (whichOption) {
case OPTION_TIME -> getDefaultScheduleTimeCondition();
case OPTION_CALENDAR -> getDefaultScheduleCalendarCondition();
default -> ZenModeConfig.toCustomManualConditionId();
};
mOptionListener.onScheduleSelected(conditionId);
}
private static Uri getDefaultScheduleTimeCondition() {
ZenModeConfig.ScheduleInfo schedule = new ZenModeConfig.ScheduleInfo();
schedule.days = ZenModeConfig.ALL_DAYS;
schedule.startHour = 9;
schedule.startMinute = 30;
schedule.endHour = 17;
return ZenModeConfig.toScheduleConditionId(schedule);
}
private static Uri getDefaultScheduleCalendarCondition() {
ZenModeConfig.EventInfo eventInfo = new ZenModeConfig.EventInfo();
eventInfo.calendarId = null; // All calendars of the current user.
eventInfo.reply = ZenModeConfig.EventInfo.REPLY_ANY_EXCEPT_NO;
return ZenModeConfig.toEventConditionId(eventInfo);
}
}

View File

@@ -16,14 +16,12 @@
package com.android.settings.notification.modes;
import android.app.Flags;
import android.content.Context;
import android.content.pm.PackageManager;
import android.database.Cursor;
import android.os.UserHandle;
import android.os.UserManager;
import android.provider.CalendarContract;
import android.service.notification.SystemZenRules;
import android.service.notification.ZenModeConfig;
import androidx.annotation.NonNull;
@@ -42,7 +40,7 @@ import java.util.List;
import java.util.Objects;
import java.util.function.Function;
public class ZenModeSetCalendarPreferenceController extends AbstractZenModePreferenceController {
class ZenModeSetCalendarPreferenceController extends AbstractZenModePreferenceController {
@VisibleForTesting
protected static final String KEY_CALENDAR = "calendar";
@VisibleForTesting
@@ -122,11 +120,7 @@ public class ZenModeSetCalendarPreferenceController extends AbstractZenModePrefe
@VisibleForTesting
protected Function<ZenMode, ZenMode> updateEventMode(ZenModeConfig.EventInfo event) {
return (zenMode) -> {
zenMode.getRule().setConditionId(ZenModeConfig.toEventConditionId(event));
if (Flags.modesApi() && Flags.modesUi()) {
zenMode.getRule().setTriggerDescription(
SystemZenRules.getTriggerDescriptionForScheduleEvent(mContext, event));
}
zenMode.setCustomModeConditionId(mContext, ZenModeConfig.toEventConditionId(event));
return zenMode;
};
}

View File

@@ -16,9 +16,7 @@
package com.android.settings.notification.modes;
import android.app.Flags;
import android.content.Context;
import android.service.notification.SystemZenRules;
import android.service.notification.ZenModeConfig;
import android.text.format.DateFormat;
import android.util.ArraySet;
@@ -116,16 +114,13 @@ class ZenModeSetSchedulePreferenceController extends AbstractZenModePreferenceCo
@VisibleForTesting
protected Function<ZenMode, ZenMode> updateScheduleMode(ZenModeConfig.ScheduleInfo schedule) {
return (zenMode) -> {
zenMode.getRule().setConditionId(ZenModeConfig.toScheduleConditionId(schedule));
if (Flags.modesApi() && Flags.modesUi()) {
zenMode.getRule().setTriggerDescription(
SystemZenRules.getTriggerDescriptionForScheduleTime(mContext, schedule));
}
zenMode.setCustomModeConditionId(mContext,
ZenModeConfig.toScheduleConditionId(schedule));
return zenMode;
};
}
private ZenModeTimePickerFragment.TimeSetter mStartSetter = (hour, minute) -> {
private final ZenModeTimePickerFragment.TimeSetter mStartSetter = (hour, minute) -> {
if (!isValidTime(hour, minute)) {
return;
}
@@ -137,7 +132,7 @@ class ZenModeSetSchedulePreferenceController extends AbstractZenModePreferenceCo
saveMode(updateScheduleMode(mSchedule));
};
private ZenModeTimePickerFragment.TimeSetter mEndSetter = (hour, minute) -> {
private final ZenModeTimePickerFragment.TimeSetter mEndSetter = (hour, minute) -> {
if (!isValidTime(hour, minute)) {
return;
}

View File

@@ -13,15 +13,13 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.notification.modes;
import static android.app.AutomaticZenRule.TYPE_SCHEDULE_CALENDAR;
import static android.app.AutomaticZenRule.TYPE_SCHEDULE_TIME;
import static com.android.settings.notification.modes.ZenModeFragmentBase.MODE_ID;
import android.content.Context;
import android.os.Bundle;
import androidx.annotation.NonNull;
import androidx.annotation.VisibleForTesting;
@@ -29,7 +27,7 @@ import androidx.preference.Preference;
import androidx.preference.PreferenceCategory;
import com.android.settings.R;
import com.android.settings.core.SubSettingLauncher;
import com.android.settings.dashboard.DashboardFragment;
import com.android.settingslib.PrimarySwitchPreference;
/**
@@ -39,9 +37,13 @@ class ZenModeSetTriggerLinkPreferenceController extends AbstractZenModePreferenc
@VisibleForTesting
protected static final String AUTOMATIC_TRIGGER_PREF_KEY = "zen_automatic_trigger_settings";
private final DashboardFragment mFragment;
ZenModeSetTriggerLinkPreferenceController(Context context, String key,
DashboardFragment fragment,
ZenModesBackend backend) {
super(context, key, backend);
mFragment = fragment;
}
@Override
@@ -54,46 +56,52 @@ class ZenModeSetTriggerLinkPreferenceController extends AbstractZenModePreferenc
// This controller is expected to govern a preference category so that it controls the
// availability of the entire preference category if the mode doesn't have a way to
// automatically trigger (such as manual DND).
Preference switchPref = ((PreferenceCategory) preference).findPreference(
PrimarySwitchPreference switchPref = ((PreferenceCategory) preference).findPreference(
AUTOMATIC_TRIGGER_PREF_KEY);
if (switchPref == null) {
return;
}
((PrimarySwitchPreference) switchPref).setChecked(zenMode.getRule().isEnabled());
switchPref.setChecked(zenMode.getRule().isEnabled());
switchPref.setOnPreferenceChangeListener(mSwitchChangeListener);
Bundle bundle = new Bundle();
bundle.putString(MODE_ID, zenMode.getId());
switchPref.setSummary(zenMode.getRule().getTriggerDescription());
switchPref.setIcon(null);
switchPref.setOnPreferenceClickListener(null);
switchPref.setIntent(null);
// TODO: b/341961712 - direct preference to app-owned intent if available
switch (zenMode.getRule().getType()) {
case TYPE_SCHEDULE_TIME:
switchPref.setTitle(R.string.zen_mode_set_schedule_link);
switchPref.setSummary(zenMode.getRule().getTriggerDescription());
switchPref.setIntent(new SubSettingLauncher(mContext)
.setDestination(ZenModeSetScheduleFragment.class.getName())
// TODO: b/332937635 - set correct metrics category
.setSourceMetricsCategory(0)
.setArguments(bundle)
.toIntent());
break;
case TYPE_SCHEDULE_CALENDAR:
switchPref.setTitle(R.string.zen_mode_set_calendar_link);
switchPref.setSummary(zenMode.getRule().getTriggerDescription());
switchPref.setIntent(new SubSettingLauncher(mContext)
.setDestination(ZenModeSetCalendarFragment.class.getName())
// TODO: b/332937635 - set correct metrics category
.setSourceMetricsCategory(0)
.setArguments(bundle)
.toIntent());
break;
default:
// TODO: b/342156843 - change this to allow adding a trigger condition for system
// rules that don't yet have a type selected
switchPref.setTitle("not implemented");
if (zenMode.isSystemOwned() && zenMode.getType() == TYPE_SCHEDULE_TIME) {
switchPref.setTitle(R.string.zen_mode_set_schedule_link);
// TODO: b/332937635 - set correct metrics category
switchPref.setIntent(ZenSubSettingLauncher.forModeFragment(mContext,
ZenModeSetScheduleFragment.class, zenMode.getId(), 0).toIntent());
} else if (zenMode.isSystemOwned() && zenMode.getType() == TYPE_SCHEDULE_CALENDAR) {
switchPref.setTitle(R.string.zen_mode_set_calendar_link);
switchPref.setIcon(null);
// TODO: b/332937635 - set correct metrics category
switchPref.setIntent(ZenSubSettingLauncher.forModeFragment(mContext,
ZenModeSetCalendarFragment.class, zenMode.getId(), 0).toIntent());
} else if (zenMode.isSystemOwned()) {
switchPref.setTitle(R.string.zen_mode_select_schedule);
switchPref.setIcon(R.drawable.ic_add_24dp);
switchPref.setSummary("");
// TODO: b/342156843 - Hide the switch (needs support in SettingsLib).
switchPref.setOnPreferenceClickListener(clickedPreference -> {
ZenModeScheduleChooserDialog.show(mFragment, mOnScheduleOptionListener);
return true;
});
} else {
// TODO: b/341961712 - direct preference to app-owned intent if available
switchPref.setTitle("not implemented");
}
}
@VisibleForTesting
final ZenModeScheduleChooserDialog.OnScheduleOptionListener mOnScheduleOptionListener =
conditionId -> saveMode(mode -> {
mode.setCustomModeConditionId(mContext, conditionId);
return mode;
});
@VisibleForTesting
protected Preference.OnPreferenceChangeListener mSwitchChangeListener = (p, newValue) -> {
final boolean newEnabled = (Boolean) newValue;
@@ -103,5 +111,6 @@ class ZenModeSetTriggerLinkPreferenceController extends AbstractZenModePreferenc
}
return zenMode;
});
// TODO: b/342156843 - Do we want to jump to the corresponding schedule editing screen?
};
}

View File

@@ -24,7 +24,6 @@ import android.content.Context;
import android.net.Uri;
import android.provider.Settings;
import android.service.notification.Condition;
import android.service.notification.SystemZenRules;
import android.service.notification.ZenModeConfig;
import android.util.Log;
@@ -194,19 +193,11 @@ class ZenModesBackend {
*/
@Nullable
ZenMode addCustomMode(String name) {
ZenModeConfig.ScheduleInfo schedule = new ZenModeConfig.ScheduleInfo();
schedule.days = ZenModeConfig.ALL_DAYS;
schedule.startHour = 22;
schedule.endHour = 7;
// TODO: b/326442408 - Create as "manual" (i.e. no trigger) instead of schedule-time.
AutomaticZenRule rule = new AutomaticZenRule.Builder(name,
ZenModeConfig.toScheduleConditionId(schedule))
.setPackage(ZenModeConfig.getScheduleConditionProvider().getPackageName())
.setType(AutomaticZenRule.TYPE_SCHEDULE_CALENDAR)
.setOwner(ZenModeConfig.getScheduleConditionProvider())
.setTriggerDescription(SystemZenRules.getTriggerDescriptionForScheduleTime(
mContext, schedule))
ZenModeConfig.toCustomManualConditionId())
.setPackage(ZenModeConfig.getCustomManualConditionProvider().getPackageName())
.setType(AutomaticZenRule.TYPE_OTHER)
.setOwner(ZenModeConfig.getCustomManualConditionProvider())
.setManualInvocationAllowed(true)
.build();

View File

@@ -29,7 +29,7 @@ class ZenSubSettingLauncher {
SettingsEnums.NOTIFICATION_ZEN_MODE_AUTOMATION);
}
private static SubSettingLauncher forModeFragment(Context context,
static SubSettingLauncher forModeFragment(Context context,
Class<? extends ZenModeFragmentBase> fragmentClass, String modeId,
int sourceMetricsCategory) {
Bundle bundle = new Bundle();

View File

@@ -17,16 +17,32 @@
package com.android.settings.print
import android.app.settings.SettingsEnums
import android.content.Context
import android.content.Intent
import android.net.Uri
import android.os.Bundle
import android.provider.Settings
import androidx.annotation.VisibleForTesting
import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.Add
import androidx.compose.material.icons.outlined.Print
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.alpha
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import androidx.core.os.bundleOf
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.android.settings.R
@@ -36,13 +52,18 @@ import com.android.settings.print.PrintSettingsFragment.EXTRA_CHECKED
import com.android.settings.print.PrintSettingsFragment.EXTRA_SERVICE_COMPONENT_NAME
import com.android.settings.print.PrintSettingsFragment.EXTRA_TITLE
import com.android.settingslib.spa.framework.common.SettingsPageProvider
import com.android.settingslib.spa.framework.compose.rememberContext
import com.android.settingslib.spa.framework.compose.rememberDrawablePainter
import com.android.settingslib.spa.framework.theme.SettingsDimension
import com.android.settingslib.spa.framework.theme.SettingsOpacity
import com.android.settingslib.spa.widget.preference.Preference
import com.android.settingslib.spa.widget.preference.PreferenceModel
import com.android.settingslib.spa.widget.scaffold.RegularScaffold
import com.android.settingslib.spa.widget.ui.Category
import com.android.settingslib.spa.widget.ui.SettingsIcon
import com.android.settingslib.spaprivileged.settingsprovider.settingsSecureStringFlow
import com.android.settingslib.spaprivileged.template.common.UserProfilePager
import kotlinx.coroutines.flow.Flow
object PrintSettingsPageProvider : SettingsPageProvider {
override val name = "PrintSettings"
@@ -52,51 +73,101 @@ object PrintSettingsPageProvider : SettingsPageProvider {
RegularScaffold(title = stringResource(R.string.print_settings)) {
val context = LocalContext.current
val printRepository = remember(context) { PrintRepository(context) }
UserProfilePager {
PrintServices(printRepository)
}
UserProfilePager { PrintServices(printRepository) }
}
}
@Composable
private fun PrintServices(printRepository: PrintRepository) {
val printServiceDisplayInfos by remember {
printRepository.printServiceDisplayInfosFlow()
}.collectAsStateWithLifecycle(initialValue = emptyList())
Category(title = stringResource(R.string.print_settings_title)) {
for (printServiceDisplayInfo in printServiceDisplayInfos) {
PrintService(printServiceDisplayInfo)
val printServiceDisplayInfos by
remember { printRepository.printServiceDisplayInfosFlow() }
.collectAsStateWithLifecycle(initialValue = emptyList())
if (printServiceDisplayInfos.isEmpty()) {
NoServicesInstalled()
} else {
Category(title = stringResource(R.string.print_settings_title)) {
for (printServiceDisplayInfo in printServiceDisplayInfos) {
PrintService(printServiceDisplayInfo)
}
}
}
AddPrintService()
}
@Composable
private fun NoServicesInstalled() {
Column(
modifier = Modifier.fillMaxSize().padding(SettingsDimension.itemPaddingAround),
horizontalAlignment = Alignment.CenterHorizontally,
) {
Icon(
imageVector = Icons.Outlined.Print,
contentDescription = null,
modifier =
Modifier.size(110.dp)
.padding(SettingsDimension.itemPaddingAround)
.alpha(SettingsOpacity.SurfaceTone),
)
Text(
text = stringResource(R.string.print_no_services_installed),
style = MaterialTheme.typography.titleLarge,
)
}
}
@VisibleForTesting
@Composable
fun PrintService(displayInfo: PrintServiceDisplayInfo) {
val context = LocalContext.current
Preference(model = object : PreferenceModel {
override val title = displayInfo.title
override val summary = { displayInfo.summary }
override val icon: @Composable () -> Unit = {
Image(
painter = rememberDrawablePainter(displayInfo.icon),
contentDescription = null,
modifier = Modifier.size(SettingsDimension.appIconItemSize),
)
}
override val onClick = {
SubSettingLauncher(context).apply {
setDestination(PrintServiceSettingsFragment::class.qualifiedName)
setArguments(
bundleOf(
EXTRA_CHECKED to displayInfo.isEnabled,
EXTRA_TITLE to displayInfo.title,
EXTRA_SERVICE_COMPONENT_NAME to displayInfo.componentName
)
Preference(
object : PreferenceModel {
override val title = displayInfo.title
override val summary = { displayInfo.summary }
override val icon: @Composable () -> Unit = {
Image(
painter = rememberDrawablePainter(displayInfo.icon),
contentDescription = null,
modifier = Modifier.size(SettingsDimension.appIconItemSize),
)
setSourceMetricsCategory(SettingsEnums.PRINT_SETTINGS)
}.launch()
}
override val onClick = { launchPrintServiceSettings(context, displayInfo) }
}
})
)
}
private fun launchPrintServiceSettings(context: Context, displayInfo: PrintServiceDisplayInfo) {
SubSettingLauncher(context)
.apply {
setDestination(PrintServiceSettingsFragment::class.qualifiedName)
setArguments(
bundleOf(
EXTRA_CHECKED to displayInfo.isEnabled,
EXTRA_TITLE to displayInfo.title,
EXTRA_SERVICE_COMPONENT_NAME to displayInfo.componentName
)
)
setSourceMetricsCategory(SettingsEnums.PRINT_SETTINGS)
}
.launch()
}
@Composable
fun AddPrintService(
searchUriFlow: Flow<String> = rememberContext { context ->
context.settingsSecureStringFlow(Settings.Secure.PRINT_SERVICE_SEARCH_URI)
},
) {
val context = LocalContext.current
val searchUri by searchUriFlow.collectAsStateWithLifecycle("")
if (searchUri.isEmpty()) return
Preference(
object : PreferenceModel {
override val title = stringResource(R.string.print_menu_item_add_service)
override val icon = @Composable { SettingsIcon(imageVector = Icons.Outlined.Add) }
override val onClick = {
context.startActivity(Intent(Intent.ACTION_VIEW, Uri.parse(searchUri)))
}
}
)
}
}

View File

@@ -35,6 +35,8 @@ import androidx.annotation.VisibleForTesting;
import androidx.fragment.app.FragmentTransaction;
import com.android.settings.R;
import com.android.settings.flags.Flags;
import com.android.settings.overlay.FeatureFactory;
import java.util.List;
@@ -236,7 +238,12 @@ public class WifiDppConfiguratorActivity extends WifiDppBaseActivity implements
WifiDppUtils.TAG_FRAGMENT_QR_CODE_GENERATOR);
if (fragment == null) {
fragment = new WifiDppQrCodeGeneratorFragment();
if (Flags.enableWifiSharingRuntimeFragment()) {
fragment = FeatureFactory.getFeatureFactory().getWifiFeatureProvider()
.getWifiDppQrCodeGeneratorFragment();
} else {
fragment = new WifiDppQrCodeGeneratorFragment();
}
} else {
if (fragment.isVisible()) {
return;

View File

@@ -56,7 +56,7 @@ public class WifiDppQrCodeGeneratorFragment extends WifiDppQrCodeBaseFragment {
private static final String TAG = "WifiDppQrCodeGeneratorFragment";
private ImageView mQrCodeView;
private String mQrCode;
protected String mQrCode;
private static final String CHIP_LABEL_METADATA_KEY = "android.service.chooser.chip_label";
private static final String CHIP_ICON_METADATA_KEY = "android.service.chooser.chip_icon";
@@ -258,7 +258,7 @@ public class WifiDppQrCodeGeneratorFragment extends WifiDppQrCodeBaseFragment {
return button;
}
private void setQrCode() {
protected void setQrCode() {
try {
final int qrcodeSize = getContext().getResources().getDimensionPixelSize(
R.dimen.qrcode_size);

View File

@@ -27,6 +27,7 @@ import androidx.lifecycle.ViewModelProvider;
import androidx.lifecycle.ViewModelStoreOwner;
import com.android.settings.wifi.details.WifiNetworkDetailsViewModel;
import com.android.settings.wifi.dpp.WifiDppQrCodeGeneratorFragment;
import com.android.settings.wifi.repository.SharedConnectivityRepository;
import com.android.settings.wifi.repository.WifiHotspotRepository;
import com.android.settings.wifi.tether.WifiHotspotSecurityViewModel;
@@ -146,6 +147,15 @@ public class WifiFeatureProvider {
return viewModel;
}
/**
* Gets an instance of WifiDppQrCodeGeneratorFragment
*/
public WifiDppQrCodeGeneratorFragment getWifiDppQrCodeGeneratorFragment() {
WifiDppQrCodeGeneratorFragment fragment = new WifiDppQrCodeGeneratorFragment();
verboseLog(TAG, "getWifiDppQrCodeGeneratorFragment():" + fragment);
return fragment;
}
/**
* Send a {@link Log#VERBOSE} log message.
*

View File

@@ -0,0 +1,102 @@
/*
* Copyright (C) 2024 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.fuelgauge.batteryusage.bugreport;
import static com.google.common.truth.Truth.assertThat;
import android.content.Context;
import androidx.test.core.app.ApplicationProvider;
import com.android.settings.fuelgauge.batteryusage.BatteryReattribute;
import com.android.settings.fuelgauge.batteryusage.db.BatteryReattributeDao;
import com.android.settings.fuelgauge.batteryusage.db.BatteryReattributeEntity;
import com.android.settings.fuelgauge.batteryusage.db.BatteryStateDatabase;
import com.android.settings.testutils.BatteryTestUtils;
import org.junit.After;
import org.junit.Before;
import org.junit.FixMethodOrder;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.MethodSorters;
import org.robolectric.RobolectricTestRunner;
import java.io.PrintWriter;
import java.io.StringWriter;
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
@RunWith(RobolectricTestRunner.class)
public final class LogUtilsTest {
private StringWriter mTestStringWriter;
private PrintWriter mTestPrintWriter;
private Context mContext;
private BatteryStateDatabase mDatabase;
private BatteryReattributeDao mBatteryReattributeDao;
@Before
public void setUp() {
mContext = ApplicationProvider.getApplicationContext();
mTestStringWriter = new StringWriter();
mTestPrintWriter = new PrintWriter(mTestStringWriter);
mDatabase = BatteryTestUtils.setUpBatteryStateDatabase(mContext);
mBatteryReattributeDao = mDatabase.batteryReattributeDao();
}
@After
public void cleanUp() {
mBatteryReattributeDao.clearAll();
}
@Test
public void dumpBatteryReattributeDatabaseHist_noData_printExpectedResult() {
LogUtils.dumpBatteryReattributeDatabaseHist(mBatteryReattributeDao, mTestPrintWriter);
assertThat(mTestStringWriter.toString())
.contains("BatteryReattribute DatabaseHistory:");
}
@Test
public void dumpBatteryReattributeDatabaseHist_printExpectedResult() {
final long currentTimeMillis = System.currentTimeMillis();
// Insert the first testing data.
final BatteryReattribute batteryReattribute1 =
BatteryReattribute.newBuilder()
.setTimestampStart(currentTimeMillis - 20000)
.setTimestampEnd(currentTimeMillis - 10000)
.putReattributeData(1001, 0.1f)
.putReattributeData(1002, 0.99f)
.build();
mBatteryReattributeDao.insert(new BatteryReattributeEntity(batteryReattribute1));
// Insert the second testing data.
final BatteryReattribute batteryReattribute2 =
BatteryReattribute.newBuilder()
.setTimestampStart(currentTimeMillis - 40000)
.setTimestampEnd(currentTimeMillis - 20000)
.putReattributeData(1003, 1f)
.build();
mBatteryReattributeDao.insert(new BatteryReattributeEntity(batteryReattribute2));
LogUtils.dumpBatteryReattributeDatabaseHist(mBatteryReattributeDao, mTestPrintWriter);
final String result = mTestStringWriter.toString();
assertThat(result).contains("BatteryReattribute DatabaseHistory:");
assertThat(result).contains(batteryReattribute1.toString());
assertThat(result).contains(batteryReattribute2.toString());
}
}

View File

@@ -34,6 +34,7 @@ import android.content.Context;
import android.net.Uri;
import android.platform.test.annotations.EnableFlags;
import android.platform.test.flag.junit.SetFlagsRule;
import android.service.notification.SystemZenRules;
import android.service.notification.ZenModeConfig;
import androidx.preference.DropDownPreference;
@@ -85,7 +86,9 @@ public class ZenModeSetCalendarPreferenceControllerTest {
@EnableFlags({Flags.FLAG_MODES_API, Flags.FLAG_MODES_UI})
public void updateEventMode_updatesConditionAndTriggerDescription() {
ZenMode mode = new ZenMode("id",
new AutomaticZenRule.Builder("name", Uri.parse("condition")).build(),
new AutomaticZenRule.Builder("name", Uri.parse("condition"))
.setPackage(SystemZenRules.PACKAGE_ANDROID)
.build(),
true); // is active
// Explicitly update preference controller with mode info first, which will also call
@@ -99,6 +102,7 @@ public class ZenModeSetCalendarPreferenceControllerTest {
// apply event mode updater to existing mode
ZenMode out = mPrefController.updateEventMode(eventInfo).apply(mode);
assertThat(out.getRule().getOwner()).isEqualTo(ZenModeConfig.getEventConditionProvider());
assertThat(out.getRule().getConditionId()).isEqualTo(
ZenModeConfig.toEventConditionId(eventInfo));
assertThat(out.getRule().getTriggerDescription()).isEqualTo("My events");

View File

@@ -29,6 +29,7 @@ import android.content.Context;
import android.net.Uri;
import android.platform.test.annotations.EnableFlags;
import android.platform.test.flag.junit.SetFlagsRule;
import android.service.notification.SystemZenRules;
import android.service.notification.ZenModeConfig;
import android.view.ViewGroup;
import android.widget.ToggleButton;
@@ -81,7 +82,9 @@ public class ZenModeSetSchedulePreferenceControllerTest {
@EnableFlags({Flags.FLAG_MODES_API, Flags.FLAG_MODES_UI})
public void updateScheduleRule_updatesConditionAndTriggerDescription() {
ZenMode mode = new ZenMode("id",
new AutomaticZenRule.Builder("name", Uri.parse("condition")).build(),
new AutomaticZenRule.Builder("name", Uri.parse("condition"))
.setPackage(SystemZenRules.PACKAGE_ANDROID)
.build(),
true); // is active
ZenModeConfig.ScheduleInfo scheduleInfo = new ZenModeConfig.ScheduleInfo();
@@ -93,6 +96,8 @@ public class ZenModeSetSchedulePreferenceControllerTest {
assertThat(out.getRule().getConditionId())
.isEqualTo(ZenModeConfig.toScheduleConditionId(scheduleInfo));
assertThat(out.getRule().getTriggerDescription()).isNotEmpty();
assertThat(out.getRule().getOwner()).isEqualTo(
ZenModeConfig.getScheduleConditionProvider());
}
@Test

View File

@@ -16,6 +16,7 @@
package com.android.settings.notification.modes;
import static android.app.AutomaticZenRule.TYPE_OTHER;
import static android.app.AutomaticZenRule.TYPE_SCHEDULE_CALENDAR;
import static android.app.AutomaticZenRule.TYPE_SCHEDULE_TIME;
import static android.app.NotificationManager.INTERRUPTION_FILTER_PRIORITY;
@@ -31,10 +32,10 @@ import static org.mockito.Mockito.when;
import android.app.AutomaticZenRule;
import android.app.Flags;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.platform.test.annotations.EnableFlags;
import android.platform.test.flag.junit.SetFlagsRule;
import android.service.notification.SystemZenRules;
import android.service.notification.ZenModeConfig;
import android.service.notification.ZenPolicy;
@@ -43,6 +44,7 @@ import androidx.test.core.app.ApplicationProvider;
import com.android.settings.R;
import com.android.settings.SettingsActivity;
import com.android.settings.dashboard.DashboardFragment;
import com.android.settingslib.PrimarySwitchPreference;
import org.junit.Before;
@@ -57,6 +59,7 @@ import org.robolectric.RobolectricTestRunner;
import java.util.Calendar;
@RunWith(RobolectricTestRunner.class)
@EnableFlags(Flags.FLAG_MODES_UI)
public class ZenModeSetTriggerLinkPreferenceControllerTest {
@Rule
public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(DEVICE_DEFAULT);
@@ -65,10 +68,13 @@ public class ZenModeSetTriggerLinkPreferenceControllerTest {
private ZenModesBackend mBackend;
private Context mContext;
private PrimarySwitchPreference mPreference;
@Mock
private PreferenceCategory mPrefCategory;
@Mock
private PrimarySwitchPreference mPreference;
private DashboardFragment mFragment;
private ZenModeSetTriggerLinkPreferenceController mPrefController;
@Before
@@ -77,12 +83,12 @@ public class ZenModeSetTriggerLinkPreferenceControllerTest {
mContext = ApplicationProvider.getApplicationContext();
mPrefController = new ZenModeSetTriggerLinkPreferenceController(mContext,
"zen_automatic_trigger_category", mBackend);
"zen_automatic_trigger_category", mFragment, mBackend);
mPreference = new PrimarySwitchPreference(mContext);
when(mPrefCategory.findPreference(AUTOMATIC_TRIGGER_PREF_KEY)).thenReturn(mPreference);
}
@Test
@EnableFlags(Flags.FLAG_MODES_UI)
public void testIsAvailable() {
// should not be available for manual DND
ZenMode manualMode = ZenMode.manualDndMode(new AutomaticZenRule.Builder("Do Not Disturb",
@@ -117,12 +123,12 @@ public class ZenModeSetTriggerLinkPreferenceControllerTest {
// Update preference controller with a zen mode that is not enabled
mPrefController.updateZenMode(mPrefCategory, zenMode);
verify(mPreference).setChecked(false);
assertThat(mPreference.getCheckedState()).isFalse();
// Now with the rule enabled
zenMode.getRule().setEnabled(true);
mPrefController.updateZenMode(mPrefCategory, zenMode);
verify(mPreference).setChecked(true);
assertThat(mPreference.getCheckedState()).isTrue();
}
@Test
@@ -154,21 +160,24 @@ public class ZenModeSetTriggerLinkPreferenceControllerTest {
eventInfo.calName = "My events";
ZenMode mode = new ZenMode("id", new AutomaticZenRule.Builder("name",
ZenModeConfig.toEventConditionId(eventInfo))
.setPackage(SystemZenRules.PACKAGE_ANDROID)
.setType(TYPE_SCHEDULE_CALENDAR)
.setTriggerDescription("My events")
.build(),
true); // is active
mPrefController.updateZenMode(mPrefCategory, mode);
verify(mPreference).setTitle(R.string.zen_mode_set_calendar_link);
verify(mPreference).setSummary(mode.getRule().getTriggerDescription());
assertThat(mPreference.getTitle()).isNotNull();
assertThat(mPreference.getTitle().toString()).isEqualTo(
mContext.getString(R.string.zen_mode_set_calendar_link));
assertThat(mPreference.getSummary()).isNotNull();
assertThat(mPreference.getSummary().toString()).isEqualTo(
mode.getRule().getTriggerDescription());
assertThat(mPreference.getIcon()).isNull();
ArgumentCaptor<Intent> captor = ArgumentCaptor.forClass(Intent.class);
verify(mPreference).setIntent(captor.capture());
// Destination as written into the intent by SubSettingLauncher
assertThat(
captor.getValue().getStringExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT)).isEqualTo(
ZenModeSetCalendarFragment.class.getName());
assertThat(mPreference.getIntent().getStringExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT))
.isEqualTo(ZenModeSetCalendarFragment.class.getName());
}
@Test
@@ -179,20 +188,75 @@ public class ZenModeSetTriggerLinkPreferenceControllerTest {
scheduleInfo.endHour = 15;
ZenMode mode = new ZenMode("id", new AutomaticZenRule.Builder("name",
ZenModeConfig.toScheduleConditionId(scheduleInfo))
.setPackage(SystemZenRules.PACKAGE_ANDROID)
.setType(TYPE_SCHEDULE_TIME)
.setTriggerDescription("some schedule")
.build(),
true); // is active
mPrefController.updateZenMode(mPrefCategory, mode);
verify(mPreference).setTitle(R.string.zen_mode_set_schedule_link);
verify(mPreference).setSummary(mode.getRule().getTriggerDescription());
assertThat(mPreference.getTitle()).isNotNull();
assertThat(mPreference.getTitle().toString()).isEqualTo(
mContext.getString(R.string.zen_mode_set_schedule_link));
assertThat(mPreference.getSummary()).isNotNull();
assertThat(mPreference.getSummary().toString()).isEqualTo(
mode.getRule().getTriggerDescription());
assertThat(mPreference.getIcon()).isNull();
ArgumentCaptor<Intent> captor = ArgumentCaptor.forClass(Intent.class);
verify(mPreference).setIntent(captor.capture());
// Destination as written into the intent by SubSettingLauncher
assertThat(
captor.getValue().getStringExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT)).isEqualTo(
ZenModeSetScheduleFragment.class.getName());
assertThat(mPreference.getIntent().getStringExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT))
.isEqualTo(ZenModeSetScheduleFragment.class.getName());
}
@Test
public void testRuleLink_manual() {
ZenMode mode = new ZenMode("id", new AutomaticZenRule.Builder("name",
ZenModeConfig.toCustomManualConditionId())
.setPackage(SystemZenRules.PACKAGE_ANDROID)
.setType(TYPE_OTHER)
.setTriggerDescription("Will not be shown")
.build(),
true); // is active
mPrefController.updateZenMode(mPrefCategory, mode);
assertThat(mPreference.getTitle()).isNotNull();
assertThat(mPreference.getTitle().toString()).isEqualTo(
mContext.getString(R.string.zen_mode_select_schedule));
assertThat(mPreference.getIcon()).isNotNull();
assertThat(mPreference.getSummary()).isNotNull();
assertThat(mPreference.getSummary().toString()).isEqualTo("");
// Set up a click listener to open the dialog.
assertThat(mPreference.getOnPreferenceClickListener()).isNotNull();
}
@Test
public void onScheduleChosen_updatesMode() {
ZenMode originalMode = new ZenMode("id",
new AutomaticZenRule.Builder("name", ZenModeConfig.toCustomManualConditionId())
.setPackage(SystemZenRules.PACKAGE_ANDROID)
.setType(TYPE_OTHER)
.setTriggerDescription("")
.build(),
false);
mPrefController.updateZenMode(mPrefCategory, originalMode);
ZenModeConfig.ScheduleInfo scheduleInfo = new ZenModeConfig.ScheduleInfo();
scheduleInfo.days = new int[] { Calendar.MONDAY };
scheduleInfo.startHour = 12;
scheduleInfo.endHour = 15;
Uri scheduleUri = ZenModeConfig.toScheduleConditionId(scheduleInfo);
mPrefController.mOnScheduleOptionListener.onScheduleSelected(scheduleUri);
// verify the backend got asked to update the mode to be schedule-based.
ArgumentCaptor<ZenMode> captor = ArgumentCaptor.forClass(ZenMode.class);
verify(mBackend).updateMode(captor.capture());
ZenMode updatedMode = captor.getValue();
assertThat(updatedMode.getType()).isEqualTo(TYPE_SCHEDULE_TIME);
assertThat(updatedMode.getRule().getConditionId()).isEqualTo(scheduleUri);
assertThat(updatedMode.getRule().getTriggerDescription()).isNotEmpty();
assertThat(updatedMode.getRule().getOwner()).isEqualTo(
ZenModeConfig.getScheduleConditionProvider());
}
}

View File

@@ -0,0 +1,109 @@
/*
* Copyright (C) 2024 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.apn
import android.content.Context
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.test.assertIsDisplayed
import androidx.compose.ui.test.assertIsEnabled
import androidx.compose.ui.test.assertIsNotEnabled
import androidx.compose.ui.test.assertIsOff
import androidx.compose.ui.test.assertIsOn
import androidx.compose.ui.test.junit4.ComposeTestRule
import androidx.compose.ui.test.junit4.createComposeRule
import androidx.compose.ui.test.onNodeWithText
import androidx.compose.ui.test.performClick
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.android.settings.R
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.kotlin.doReturn
import org.mockito.kotlin.spy
import org.mockito.kotlin.stub
@RunWith(AndroidJUnit4::class)
class ApnEditCarrierEnabledTest {
@get:Rule val composeTestRule = createComposeRule()
private val context: Context = spy(ApplicationProvider.getApplicationContext()) {}
private val resources = spy(context.resources) {}
@Before
fun setUp() {
context.stub { on { resources } doReturn resources }
}
@Test
fun carrierEnabled_displayed() {
composeTestRule.setContent { ApnEditCarrierEnabled(ApnData()) {} }
composeTestRule.onCarrierEnabled().assertIsDisplayed()
}
@Test
fun carrierEnabled_isChecked() {
val apnData = ApnData(carrierEnabled = true)
composeTestRule.setContent { ApnEditCarrierEnabled(apnData) {} }
composeTestRule.onCarrierEnabled().assertIsOn()
}
@Test
fun carrierEnabled_allowEdit_checkChanged() {
resources.stub { on { getBoolean(R.bool.config_allow_edit_carrier_enabled) } doReturn true }
var apnData by mutableStateOf(ApnData(carrierEnabled = true))
composeTestRule.setContent {
CompositionLocalProvider(LocalContext provides context) {
ApnEditCarrierEnabled(apnData) { apnData = apnData.copy(carrierEnabled = it) }
}
}
composeTestRule.onCarrierEnabled().performClick()
composeTestRule.onCarrierEnabled().assertIsEnabled().assertIsOff()
}
@Test
fun carrierEnabled_notAllowEdit_checkNotChanged() {
resources.stub {
on { getBoolean(R.bool.config_allow_edit_carrier_enabled) } doReturn false
}
var apnData by mutableStateOf(ApnData(carrierEnabled = true))
composeTestRule.setContent {
CompositionLocalProvider(LocalContext provides context) {
ApnEditCarrierEnabled(apnData) { apnData = apnData.copy(carrierEnabled = it) }
}
}
composeTestRule.onCarrierEnabled().performClick()
composeTestRule.onCarrierEnabled().assertIsNotEnabled().assertIsOn()
}
private fun ComposeTestRule.onCarrierEnabled() =
onNodeWithText(context.getString(R.string.carrier_enabled))
}

View File

@@ -21,24 +21,17 @@ import android.net.Uri
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.ui.test.assertIsDisplayed
import androidx.compose.ui.test.assertIsOff
import androidx.compose.ui.test.assertIsOn
import androidx.compose.ui.test.hasText
import androidx.compose.ui.test.isFocused
import androidx.compose.ui.test.junit4.createComposeRule
import androidx.compose.ui.test.onAllNodesWithText
import androidx.compose.ui.test.onChild
import androidx.compose.ui.test.onChildAt
import androidx.compose.ui.test.onLast
import androidx.compose.ui.test.onNodeWithText
import androidx.compose.ui.test.onRoot
import androidx.compose.ui.test.performClick
import androidx.compose.ui.test.performScrollToNode
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.android.settings.R
import com.google.common.truth.Truth
import org.junit.Ignore
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
@@ -55,7 +48,6 @@ class ApnEditPageProviderTest {
private val port = "port"
private val apnType = context.resources.getString(R.string.apn_type)
private val apnRoaming = "IPv4"
private val apnEnable = context.resources.getString(R.string.carrier_enabled)
private val apnProtocolOptions =
context.resources.getStringArray(R.array.apn_protocol_entries).toList()
private val passwordTitle = context.resources.getString(R.string.apn_password)
@@ -65,7 +57,6 @@ class ApnEditPageProviderTest {
port = port,
apnType = apnType,
apnRoaming = apnProtocolOptions.indexOf(apnRoaming),
apnEnable = true
)
private val apnData = mutableStateOf(
apnInit
@@ -133,39 +124,6 @@ class ApnEditPageProviderTest {
composeTestRule.onNodeWithText(apnRoaming, true).assertIsDisplayed()
}
@Ignore("b/342374681")
@Test
fun carrier_enabled_displayed() {
composeTestRule.setContent {
ApnPage(apnInit, remember { apnData }, uri)
}
composeTestRule.onRoot().onChild().onChildAt(0)
.performScrollToNode(hasText(apnEnable, true))
composeTestRule.onNodeWithText(apnEnable, true).assertIsDisplayed()
}
@Test
fun carrier_enabled_isChecked() {
composeTestRule.setContent {
ApnPage(apnInit, remember { apnData }, uri)
}
composeTestRule.onRoot().onChild().onChildAt(0)
.performScrollToNode(hasText(apnEnable, true))
composeTestRule.onNodeWithText(apnEnable, true).assertIsOn()
}
@Ignore("b/342374681")
@Test
fun carrier_enabled_checkChanged() {
composeTestRule.setContent {
ApnPage(apnInit, remember { apnData }, uri)
}
composeTestRule.onRoot().onChild().onChildAt(0)
.performScrollToNode(hasText(apnEnable, true))
composeTestRule.onNodeWithText(apnEnable, true).performClick()
composeTestRule.onNodeWithText(apnEnable, true).assertIsOff()
}
@Test
fun password_displayed() {
composeTestRule.setContent {

View File

@@ -17,6 +17,7 @@
package com.android.settings.print
import android.content.Context
import android.net.Uri
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.test.isDisplayed
@@ -31,7 +32,9 @@ import com.android.settings.print.PrintRepository.PrintServiceDisplayInfo
import com.android.settings.print.PrintSettingsFragment.EXTRA_CHECKED
import com.android.settings.print.PrintSettingsFragment.EXTRA_SERVICE_COMPONENT_NAME
import com.android.settings.print.PrintSettingsFragment.EXTRA_TITLE
import com.android.settings.print.PrintSettingsPageProvider.AddPrintService
import com.android.settings.print.PrintSettingsPageProvider.PrintService
import kotlinx.coroutines.flow.flowOf
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
@@ -44,35 +47,32 @@ import org.mockito.kotlin.whenever
@RunWith(AndroidJUnit4::class)
class PrintSettingsPageProviderTest {
@get:Rule
val composeTestRule = createComposeRule()
@get:Rule val composeTestRule = createComposeRule()
private val context: Context = spy(ApplicationProvider.getApplicationContext()) {
doNothing().whenever(mock).startActivity(any())
}
private val context: Context =
spy(ApplicationProvider.getApplicationContext()) {
doNothing().whenever(mock).startActivity(any())
}
private val displayInfo = PrintServiceDisplayInfo(
title = TITLE,
isEnabled = true,
summary = SUMMARY,
icon = context.getDrawable(R.drawable.ic_settings_print)!!,
componentName = "ComponentName",
)
private val displayInfo =
PrintServiceDisplayInfo(
title = TITLE,
isEnabled = true,
summary = SUMMARY,
icon = context.getDrawable(R.drawable.ic_settings_print)!!,
componentName = "ComponentName",
)
@Test
fun printService_titleDisplayed() {
composeTestRule.setContent {
PrintService(displayInfo)
}
composeTestRule.setContent { PrintService(displayInfo) }
composeTestRule.onNodeWithText(TITLE).isDisplayed()
}
@Test
fun printService_summaryDisplayed() {
composeTestRule.setContent {
PrintService(displayInfo)
}
composeTestRule.setContent { PrintService(displayInfo) }
composeTestRule.onNodeWithText(SUMMARY).isDisplayed()
}
@@ -80,25 +80,43 @@ class PrintSettingsPageProviderTest {
@Test
fun printService_onClick() {
composeTestRule.setContent {
CompositionLocalProvider(LocalContext provides context) {
PrintService(displayInfo)
}
CompositionLocalProvider(LocalContext provides context) { PrintService(displayInfo) }
}
composeTestRule.onNodeWithText(TITLE).performClick()
verify(context).startActivity(argThat {
val fragment = getStringExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT)
val arguments = getBundleExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT_ARGUMENTS)!!
fragment == PrintServiceSettingsFragment::class.qualifiedName &&
arguments.getBoolean(EXTRA_CHECKED) == displayInfo.isEnabled &&
arguments.getString(EXTRA_TITLE) == displayInfo.title &&
arguments.getString(EXTRA_SERVICE_COMPONENT_NAME) == displayInfo.componentName
})
verify(context)
.startActivity(
argThat {
val fragment = getStringExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT)
val arguments = getBundleExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT_ARGUMENTS)!!
fragment == PrintServiceSettingsFragment::class.qualifiedName &&
arguments.getBoolean(EXTRA_CHECKED) == displayInfo.isEnabled &&
arguments.getString(EXTRA_TITLE) == displayInfo.title &&
arguments.getString(EXTRA_SERVICE_COMPONENT_NAME) ==
displayInfo.componentName
}
)
}
@Test
fun addPrintService_onClick() {
composeTestRule.setContent {
CompositionLocalProvider(LocalContext provides context) {
AddPrintService(flowOf(SEARCH_URI))
}
}
composeTestRule
.onNodeWithText(context.getString(R.string.print_menu_item_add_service))
.performClick()
verify(context).startActivity(argThat { data == Uri.parse(SEARCH_URI) })
}
private companion object {
const val TITLE = "Title"
const val SUMMARY = "Summary"
const val SEARCH_URI = "search.uri"
}
}

View File

@@ -16,20 +16,16 @@
package com.android.settings.network;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.isNull;
import static org.mockito.Mockito.anyInt;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import android.content.ContentProvider;
import android.content.ContentResolver;
import android.content.ContentProviderClient;
import android.content.Context;
import android.net.ConnectivityManager;
import android.net.NetworkPolicyManager;
@@ -67,7 +63,7 @@ public class ResetNetworkOperationBuilderTest {
@Mock
private NetworkPolicyManager mNetworkPolicyManager;
@Mock
private ContentProvider mContentProvider;;
private ContentProviderClient mContentProviderClient;
private Context mContext;
@@ -77,9 +73,8 @@ public class ResetNetworkOperationBuilderTest {
public void setUp() {
MockitoAnnotations.initMocks(this);
mContext = spy(ApplicationProvider.getApplicationContext());
doReturn(ContentResolver.wrap(mContentProvider)).when(mContext).getContentResolver();
mBuilder = spy(new ResetNetworkOperationBuilder(mContext));
doReturn(mContentProviderClient).when(mBuilder).getUnstableTelephonyContentProviderClient();
}
@Test
@@ -184,38 +179,38 @@ public class ResetNetworkOperationBuilderTest {
}
@Test
public void restartPhoneProcess_withoutTelephonyContentProvider_shouldNotCrash() {
doThrow(new IllegalArgumentException()).when(mContentProvider).call(
anyString(), anyString(), anyString(), any());
public void restartPhoneProcess_withoutTelephonyContentProvider_shouldNotCrash()
throws Exception {
doReturn(null).when(mBuilder).getUnstableTelephonyContentProviderClient();
mBuilder.restartPhoneProcess().build().run();
}
@Test
public void restartRild_withoutTelephonyContentProvider_shouldNotCrash() {
doThrow(new IllegalArgumentException()).when(mContentProvider).call(
anyString(), anyString(), anyString(), any());
public void restartRild_withoutTelephonyContentProvider_shouldNotCrash()
throws Exception {
doReturn(null).when(mBuilder).getUnstableTelephonyContentProviderClient();
mBuilder.restartRild().build().run();
}
@Test
public void restartPhoneProcess_withTelephonyContentProvider_shouldCallRestartPhoneProcess() {
public void restartPhoneProcess_withTelephonyContentProvider_shouldCallRestartPhoneProcess()
throws Exception {
mBuilder.restartPhoneProcess().build().run();
verify(mContentProvider).call(
eq(mBuilder.getResetTelephonyContentProviderAuthority()),
verify(mContentProviderClient).call(
eq(ResetNetworkOperationBuilder.METHOD_RESTART_PHONE_PROCESS),
isNull(),
isNull());
}
@Test
public void restartRild_withTelephonyContentProvider_shouldCallRestartRild() {
public void restartRild_withTelephonyContentProvider_shouldCallRestartRild()
throws Exception {
mBuilder.restartRild().build().run();
verify(mContentProvider).call(
eq(mBuilder.getResetTelephonyContentProviderAuthority()),
verify(mContentProviderClient).call(
eq(ResetNetworkOperationBuilder.METHOD_RESTART_RILD),
isNull(),
isNull());