Snap for 8503143 from ed5ba9a647 to tm-qpr1-release

Change-Id: I4a4a49435ff0249f6342b74734dd4ff5970c7330
This commit is contained in:
Android Build Coastguard Worker
2022-04-27 03:09:28 +00:00
26 changed files with 1185 additions and 192 deletions

View File

@@ -0,0 +1,18 @@
<?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.
-->
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:color="?android:attr/textColorPrimaryInverse" />
</selector>

View File

@@ -0,0 +1,18 @@
<?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.
-->
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:color="?android:attr/textColorSecondaryInverse" />
</selector>

View File

@@ -0,0 +1,18 @@
<?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.
-->
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:color="@android:color/system_accent2_500" android:lStar="80" />
</selector>

View File

@@ -0,0 +1,18 @@
<?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.
-->
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:color="?android:attr/textColorPrimary" />
</selector>

View File

@@ -0,0 +1,18 @@
<?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.
-->
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:color="?android:attr/textColorSecondary" />
</selector>

View File

@@ -17,7 +17,7 @@
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid
android:color="?android:attr/textColorPrimary" />
android:color="@color/accent_select_background" />
<corners
android:radius="@dimen/homepage_preference_corner_radius" />
</shape>

View File

@@ -0,0 +1,32 @@
<?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"
xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
android:color="@color/settingslib_ripple_color">
<item android:id="@android:id/mask">
<shape android:shape="rectangle">
<solid android:color="?androidprv:attr/colorSurface"/>
<corners android:radius="20dp"/>
</shape>
</item>
<item android:id="@android:id/background">
<shape android:shape="rectangle">
<stroke android:color="?androidprv:attr/colorAccentPrimaryVariant" android:width="1dp"/>
<corners android:radius="20dp"/>
</shape>
</item>
</ripple>

View File

@@ -0,0 +1,29 @@
<?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.
-->
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingTop="40dp"
android:paddingBottom="40dp">
<com.android.internal.widget.RecyclerView
android:id="@+id/list"
android:overScrollMode="never"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"/>
</FrameLayout>

View File

@@ -0,0 +1,47 @@
<?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.
-->
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@android:id/widget_frame"
android:layout_width="120dp"
android:layout_height="112dp">
<FrameLayout
android:id="@+id/button"
android:layout_width="82dp"
android:layout_height="82dp"
android:layout_alignParentTop="true"
android:layout_centerHorizontal="true"
android:background="@drawable/user_select_background">
<ImageView
android:id="@android:id/icon"
android:layout_width="32dp"
android:layout_height="32dp"
android:layout_gravity="center"
android:scaleType="fitCenter"/>
</FrameLayout>
<TextView
android:id="@android:id/title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_centerHorizontal="true"
android:labelFor="@android:id/icon"
android:textSize="14sp"
style="@style/TextAppearance.PreferenceTitle.SettingsLib"/>
</RelativeLayout>

View File

@@ -0,0 +1,27 @@
<?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.
-->
<com.android.internal.widget.DialogTitle
xmlns:android="http://schemas.android.com/apk/res/android"
style="?android:attr/windowTitleStyle"
android:id="@android:id/title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:paddingTop="24dp"
android:ellipsize="end"
android:singleLine="true"
android:textAlignment="center"
android:textSize="24sp"/>

View File

@@ -85,7 +85,6 @@ import android.text.TextUtils;
import android.text.format.DateUtils;
import android.text.style.TtsSpan;
import android.util.ArraySet;
import android.util.FeatureFlagUtils;
import android.util.IconDrawableFactory;
import android.util.Log;
import android.view.LayoutInflater;
@@ -1220,7 +1219,7 @@ public final class Utils extends com.android.settingslib.Utils {
*/
@ColorInt
public static int getHomepageIconColor(Context context) {
return getColorAttrDefaultColor(context, android.R.attr.textColorSecondary);
return getColorAttrDefaultColor(context, android.R.attr.textColorPrimary);
}
/**
@@ -1228,6 +1227,6 @@ public final class Utils extends com.android.settingslib.Utils {
*/
@ColorInt
public static int getHomepageIconColorHighlight(Context context) {
return getColorAttrDefaultColor(context, android.R.attr.textColorSecondaryInverse);
return context.getColor(R.color.accent_select_primary_text);
}
}

View File

@@ -470,9 +470,7 @@ public class ToggleScreenMagnificationPreferenceFragment extends
mShortcutPreference.setKey(getShortcutPreferenceKey());
mShortcutPreference.setSummary(getShortcutTypeSummary(getPrefContext()));
mShortcutPreference.setOnClickCallback(this);
final CharSequence title = getString(R.string.accessibility_shortcut_title, mPackageName);
mShortcutPreference.setTitle(title);
mShortcutPreference.setTitle(getShortcutTitle());
final PreferenceCategory generalCategory = findPreference(KEY_GENERAL_CATEGORY);
generalCategory.addPreference(mShortcutPreference);

View File

@@ -20,7 +20,6 @@ import android.app.Dialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.DialogInterface.OnCancelListener;
import android.content.DialogInterface.OnClickListener;
import android.content.DialogInterface.OnDismissListener;
import android.content.DialogInterface.OnShowListener;
import android.content.Intent;
@@ -28,17 +27,27 @@ import android.os.Bundle;
import android.os.UserHandle;
import android.os.UserManager;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AlertDialog;
import androidx.fragment.app.DialogFragment;
import androidx.fragment.app.FragmentManager;
import com.android.internal.widget.DialogTitle;
import com.android.internal.widget.LinearLayoutManager;
import com.android.internal.widget.RecyclerView;
import com.android.settings.R;
import com.android.settings.overlay.FeatureFactory;
import com.android.settingslib.drawer.Tile;
import java.util.List;
public class ProfileSelectDialog extends DialogFragment implements OnClickListener {
/**
* A {@link DialogFragment} that can select one of the different profiles.
*/
public class ProfileSelectDialog extends DialogFragment implements UserAdapter.OnClickListener {
private static final String TAG = "ProfileSelectDialog";
private static final String ARG_SELECTED_TILE = "selectedTile";
@@ -53,6 +62,7 @@ public class ProfileSelectDialog extends DialogFragment implements OnClickListen
/**
* Display the profile select dialog, adding the fragment to the given FragmentManager.
*
* @param manager The FragmentManager this fragment will be added to.
* @param tile The tile for this fragment.
* @param sourceMetricCategory The source metric category.
@@ -77,32 +87,53 @@ public class ProfileSelectDialog extends DialogFragment implements OnClickListen
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mSelectedTile = getArguments().getParcelable(ARG_SELECTED_TILE);
mSourceMetricCategory = getArguments().getInt(ARG_SOURCE_METRIC_CATEGORY);
Bundle arguments = requireArguments();
mSelectedTile = arguments.getParcelable(ARG_SELECTED_TILE, Tile.class);
mSourceMetricCategory = arguments.getInt(ARG_SOURCE_METRIC_CATEGORY);
}
@NonNull
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
final Context context = getActivity();
final AlertDialog.Builder builder = new AlertDialog.Builder(context);
final UserAdapter adapter = UserAdapter.createUserAdapter(UserManager.get(context), context,
mSelectedTile.userHandle);
builder.setTitle(com.android.settingslib.R.string.choose_profile)
.setAdapter(adapter, this);
return createDialog(getContext(), mSelectedTile.userHandle, this);
}
return builder.create();
/**
* Creates the profile select dialog.
*/
public static Dialog createDialog(Context context, List<UserHandle> userProfiles,
UserAdapter.OnClickListener onClickListener) {
LayoutInflater layoutInflater = context.getSystemService(LayoutInflater.class);
DialogTitle titleView =
(DialogTitle) layoutInflater.inflate(R.layout.user_select_title, null);
titleView.setText(com.android.settingslib.R.string.choose_profile);
View contentView = layoutInflater.inflate(R.layout.user_select, null);
RecyclerView listView = contentView.findViewById(R.id.list);
listView.setAdapter(
UserAdapter.createUserRecycleViewAdapter(context, userProfiles, onClickListener));
listView.setLayoutManager(
new LinearLayoutManager(context, LinearLayoutManager.HORIZONTAL, false));
return new AlertDialog.Builder(context)
.setCustomTitle(titleView)
.setView(contentView)
.create();
}
@Override
public void onClick(DialogInterface dialog, int which) {
final UserHandle user = mSelectedTile.userHandle.get(which);
public void onClick(int position) {
final UserHandle user = mSelectedTile.userHandle.get(position);
// Show menu on top level items.
final Intent intent = new Intent(mSelectedTile.getIntent());
FeatureFactory.getFactory(getContext()).getMetricsFeatureProvider()
.logStartedIntentWithProfile(intent, mSourceMetricCategory,
which == 1 /* isWorkProfile */);
position == 1 /* isWorkProfile */);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
getActivity().startActivityAsUser(intent, user);
dismiss();
}
@Override

View File

@@ -18,188 +18,156 @@ package com.android.settings.dashboard.profileselector;
import static android.app.admin.DevicePolicyResources.Strings.Settings.PERSONAL_CATEGORY_HEADER;
import static android.app.admin.DevicePolicyResources.Strings.Settings.WORK_CATEGORY_HEADER;
import static android.app.admin.DevicePolicyResources.Strings.Settings.WORK_PROFILE_USER_LABEL;
import android.app.ActivityManager;
import android.app.admin.DevicePolicyManager;
import android.app.admin.DevicePolicyResourcesManager;
import android.content.Context;
import android.content.pm.UserInfo;
import android.database.DataSetObserver;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.os.UserHandle;
import android.os.UserManager;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.ListAdapter;
import android.widget.SpinnerAdapter;
import android.widget.TextView;
import com.android.internal.util.UserIcons;
import com.android.internal.widget.RecyclerView;
import com.android.settingslib.R;
import com.android.settingslib.drawable.UserIconDrawable;
import com.android.settingslib.Utils;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
/**
* Adapter for a spinner that shows a list of users.
*/
public class UserAdapter implements SpinnerAdapter, ListAdapter {
public class UserAdapter extends BaseAdapter {
/** Holder for user details */
public static class UserDetails {
private final UserHandle mUserHandle;
private final String mName;
private final Drawable mIcon;
private final String mTitle;
public UserDetails(UserHandle userHandle, UserManager um, Context context) {
mUserHandle = userHandle;
UserInfo userInfo = um.getUserInfo(mUserHandle.getIdentifier());
Drawable icon;
int tintColor = Utils.getColorAttrDefaultColor(context,
com.android.internal.R.attr.colorAccentPrimaryVariant);
if (userInfo.isManagedProfile()) {
mName = context.getSystemService(DevicePolicyManager.class).getResources()
.getString(WORK_PROFILE_USER_LABEL,
() -> context.getString(R.string.managed_user_title));
icon = context.getPackageManager().getUserBadgeForDensityNoBackground(
mIcon = context.getPackageManager().getUserBadgeForDensityNoBackground(
userHandle, /* density= */ 0);
mIcon.setTint(tintColor);
} else {
mName = userInfo.name;
final int userId = userInfo.id;
if (um.getUserIcon(userId) != null) {
icon = new BitmapDrawable(context.getResources(), um.getUserIcon(userId));
} else {
icon = UserIcons.getDefaultUserIcon(
context.getResources(), userId, /* light= */ false);
mIcon = UserIcons.getDefaultUserIconInColor(context.getResources(), tintColor);
}
}
this.mIcon = encircle(context, icon);
mTitle = getTitle(context);
}
private static Drawable encircle(Context context, Drawable icon) {
return new UserIconDrawable(UserIconDrawable.getDefaultSize(context))
.setIconDrawable(icon).bake();
private String getTitle(Context context) {
DevicePolicyManager devicePolicyManager =
Objects.requireNonNull(context.getSystemService(DevicePolicyManager.class));
DevicePolicyResourcesManager resources = devicePolicyManager.getResources();
int userHandle = mUserHandle.getIdentifier();
if (userHandle == UserHandle.USER_CURRENT
|| userHandle == ActivityManager.getCurrentUser()) {
return resources.getString(PERSONAL_CATEGORY_HEADER,
() -> context.getString(R.string.category_personal));
} else {
return resources.getString(WORK_CATEGORY_HEADER,
() -> context.getString(R.string.category_work));
}
}
}
private ArrayList<UserDetails> data;
private final Context mContext;
private final ArrayList<UserDetails> mUserDetails;
private final LayoutInflater mInflater;
private final DevicePolicyManager mDevicePolicyManager;
public UserAdapter(Context context, ArrayList<UserDetails> users) {
if (users == null) {
throw new IllegalArgumentException("A list of user details must be provided");
}
mContext = context;
this.data = users;
mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
mDevicePolicyManager = context.getSystemService(DevicePolicyManager.class);
mUserDetails = users;
mInflater = context.getSystemService(LayoutInflater.class);
}
public UserHandle getUserHandle(int position) {
if (position < 0 || position >= data.size()) {
if (position < 0 || position >= mUserDetails.size()) {
return null;
}
return data.get(position).mUserHandle;
}
@Override
public View getDropDownView(int position, View convertView, ViewGroup parent) {
final View row = convertView != null ? convertView : createUser(parent);
UserDetails user = data.get(position);
((ImageView) row.findViewById(android.R.id.icon)).setImageDrawable(user.mIcon);
((TextView) row.findViewById(android.R.id.title)).setText(getTitle(user));
return row;
}
private String getTitle(UserDetails user) {
int userHandle = user.mUserHandle.getIdentifier();
if (userHandle == UserHandle.USER_CURRENT
|| userHandle == ActivityManager.getCurrentUser()) {
return mDevicePolicyManager.getResources().getString(PERSONAL_CATEGORY_HEADER,
() -> mContext.getString(R.string.category_personal));
} else {
return mDevicePolicyManager.getResources().getString(WORK_CATEGORY_HEADER,
() -> mContext.getString(R.string.category_work));
}
}
private View createUser(ViewGroup parent) {
return mInflater.inflate(R.layout.user_preference, parent, false);
}
@Override
public void registerDataSetObserver(DataSetObserver observer) {
// We don't support observers
}
@Override
public void unregisterDataSetObserver(DataSetObserver observer) {
// We don't support observers
}
@Override
public int getCount() {
return data.size();
}
@Override
public UserAdapter.UserDetails getItem(int position) {
return data.get(position);
}
@Override
public long getItemId(int position) {
return data.get(position).mUserHandle.getIdentifier();
}
@Override
public boolean hasStableIds() {
return false;
return mUserDetails.get(position).mUserHandle;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
return getDropDownView(position, convertView, parent);
ViewHolder holder;
if (convertView != null) {
holder = (ViewHolder) convertView.getTag();
} else {
convertView = mInflater.inflate(R.layout.user_preference, parent, false);
holder = new ViewHolder(convertView);
convertView.setTag(holder);
}
bindViewHolder(holder, position);
return convertView;
}
private void bindViewHolder(ViewHolder holder, int position) {
UserDetails userDetails = getItem(position);
holder.getIconView().setImageDrawable(userDetails.mIcon);
holder.getTitleView().setText(userDetails.mTitle);
}
@Override
public int getItemViewType(int position) {
return 0;
public int getCount() {
return mUserDetails.size();
}
@Override
public int getViewTypeCount() {
return 1;
public UserAdapter.UserDetails getItem(int position) {
return mUserDetails.get(position);
}
@Override
public boolean isEmpty() {
return data.isEmpty();
public long getItemId(int position) {
return mUserDetails.get(position).mUserHandle.getIdentifier();
}
private RecyclerView.Adapter<ViewHolder> createRecyclerViewAdapter(
OnClickListener onClickListener) {
return new RecyclerView.Adapter<ViewHolder>() {
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext())
.inflate(R.layout.user_select_item, parent, false);
return new ViewHolder(view, onClickListener);
}
@Override
public boolean areAllItemsEnabled() {
return true;
public void onBindViewHolder(ViewHolder holder, int position) {
UserAdapter.this.bindViewHolder(holder, position);
}
@Override
public boolean isEnabled(int position) {
return true;
public int getItemCount() {
return getCount();
}
};
}
/**
* Creates a {@link UserAdapter} if there is more than one
* profile on the device.
* Creates a {@link UserAdapter} if there is more than one profile on the device.
*
* <p> The adapter can be used to populate a spinner that switches between the Settings
* app on the different profiles.
* <p> The adapter can be used to populate a spinner that switches between the different
* profiles.
*
* @return a {@link UserAdapter} or null if there is only one
* profile.
* @return a {@link UserAdapter} or null if there is only one profile.
*/
public static UserAdapter createUserSpinnerAdapter(UserManager userManager, Context context) {
List<UserHandle> userProfiles = userManager.getUserProfiles();
@@ -215,13 +183,60 @@ public class UserAdapter implements SpinnerAdapter, ListAdapter {
return createUserAdapter(userManager, context, userProfiles);
}
public static UserAdapter createUserAdapter(
/**
* Creates a {@link RecyclerView} adapter which be used to populate a {@link RecyclerView} that
* select one of the different profiles.
*/
public static RecyclerView.Adapter<ViewHolder> createUserRecycleViewAdapter(
Context context, List<UserHandle> userProfiles, OnClickListener onClickListener) {
UserManager systemService = context.getSystemService(UserManager.class);
return createUserAdapter(systemService, context, userProfiles)
.createRecyclerViewAdapter(onClickListener);
}
private static UserAdapter createUserAdapter(
UserManager userManager, Context context, List<UserHandle> userProfiles) {
ArrayList<UserDetails> userDetails = new ArrayList<>(userProfiles.size());
final int count = userProfiles.size();
for (int i = 0; i < count; i++) {
userDetails.add(new UserDetails(userProfiles.get(i), userManager, context));
for (UserHandle userProfile : userProfiles) {
userDetails.add(new UserDetails(userProfile, userManager, context));
}
return new UserAdapter(context, userDetails);
}
static class ViewHolder extends RecyclerView.ViewHolder {
private final ImageView mIconView;
private final TextView mTitleView;
private ViewHolder(View view) {
super(view);
mIconView = view.findViewById(android.R.id.icon);
mTitleView = view.findViewById(android.R.id.title);
}
private ViewHolder(View view, OnClickListener onClickListener) {
this(view);
View button = view.findViewById(R.id.button);
if (button != null) {
button.setOnClickListener(v -> onClickListener.onClick(getAdapterPosition()));
}
}
private ImageView getIconView() {
return mIconView;
}
private TextView getTitleView() {
return mTitleView;
}
}
/**
* Interface definition for a callback to be invoked when a user is clicked.
*/
public interface OnClickListener {
/**
* Called when a user has been clicked.
*/
void onClick(int position);
}
}

View File

@@ -14,6 +14,7 @@
package com.android.settings.display;
import android.content.Context;
import android.os.UserManager;
import androidx.preference.Preference;
@@ -32,8 +33,11 @@ public class ScreenSaverPreferenceController extends AbstractPreferenceControlle
@Override
public boolean isAvailable() {
return mContext.getResources().getBoolean(
final boolean dreamsSupported = mContext.getResources().getBoolean(
com.android.internal.R.bool.config_dreamsSupported);
final boolean dreamsOnlyEnabledForSystemUser = mContext.getResources().getBoolean(
com.android.internal.R.bool.config_dreamsOnlyEnabledForSystemUser);
return dreamsSupported && (!dreamsOnlyEnabledForSystemUser || isSystemUser());
}
@Override
@@ -45,4 +49,9 @@ public class ScreenSaverPreferenceController extends AbstractPreferenceControlle
public void updateState(Preference preference) {
preference.setSummary(DreamSettings.getSummaryTextWithDreamName(mContext));
}
private boolean isSystemUser() {
final UserManager userManager = mContext.getSystemService(UserManager.class);
return userManager.isSystemUser();
}
}

View File

@@ -0,0 +1,104 @@
/*
* 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.network.helper;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Handler;
import androidx.annotation.NonNull;
import androidx.annotation.VisibleForTesting;
import androidx.lifecycle.Lifecycle;
import java.util.function.Consumer;
/**
* A {@link BroadcastReceiver} for {@link Intent}.
*
* This is {@link BroadcastReceiver} supported by {@link LifecycleCallbackConverter},
* and only register when state is either START or RESUME.
*/
@VisibleForTesting
public class LifecycleCallbackIntentReceiver extends LifecycleCallbackConverter<Intent> {
private static final String TAG = "LifecycleCallbackIntentReceiver";
@VisibleForTesting
protected final BroadcastReceiver mReceiver;
private final Runnable mRegisterCallback;
private final Runnable mUnRegisterCallback;
/**
* Constructor
* @param lifecycle {@link Lifecycle} to monitor
* @param context for this BroadcastReceiver
* @param filter the IntentFilter for BroadcastReceiver
* @param broadcastPermission for permission when listening
* @param scheduler for running in background thread
* @param resultCallback for the Intent from BroadcastReceiver
*/
@VisibleForTesting
public LifecycleCallbackIntentReceiver(@NonNull Lifecycle lifecycle,
@NonNull Context context, @NonNull IntentFilter filter,
String broadcastPermission, Handler scheduler,
@NonNull Consumer<Intent> resultCallback) {
super(lifecycle, resultCallback);
// BroadcastReceiver
mReceiver = new BroadcastReceiver() {
public void onReceive(Context context, Intent intent) {
if (isInitialStickyBroadcast()) {
return;
}
final String action = intent.getAction();
if ((action == null) || (action.length() <= 0)) {
return;
}
postResult(intent);
}
};
// Register operation
mRegisterCallback = () -> {
Intent initIntent = context.registerReceiver(mReceiver,
filter, broadcastPermission, scheduler);
if (initIntent != null) {
postResult(initIntent);
}
};
// Un-Register operation
mUnRegisterCallback = () -> {
context.unregisterReceiver(mReceiver);
};
}
@Override
public void setCallbackActive(boolean isActive) {
super.setCallbackActive(isActive);
Runnable op = (isActive) ? mRegisterCallback : mUnRegisterCallback;
op.run();
}
@Override
public void close() {
super.close();
if (isCallbackActive()) {
setCallbackActive(false);
}
}
}

View File

@@ -0,0 +1,72 @@
/*
* 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.network.helper;
import android.telephony.TelephonyCallback;
import android.telephony.TelephonyManager;
import androidx.annotation.NonNull;
import androidx.annotation.VisibleForTesting;
import androidx.lifecycle.Lifecycle;
import java.util.concurrent.Executor;
import java.util.function.Consumer;
/**
* A {@link LifecycleCallbackConverter} for supporting the register/unregister work for
* {@link TelephonyCallback}.
*/
@VisibleForTesting
public class LifecycleCallbackTelephonyAdapter<T> extends LifecycleCallbackConverter<T> {
private static final String TAG = "LifecycleCallbackTelephony";
private final Runnable mRegisterCallback;
private final Runnable mUnRegisterCallback;
/**
* Constructor
* @param lifecycle {@link Lifecycle} to monitor
* @param telephonyManager {@link TelephonyManager} to interact with
* @param telephonyCallback {@link TelephonyCallback}
* @param executor {@link Executor} for receiving the notify from telephony framework.
* @param resultCallback for the result from {@link TelephonyCallback}
*/
@VisibleForTesting
public LifecycleCallbackTelephonyAdapter(@NonNull Lifecycle lifecycle,
@NonNull TelephonyManager telephonyManager,
@NonNull TelephonyCallback telephonyCallback,
Executor executor, @NonNull Consumer<T> resultCallback) {
super(lifecycle, resultCallback);
// Register operation
mRegisterCallback = () -> {
telephonyManager.registerTelephonyCallback(executor, telephonyCallback);
};
// Un-Register operation
mUnRegisterCallback = () -> {
telephonyManager.unregisterTelephonyCallback(telephonyCallback);
};
}
@Override
public void setCallbackActive(boolean isActive) {
super.setCallbackActive(isActive);
Runnable op = (isActive) ? mRegisterCallback : mUnRegisterCallback;
op.run();
}
}

View File

@@ -326,7 +326,7 @@ public class ToggleSubscriptionDialogActivity extends SubscriptionActionDialogAc
/* Handles the enabling SIM action. */
private void showEnableSubDialog() {
Log.i(TAG, "Handle subscription enabling.");
Log.d(TAG, "Handle subscription enabling.");
if (isDsdsConditionSatisfied()) {
showEnableDsdsConfirmDialog();
return;
@@ -452,7 +452,7 @@ public class ToggleSubscriptionDialogActivity extends SubscriptionActionDialogAc
}
private void showMepSwitchSimConfirmDialog() {
Log.i(TAG, "showMepSwitchSimConfirmDialog");
Log.d(TAG, "showMepSwitchSimConfirmDialog");
final CharSequence displayName = SubscriptionUtil.getUniqueSubscriptionDisplayName(
mSubInfo, this);
String title = getString(R.string.sim_action_switch_sub_dialog_mep_title, displayName);
@@ -556,27 +556,35 @@ public class ToggleSubscriptionDialogActivity extends SubscriptionActionDialogAc
private boolean isDsdsConditionSatisfied() {
if (mTelMgr.isMultiSimEnabled()) {
Log.i(TAG, "DSDS is already enabled. Condition not satisfied.");
Log.d(TAG, "DSDS is already enabled. Condition not satisfied.");
return false;
}
if (mTelMgr.isMultiSimSupported() != TelephonyManager.MULTISIM_ALLOWED) {
Log.i(TAG, "Hardware does not support DSDS.");
Log.d(TAG, "Hardware does not support DSDS.");
return false;
}
boolean isActiveSim = SubscriptionUtil.getActiveSubscriptions(
mSubscriptionManager).size() > 0;
if (isMultipleEnabledProfilesSupported() && isActiveSim) {
Log.d(TAG,
"Device supports MEP and eSIM operation and eSIM profile is enabled."
+ " DSDS condition satisfied.");
return true;
}
boolean isRemovableSimEnabled = isRemovableSimEnabled();
if (mIsEsimOperation && isRemovableSimEnabled) {
Log.i(TAG, "eSIM operation and removable SIM is enabled. DSDS condition satisfied.");
Log.d(TAG, "eSIM operation and removable SIM is enabled. DSDS condition satisfied.");
return true;
}
boolean isEsimProfileEnabled =
SubscriptionUtil.getActiveSubscriptions(mSubscriptionManager).stream()
.anyMatch(SubscriptionInfo::isEmbedded);
if (!mIsEsimOperation && isEsimProfileEnabled) {
Log.i(TAG, "Removable SIM operation and eSIM profile is enabled. DSDS condition"
Log.d(TAG, "Removable SIM operation and eSIM profile is enabled. DSDS condition"
+ " satisfied.");
return true;
}
Log.i(TAG, "DSDS condition not satisfied.");
Log.d(TAG, "DSDS condition not satisfied.");
return false;
}

View File

@@ -93,7 +93,7 @@ public class NfcForegroundPreferenceController extends BasePreferenceController
return;
}
final ListPreference listPreference = (ListPreference) preference;
listPreference.setIconSpaceReserved(true);
listPreference.setIconSpaceReserved(false);
listPreference.setValue(mListValues[mPaymentBackend.isForegroundMode() ? 1 : 0]);
}

View File

@@ -19,19 +19,18 @@ package com.android.settings.privacy;
import android.annotation.NonNull;
import android.content.ComponentName;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.UserInfo;
import android.os.UserHandle;
import android.os.UserManager;
import android.text.TextUtils;
import android.util.Log;
import androidx.appcompat.app.AlertDialog;
import androidx.preference.Preference;
import com.android.settings.R;
import com.android.settings.core.TogglePreferenceController;
import com.android.settings.dashboard.profileselector.UserAdapter;
import com.android.settings.dashboard.profileselector.ProfileSelectDialog;
import com.android.settings.utils.ContentCaptureUtils;
import java.util.ArrayList;
@@ -42,13 +41,9 @@ public final class EnableContentCaptureWithServiceSettingsPreferenceController
private static final String TAG = "ContentCaptureController";
private final UserManager mUserManager;
public EnableContentCaptureWithServiceSettingsPreferenceController(@NonNull Context context,
@NonNull String key) {
super(context, key);
mUserManager = UserManager.get(context);
}
@Override
@@ -74,11 +69,6 @@ public final class EnableContentCaptureWithServiceSettingsPreferenceController
Log.w(TAG, "No component name for custom service settings");
preference.setSelectable(false);
}
preference.setOnPreferenceClickListener((pref) -> {
ProfileSelectDialog.show(mContext, pref);
return true;
});
}
@Override
@@ -93,32 +83,30 @@ public final class EnableContentCaptureWithServiceSettingsPreferenceController
return R.string.menu_key_privacy;
}
private static final class ProfileSelectDialog {
public static void show(Context context, Preference pref) {
final UserManager userManager = UserManager.get(context);
final List<UserInfo> userInfos = userManager.getUsers();
final ArrayList<UserHandle> userHandles = new ArrayList<>(userInfos.size());
for (UserInfo info: userInfos) {
userHandles.add(info.getUserHandle());
}
if (userHandles.size() == 1) {
final Intent intent = pref.getIntent().addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
context.startActivityAsUser(intent, userHandles.get(0));
} else {
AlertDialog.Builder builder = new AlertDialog.Builder(context);
UserAdapter adapter = UserAdapter.createUserAdapter(userManager, context,
userHandles);
builder.setTitle(com.android.settingslib.R.string.choose_profile)
.setAdapter(adapter, (DialogInterface dialog, int which) -> {
final UserHandle user = userHandles.get(which);
// Show menu on top level items.
final Intent intent = pref.getIntent()
.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
context.startActivityAsUser(intent, user);
})
.show();
}
@Override
public boolean handlePreferenceTreeClick(Preference preference) {
if (!TextUtils.equals(preference.getKey(), getPreferenceKey())) {
return false;
}
show(preference);
return true;
}
private void show(Preference preference) {
final UserManager userManager = UserManager.get(mContext);
final List<UserInfo> userInfos = userManager.getUsers();
final ArrayList<UserHandle> userHandles = new ArrayList<>(userInfos.size());
for (UserInfo info : userInfos) {
userHandles.add(info.getUserHandle());
}
final Intent intent = preference.getIntent().addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
if (userHandles.size() == 1) {
mContext.startActivityAsUser(intent, userHandles.get(0));
return;
}
ProfileSelectDialog.createDialog(mContext, userHandles, (int position) -> {
// Show menu on top level items.
mContext.startActivityAsUser(intent, userHandles.get(position));
}).show();
}
}

View File

@@ -78,12 +78,10 @@ public class HighlightableTopLevelPreferenceAdapter extends PreferenceGroupAdapt
Context context = preferenceGroup.getContext();
mTitleColorNormal = Utils.getColorAttrDefaultColor(context,
android.R.attr.textColorPrimary);
mTitleColorHighlight = Utils.getColorAttrDefaultColor(context,
android.R.attr.textColorPrimaryInverse);
mTitleColorHighlight = context.getColor(R.color.accent_select_primary_text);
mSummaryColorNormal = Utils.getColorAttrDefaultColor(context,
android.R.attr.textColorSecondary);
mSummaryColorHighlight = Utils.getColorAttrDefaultColor(context,
android.R.attr.textColorSecondaryInverse);
mSummaryColorHighlight = context.getColor(R.color.accent_select_secondary_text);
mIconColorNormal = Utils.getHomepageIconColor(context);
mIconColorHighlight = Utils.getHomepageIconColorHighlight(context);
}

View File

@@ -23,21 +23,29 @@ import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.app.Dialog;
import android.content.Context;
import android.content.pm.ActivityInfo;
import android.content.pm.UserInfo;
import android.os.UserHandle;
import android.os.UserManager;
import android.widget.TextView;
import androidx.test.core.app.ApplicationProvider;
import com.android.settings.R;
import com.android.settingslib.drawer.ActivityTile;
import com.android.settingslib.drawer.CategoryKey;
import com.android.settingslib.drawer.Tile;
import com.google.android.collect.Lists;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.mockito.Spy;
import org.robolectric.RobolectricTestRunner;
@RunWith(RobolectricTestRunner.class)
@@ -46,8 +54,9 @@ public class ProfileSelectDialogTest {
private static final UserHandle NORMAL_USER = new UserHandle(1111);
private static final UserHandle REMOVED_USER = new UserHandle(2222);
@Mock
private Context mContext;
@Spy
private Context mContext = ApplicationProvider.getApplicationContext();
@Mock
private UserManager mUserManager;
@@ -91,4 +100,18 @@ public class ProfileSelectDialogTest {
verify(mUserManager, times(1)).getUserInfo(NORMAL_USER.getIdentifier());
verify(mUserManager, times(2)).getUserInfo(REMOVED_USER.getIdentifier());
}
@Test
public void createDialog_showsCorrectTitle() {
mContext.setTheme(R.style.Theme_AppCompat);
Dialog dialog = ProfileSelectDialog.createDialog(mContext, Lists.newArrayList(NORMAL_USER),
(position) -> {
});
dialog.show();
TextView titleView = dialog.findViewById(R.id.topPanel).findViewById(android.R.id.title);
assertThat(titleView.getText().toString()).isEqualTo(
mContext.getText(com.android.settingslib.R.string.choose_profile).toString());
}
}

View File

@@ -0,0 +1,122 @@
/*
* 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.dashboard.profileselector;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.content.Context;
import android.content.pm.UserInfo;
import android.os.UserHandle;
import android.os.UserManager;
import android.widget.FrameLayout;
import android.widget.TextView;
import androidx.test.core.app.ApplicationProvider;
import com.android.internal.widget.RecyclerView;
import com.android.settingslib.R;
import com.google.android.collect.Lists;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.Spy;
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
import org.robolectric.RobolectricTestRunner;
import java.util.ArrayList;
@RunWith(RobolectricTestRunner.class)
public class UserAdapterTest {
@Rule
public MockitoRule mRule = MockitoJUnit.rule();
private final int mPersonalUserId = UserHandle.myUserId();
private static final int WORK_USER_ID = 1;
@Mock
private UserManager mUserManager;
@Mock
private UserInfo mPersonalUserInfo;
@Mock
private UserInfo mWorkUserInfo;
@Mock
private UserAdapter.OnClickListener mOnClickListener;
@Spy
private Context mContext = ApplicationProvider.getApplicationContext();
@Before
public void setUp() {
when(mContext.getSystemService(UserManager.class)).thenReturn(mUserManager);
when(mUserManager.getUserInfo(mPersonalUserId)).thenReturn(mPersonalUserInfo);
when(mUserManager.getUserInfo(WORK_USER_ID)).thenReturn(mWorkUserInfo);
}
@Test
public void createUserSpinnerAdapter_singleProfile_returnsNull() {
when(mUserManager.getUserProfiles()).thenReturn(
Lists.newArrayList(UserHandle.of(mPersonalUserId)));
UserAdapter userSpinnerAdapter =
UserAdapter.createUserSpinnerAdapter(mUserManager, mContext);
assertThat(userSpinnerAdapter).isNull();
}
@Test
public void createUserSpinnerAdapter_twoProfiles_succeed() {
when(mUserManager.getUserProfiles()).thenReturn(
Lists.newArrayList(UserHandle.of(mPersonalUserId), UserHandle.of(WORK_USER_ID)));
UserAdapter userSpinnerAdapter =
UserAdapter.createUserSpinnerAdapter(mUserManager, mContext);
assertThat(userSpinnerAdapter.getCount()).isEqualTo(2);
assertThat(userSpinnerAdapter.getUserHandle(0).getIdentifier()).isEqualTo(mPersonalUserId);
assertThat(userSpinnerAdapter.getUserHandle(1).getIdentifier()).isEqualTo(WORK_USER_ID);
}
@Test
public void createUserRecycleViewAdapter_canBindViewHolderCorrectly() {
ArrayList<UserHandle> userHandles =
Lists.newArrayList(UserHandle.of(mPersonalUserId), UserHandle.of(WORK_USER_ID));
FrameLayout parent = new FrameLayout(mContext);
RecyclerView.Adapter<UserAdapter.ViewHolder> adapter =
UserAdapter.createUserRecycleViewAdapter(mContext, userHandles, mOnClickListener);
UserAdapter.ViewHolder holder = adapter.createViewHolder(parent, 0);
adapter.bindViewHolder(holder, 0);
holder.itemView.findViewById(R.id.button).performClick();
assertThat(adapter.getItemCount()).isEqualTo(2);
TextView textView = holder.itemView.findViewById(android.R.id.title);
assertThat(textView.getText().toString()).isEqualTo("Personal");
verify(mOnClickListener).onClick(anyInt());
}
}

View File

@@ -0,0 +1,112 @@
/*
* 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.display;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.when;
import android.content.Context;
import android.content.res.Resources;
import android.os.UserManager;
import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.mockito.Spy;
@RunWith(AndroidJUnit4.class)
public class ScreenSaverPreferenceControllerTest {
@Spy
private final Context mContext = ApplicationProvider.getApplicationContext();
@Spy
private final Resources mResources = mContext.getResources();
@Mock
private UserManager mUserManager;
private ScreenSaverPreferenceController mController;
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
mController = new ScreenSaverPreferenceController(mContext);
when(mContext.getResources()).thenReturn(mResources);
when(mContext.getSystemService(UserManager.class)).thenReturn(mUserManager);
}
@Test
public void isAvailable_dreamsEnabledForAllUsers_shouldBeTrueForSystemUser() {
when(mResources.getBoolean(
com.android.internal.R.bool.config_dreamsSupported)).thenReturn(true);
when(mResources.getBoolean(
com.android.internal.R.bool.config_dreamsOnlyEnabledForSystemUser))
.thenReturn(false);
when(mUserManager.isSystemUser()).thenReturn(true);
assertTrue(mController.isAvailable());
}
@Test
public void isAvailable_dreamsEnabledForAllUsers_shouldBeTrueForNonSystemUser() {
when(mResources.getBoolean(
com.android.internal.R.bool.config_dreamsSupported)).thenReturn(true);
when(mResources.getBoolean(
com.android.internal.R.bool.config_dreamsOnlyEnabledForSystemUser))
.thenReturn(false);
when(mUserManager.isSystemUser()).thenReturn(false);
assertTrue(mController.isAvailable());
}
@Test
public void isAvailable_dreamsDisabled_shouldBeFalseForSystemUser() {
when(mResources.getBoolean(
com.android.internal.R.bool.config_dreamsSupported)).thenReturn(false);
when(mResources.getBoolean(
com.android.internal.R.bool.config_dreamsOnlyEnabledForSystemUser))
.thenReturn(false);
when(mUserManager.isSystemUser()).thenReturn(true);
assertFalse(mController.isAvailable());
}
@Test
public void isAvailable_dreamsOnlyEnabledForSystemUser_shouldBeTrueForSystemUser() {
when(mResources.getBoolean(
com.android.internal.R.bool.config_dreamsSupported)).thenReturn(true);
when(mResources.getBoolean(
com.android.internal.R.bool.config_dreamsOnlyEnabledForSystemUser))
.thenReturn(true);
when(mUserManager.isSystemUser()).thenReturn(true);
assertTrue(mController.isAvailable());
}
@Test
public void isAvailable_dreamsOnlyEnabledForSystemUser_shouldBeFalseForNonSystemUser() {
when(mResources.getBoolean(
com.android.internal.R.bool.config_dreamsSupported)).thenReturn(true);
when(mResources.getBoolean(
com.android.internal.R.bool.config_dreamsOnlyEnabledForSystemUser))
.thenReturn(true);
when(mUserManager.isSystemUser()).thenReturn(false);
assertFalse(mController.isAvailable());
}
}

View File

@@ -0,0 +1,184 @@
/*
* 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.network.helper;
import static com.google.common.truth.Truth.assertThat;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Handler;
import android.os.HandlerThread;
import androidx.lifecycle.Lifecycle;
import androidx.lifecycle.LifecycleOwner;
import androidx.lifecycle.LifecycleRegistry;
import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import java.util.function.Consumer;
@RunWith(AndroidJUnit4.class)
public class LifecycleCallbackIntentReceiverTest implements LifecycleOwner {
private final LifecycleRegistry mRegistry = LifecycleRegistry.createUnsafe(this);
private static final String TEST_SCHEDULER_HANDLER = "testScheduler";
private static final String TEST_INTENT_ACTION = "testAction";
private static final String TEST_INTENT_PERMISSION = "testPermission";
private Context mContext;
private Intent mIntent;
private IntentFilter mIntentFilter;
private Handler mHandler;
private TestConsumer mConsumer;
private TestObj mTarget;
@Before
public void setUp() {
mContext = ApplicationProvider.getApplicationContext();
mIntentFilter = new IntentFilter(TEST_INTENT_ACTION);
mIntent = new Intent(TEST_INTENT_ACTION);
HandlerThread thread = new HandlerThread(TEST_SCHEDULER_HANDLER);
thread.start();
mHandler = new Handler(thread.getLooper());
mConsumer = new TestConsumer();
mTarget = new TestObj(getLifecycle(), mContext,
mIntentFilter, TEST_INTENT_PERMISSION,
mHandler, mConsumer);
}
public Lifecycle getLifecycle() {
return mRegistry;
}
@Test
public void receiver_register_whenActive() {
mRegistry.handleLifecycleEvent(Lifecycle.Event.ON_CREATE);
assertThat(mTarget.getCallbackActiveCount(true)
+ mTarget.getCallbackActiveCount(false)).isEqualTo(0);
mTarget.mReceiver.onReceive(mContext, mIntent);
assertThat(mConsumer.getCallbackCount()).isEqualTo(0);
mRegistry.handleLifecycleEvent(Lifecycle.Event.ON_START);
assertThat(mTarget.getCallbackActiveCount(true)).isEqualTo(1);
assertThat(mConsumer.getCallbackCount()).isEqualTo(0);
mTarget.mReceiver.onReceive(mContext, mIntent);
assertThat(mConsumer.getCallbackCount()).isEqualTo(1);
assertThat(mConsumer.getData()).isEqualTo(mIntent);
}
@Test
public void receiver_unregister_whenInActive() {
mRegistry.handleLifecycleEvent(Lifecycle.Event.ON_CREATE);
mRegistry.handleLifecycleEvent(Lifecycle.Event.ON_START);
mRegistry.handleLifecycleEvent(Lifecycle.Event.ON_STOP);
assertThat(mTarget.getCallbackActiveCount(false)).isEqualTo(1);
mTarget.mReceiver.onReceive(mContext, mIntent);
assertThat(mConsumer.getCallbackCount()).isEqualTo(0);
}
@Test
public void receiver_register_whenReActive() {
mRegistry.handleLifecycleEvent(Lifecycle.Event.ON_CREATE);
mRegistry.handleLifecycleEvent(Lifecycle.Event.ON_START);
mRegistry.handleLifecycleEvent(Lifecycle.Event.ON_STOP);
mRegistry.handleLifecycleEvent(Lifecycle.Event.ON_START);
assertThat(mTarget.getCallbackActiveCount(true)).isEqualTo(2);
mTarget.mReceiver.onReceive(mContext, mIntent);
assertThat(mConsumer.getCallbackCount()).isEqualTo(1);
assertThat(mConsumer.getData()).isEqualTo(mIntent);
}
@Test
public void receiver_close_whenDestroy() {
mRegistry.handleLifecycleEvent(Lifecycle.Event.ON_CREATE);
mRegistry.handleLifecycleEvent(Lifecycle.Event.ON_START);
mRegistry.handleLifecycleEvent(Lifecycle.Event.ON_STOP);
mRegistry.handleLifecycleEvent(Lifecycle.Event.ON_DESTROY);
assertThat(mTarget.getCallbackActiveCount(false)).isEqualTo(1);
mTarget.mReceiver.onReceive(mContext, mIntent);
assertThat(mConsumer.getCallbackCount()).isEqualTo(0);
}
public static class TestConsumer implements Consumer<Intent> {
long mNumberOfCallback;
Intent mLatestData;
public TestConsumer() {}
public void accept(Intent data) {
mLatestData = data;
mNumberOfCallback ++;
}
protected long getCallbackCount() {
return mNumberOfCallback;
}
protected Intent getData() {
return mLatestData;
}
}
public static class TestObj extends LifecycleCallbackIntentReceiver {
long mCallbackActiveCount;
long mCallbackInActiveCount;
public TestObj(Lifecycle lifecycle, Context context, IntentFilter filter,
String broadcastPermission, Handler scheduler, Consumer<Intent> resultCallback) {
super(lifecycle, context, filter, broadcastPermission, scheduler, resultCallback);
}
@Override
public void setCallbackActive(boolean isActive) {
if (isActive) {
mCallbackActiveCount ++;
} else {
mCallbackInActiveCount ++;
}
super.setCallbackActive(isActive);
}
protected long getCallbackActiveCount(boolean forActive) {
return forActive ? mCallbackActiveCount : mCallbackInActiveCount;
}
}
}

View File

@@ -0,0 +1,105 @@
/*
* 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.network.helper;
import static org.mockito.ArgumentMatchers.anyObject;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import android.telephony.TelephonyCallback;
import android.telephony.TelephonyManager;
import androidx.lifecycle.Lifecycle;
import androidx.lifecycle.LifecycleOwner;
import androidx.lifecycle.LifecycleRegistry;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import java.util.concurrent.atomic.AtomicReference;
@RunWith(AndroidJUnit4.class)
public class LifecycleCallbackTelephonyAdapterTest implements LifecycleOwner {
private final LifecycleRegistry mRegistry = LifecycleRegistry.createUnsafe(this);
@Mock
private TelephonyManager mTelMgr;
private TestCallback mTestCallback;
private AtomicReference<Object> mResult;
private LifecycleCallbackTelephonyAdapter<Object> mAdapter;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mResult = new AtomicReference<Object>();
mTestCallback = new TestCallback();
doNothing().when(mTelMgr).registerTelephonyCallback(null, mTestCallback);
doNothing().when(mTelMgr).unregisterTelephonyCallback(mTestCallback);
mAdapter = new LifecycleCallbackTelephonyAdapter<Object>(getLifecycle(), mTelMgr,
mTestCallback, null, result -> mResult.set(result));
}
public Lifecycle getLifecycle() {
return mRegistry;
}
@Test
public void telephonyCallback_register_whenActive() {
mRegistry.handleLifecycleEvent(Lifecycle.Event.ON_CREATE);
verify(mTelMgr, never()).registerTelephonyCallback(anyObject(), anyObject());
mRegistry.handleLifecycleEvent(Lifecycle.Event.ON_START);
verify(mTelMgr).registerTelephonyCallback(anyObject(), anyObject());
}
@Test
public void telephonyCallback_unregister_whenInActive() {
mRegistry.handleLifecycleEvent(Lifecycle.Event.ON_CREATE);
verify(mTelMgr, never()).unregisterTelephonyCallback(anyObject());
mRegistry.handleLifecycleEvent(Lifecycle.Event.ON_START);
verify(mTelMgr, never()).unregisterTelephonyCallback(anyObject());
mRegistry.handleLifecycleEvent(Lifecycle.Event.ON_STOP);
verify(mTelMgr).unregisterTelephonyCallback(anyObject());
mRegistry.handleLifecycleEvent(Lifecycle.Event.ON_DESTROY);
verify(mTelMgr, times(1)).unregisterTelephonyCallback(anyObject());
}
protected static class TestCallback extends TelephonyCallback
implements TelephonyCallback.CallStateListener {
@Override
public void onCallStateChanged(int state) {}
}
}