Rewrite dream settings.

The available dreams will now be shown in a grid, with a preview button
at the bottom.

Screenshot (white placeholder image used):
http://screen/6pe6EoACPoQN8TZ.png
http://screen/4nR6Wg5pN7pL6Wb.png

Phone (single column layout):
http://screen/4WFqzZR8Yf9H3VZ.png

Colors and margins will be finalized once final mocks are done.

Bug: 207681076
Bug: 214250590
Test: locally on device
Change-Id: Ie68bdc60e74e72dc3c3cee3f1ffd40f5613109b9
This commit is contained in:
Lucas Silva
2021-12-22 15:52:00 +00:00
parent b81f2b77f0
commit 38d0a7ca91
16 changed files with 500 additions and 561 deletions

View File

@@ -0,0 +1,22 @@
<?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"
xmlns:androidprv="http://schemas.android.com/apk/prv/res/android">
<item android:state_activated="true" android:color="?androidprv:attr/colorSurfaceHighlight"/>
<item android:color="?androidprv:attr/colorSurface"/>
</selector>

View File

@@ -0,0 +1,42 @@
<?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.
-->
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="@dimen/dream_picker_padding">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/dream_list"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:clipToPadding="true"
android:nestedScrollingEnabled="false"/>
<Button
android:id="@+id/preview_button"
style="@style/ActionPrimaryButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/dream_preview_button_title"
app:layout_constraintTop_toBottomOf="@+id/dream_list"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"/>
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@@ -0,0 +1,104 @@
<?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.
-->
<androidx.cardview.widget.CardView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="10dp"
style="@style/DreamCardStyle"
android:clickable="true">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="8dp"
android:minHeight="?android:attr/listPreferredItemHeight">
<androidx.cardview.widget.CardView
android:id="@+id/preview_container"
android:layout_width="match_parent"
android:layout_height="0dp"
app:cardCornerRadius="@dimen/dream_item_corner_radius"
app:cardElevation="0dp"
app:layout_constraintDimensionRatio="3:2"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent">
<ImageView
android:id="@+id/preview"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="centerCrop"/>
</androidx.cardview.widget.CardView>
<Button
android:id="@+id/customize_button"
style="@style/ActionPrimaryButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:visibility="gone"
android:text="@string/customize_button_title"
app:layout_constraintTop_toTopOf="@+id/preview_container"
app:layout_constraintBottom_toBottomOf="@+id/preview_container"
app:layout_constraintStart_toStartOf="@+id/preview_container"
app:layout_constraintEnd_toEndOf="@+id/preview_container"/>
<ImageView
android:id="@+id/icon"
android:layout_width="18dp"
android:layout_height="0dp"
android:layout_marginTop="18dp"
android:layout_marginStart="10dp"
android:layout_marginBottom="10dp"
app:layout_constraintDimensionRatio="1:1"
app:layout_constraintTop_toBottomOf="@+id/preview_container"
app_layout_constraintEnd_toStartOf="@+id/title_text"
app:layout_constraintStart_toStartOf="parent"/>
<TextView
android:id="@+id/title_text"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="18dp"
android:layout_marginTop="16dp"
android:layout_marginBottom="10dp"
android:maxLines="1"
android:ellipsize="end"
android:textAppearance="?android:attr/textAppearanceListItemSmall"
app:layout_constraintTop_toBottomOf="@+id/preview_container"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/icon"/>
<TextView
android:id="@+id/summary_text"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:textColor="?android:attr/textColorSecondary"
android:maxLines="2"
android:layout_marginBottom="8dp"
android:layout_marginStart="8dp"
android:layout_marginEnd="8dp"
app:layout_constraintTop_toBottomOf="@+id/title_text"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"/>
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.cardview.widget.CardView>

View File

@@ -1,36 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (C) 2021 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:orientation="horizontal"
android:gravity="bottom"
android:paddingStart="72dp"
android:paddingEnd="72dp"
android:layout_width="match_parent"
android:layout_height="match_parent">
<Button
android:id="@+id/dream_start_now_button"
style="@style/ActionPrimaryButton"
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:text="@string/screensaver_settings_dream_start"/>
</LinearLayout>

View File

@@ -439,6 +439,13 @@
<dimen name="chartview_trapezoid_margin_start">1dp</dimen>
<dimen name="chartview_trapezoid_margin_bottom">2dp</dimen>
<!-- Dimensions for Dream settings cards -->
<dimen name="dream_item_min_column_width">230dp</dimen>
<dimen name="dream_item_corner_radius">@*android:dimen/config_dialogCornerRadius</dimen>
<dimen name="dream_preference_margin_horizontal">4dp</dimen>
<dimen name="dream_preference_margin_bottom">8dp</dimen>
<dimen name="dream_picker_padding">40dp</dimen>
<!-- Sims/Data mobile/Calls/SMS select dialog-->
<dimen name="sims_select_margin_bottom">24dp</dimen>
<dimen name="sims_select_margin_top">8dp</dimen>

View File

@@ -3140,8 +3140,6 @@
<string name="screensaver_settings_when_to_dream">When to start</string>
<!-- Dream settings screen, action label, current selected screen saver -->
<string name="screensaver_settings_current">Current screen saver</string>
<!-- Dream settings screen, button label to start dreaming [CHAR LIMIT=30] -->
<string name="screensaver_settings_dream_start">Start now</string>
<!-- Dream settings screen, button label for settings for a specific screensaver -->
<string name="screensaver_settings_button">Settings</string>
<!-- Sound & display settings screen, setting option name to change whether the screen adjusts automatically based on lighting conditions -->
@@ -13760,4 +13758,8 @@
</string-array>
<!-- TARE dialog button to proceed with a value change [CHAR LIMIT=none] -->
<string name="tare_dialog_confirm_button_title">Confirm</string>
<!-- Button to preview the selected screensaver in settings [CHAR LIMIT=40] -->
<string name="dream_preview_button_title">Preview screensaver</string>
<!-- Button to customize the screensaver [CHAR LIMIT=20] -->
<string name="customize_button_title">Customize</string>
</resources>

View File

@@ -936,4 +936,14 @@
<style name="SimConfirmDialog.ButtonBarStyle" parent="@android:style/Widget.Material.ButtonBar">
<item name="android:paddingEnd">8dp</item>
</style>
<style name="DreamCardStyle">
<item name="android:layout_marginBottom">@dimen/dream_preference_margin_bottom</item>
<item name="android:layout_marginStart">@dimen/dream_preference_margin_horizontal</item>
<item name="android:layout_marginEnd">@dimen/dream_preference_margin_horizontal</item>
<item name="cardBackgroundColor">@color/dream_card_color_state_list</item>
<item name="cardCornerRadius">@dimen/dream_item_corner_radius</item>
<item name="cardElevation">0dp</item>
<item name="rippleColor">?android:attr/colorControlHighlight</item>
</style>
</resources>

View File

@@ -19,23 +19,15 @@
xmlns:settings="http://schemas.android.com/apk/res-auto"
android:title="@string/screensaver_settings_title">
<com.android.settings.widget.GearPreference
android:key="current_screensaver"
android:title="@string/screensaver_settings_current"
android:fragment="com.android.settings.dream.CurrentDreamPicker"
settings:controller="com.android.settings.dream.CurrentDreamPreferenceController" />
<com.android.settingslib.widget.LayoutPreference
android:key="dream_picker"
android:selectable="false"
android:layout="@layout/dream_picker_layout"
settings:controller="com.android.settings.dream.DreamPickerController"/>
<Preference
android:key="when_to_start"
android:title="@string/screensaver_settings_when_to_dream"
android:fragment="com.android.settings.dream.WhenToDreamPicker" />
<PreferenceCategory>
<com.android.settingslib.widget.LayoutPreference
android:key="dream_start_now_button_container"
android:layout="@layout/dream_start_button"
android:selectable="false"
settings:controller="com.android.settings.dream.StartNowPreferenceController" />
</PreferenceCategory>
android:fragment="com.android.settings.dream.WhenToDreamPicker"/>
</PreferenceScreen>

View File

@@ -1,125 +0,0 @@
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.dream;
import android.app.settings.SettingsEnums;
import android.content.ComponentName;
import android.content.Context;
import android.graphics.drawable.Drawable;
import com.android.settings.R;
import com.android.settings.widget.RadioButtonPickerFragment;
import com.android.settingslib.dream.DreamBackend;
import com.android.settingslib.dream.DreamBackend.DreamInfo;
import com.android.settingslib.widget.CandidateInfo;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
public final class CurrentDreamPicker extends RadioButtonPickerFragment {
private DreamBackend mBackend;
@Override
public void onAttach(Context context) {
super.onAttach(context);
mBackend = DreamBackend.getInstance(context);
}
@Override
protected int getPreferenceScreenResId() {
return R.xml.current_dream_settings;
}
@Override
public int getMetricsCategory() {
return SettingsEnums.DREAM;
}
@Override
protected boolean setDefaultKey(String key) {
Map<String, ComponentName> componentNameMap = getDreamComponentsMap();
if (componentNameMap.get(key) != null) {
mBackend.setActiveDream(componentNameMap.get(key));
return true;
}
return false;
}
@Override
protected String getDefaultKey() {
return mBackend.getActiveDream().flattenToString();
}
@Override
protected List<? extends CandidateInfo> getCandidates() {
final List<DreamCandidateInfo> candidates;
candidates = mBackend.getDreamInfos().stream()
.map(DreamCandidateInfo::new)
.collect(Collectors.toList());
return candidates;
}
@Override
protected void onSelectionPerformed(boolean success) {
super.onSelectionPerformed(success);
getActivity().finish();
}
private Map<String, ComponentName> getDreamComponentsMap() {
Map<String, ComponentName> comps = new HashMap<>();
mBackend.getDreamInfos()
.forEach((info) ->
comps.put(info.componentName.flattenToString(), info.componentName));
return comps;
}
private static final class DreamCandidateInfo extends CandidateInfo {
private final CharSequence name;
private final Drawable icon;
private final String key;
DreamCandidateInfo(DreamInfo info) {
super(true);
name = info.caption;
icon = info.icon;
key = info.componentName.flattenToString();
}
@Override
public CharSequence loadLabel() {
return name;
}
@Override
public Drawable loadIcon() {
return icon;
}
@Override
public String getKey() {
return key;
}
}
}

View File

@@ -1,93 +0,0 @@
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.dream;
import android.content.Context;
import androidx.preference.Preference;
import com.android.settings.Utils;
import com.android.settings.core.BasePreferenceController;
import com.android.settings.widget.GearPreference;
import com.android.settingslib.RestrictedPreference;
import com.android.settingslib.dream.DreamBackend;
import com.android.settingslib.dream.DreamBackend.DreamInfo;
import java.util.Optional;
public class CurrentDreamPreferenceController extends BasePreferenceController {
private final DreamBackend mBackend;
public CurrentDreamPreferenceController(Context context, String key) {
super(context, key);
mBackend = DreamBackend.getInstance(context);
}
@Override
public int getAvailabilityStatus() {
return mBackend.getDreamInfos().size() > 0 ? AVAILABLE : CONDITIONALLY_UNAVAILABLE;
}
@Override
public void updateState(Preference preference) {
super.updateState(preference);
setGearClickListenerForPreference(preference);
setActiveDreamIcon(preference);
}
@Override
public CharSequence getSummary() {
return mBackend.getActiveDreamName();
}
private void setGearClickListenerForPreference(Preference preference) {
if (!(preference instanceof GearPreference)) {
return;
}
final GearPreference gearPreference = (GearPreference) preference;
final Optional<DreamInfo> info = getActiveDreamInfo();
if (!info.isPresent() || info.get().settingsComponentName == null) {
gearPreference.setOnGearClickListener(null);
return;
}
gearPreference.setOnGearClickListener(gearPref -> launchScreenSaverSettings());
}
private void launchScreenSaverSettings() {
final Optional<DreamInfo> info = getActiveDreamInfo();
if (!info.isPresent()) return;
mBackend.launchSettings(mContext, info.get());
}
private Optional<DreamInfo> getActiveDreamInfo() {
return mBackend.getDreamInfos()
.stream()
.filter((info) -> info.isActive)
.findFirst();
}
private void setActiveDreamIcon(Preference preference) {
if (!(preference instanceof GearPreference)) {
return;
}
final GearPreference gearPref = (GearPreference) preference;
gearPref.setIconSize(RestrictedPreference.ICON_SIZE_SMALL);
gearPref.setIcon(Utils.getSafeIcon(mBackend.getActiveIcon()));
}
}

View File

@@ -0,0 +1,129 @@
/*
* 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.dream;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import com.android.settings.R;
import com.android.settingslib.dream.DreamBackend.DreamInfo;
import java.util.List;
/**
* RecyclerView adapter which displays list of available dreams for the user to select.
*/
class DreamPickerAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private final List<DreamInfo> mDreamInfoList;
private final OnItemClickListener mItemClickListener;
private final OnItemClickListener mOnDreamSelected = new OnItemClickListener() {
@Override
public void onItemClicked(DreamInfo dreamInfo) {
if (mItemClickListener != null) {
mItemClickListener.onItemClicked(dreamInfo);
}
mDreamInfoList.forEach(dream -> {
if (dream != null) {
dream.isActive = false;
}
});
dreamInfo.isActive = true;
notifyDataSetChanged();
}
};
private final OnItemClickListener mOnCustomizeListener;
interface OnItemClickListener {
void onItemClicked(DreamInfo dreamInfo);
}
/**
* View holder for each Dream service.
*/
private static class DreamViewHolder extends RecyclerView.ViewHolder {
private final ImageView mIconView;
private final TextView mTitleView;
private final TextView mSummaryView;
private final ImageView mPreviewView;
private final Button mCustomizeButton;
DreamViewHolder(View view) {
super(view);
mPreviewView = view.findViewById(R.id.preview);
mIconView = view.findViewById(R.id.icon);
mTitleView = view.findViewById(R.id.title_text);
mSummaryView = view.findViewById(R.id.summary_text);
mCustomizeButton = view.findViewById(R.id.customize_button);
}
/**
* Bind the dream service view at the given position. Add details on the
* dream's icon, name and description.
*/
public void bindView(DreamInfo dreamInfo, OnItemClickListener clickListener,
OnItemClickListener customizeListener) {
mIconView.setImageDrawable(dreamInfo.icon);
mTitleView.setText(dreamInfo.caption);
mPreviewView.setImageDrawable(dreamInfo.previewImage);
mSummaryView.setText(dreamInfo.description);
itemView.setActivated(dreamInfo.isActive);
if (dreamInfo.isActive && dreamInfo.settingsComponentName != null) {
mCustomizeButton.setVisibility(View.VISIBLE);
mCustomizeButton.setOnClickListener(
v -> customizeListener.onItemClicked(dreamInfo));
} else {
mCustomizeButton.setVisibility(View.GONE);
}
itemView.setOnClickListener(v -> clickListener.onItemClicked(dreamInfo));
}
}
DreamPickerAdapter(List<DreamInfo> dreamInfos, OnItemClickListener clickListener,
OnItemClickListener onCustomizeListener) {
mDreamInfoList = dreamInfos;
mItemClickListener = clickListener;
mOnCustomizeListener = onCustomizeListener;
}
@NonNull
@Override
public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, int viewType) {
View view = LayoutInflater.from(viewGroup.getContext())
.inflate(R.layout.dream_preference_layout, viewGroup, false);
return new DreamViewHolder(view);
}
@Override
public void onBindViewHolder(@NonNull RecyclerView.ViewHolder viewHolder, int i) {
((DreamViewHolder) viewHolder).bindView(mDreamInfoList.get(i), mOnDreamSelected,
mOnCustomizeListener);
}
@Override
public int getItemCount() {
return mDreamInfoList.size();
}
}

View File

@@ -0,0 +1,138 @@
/*
* 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.dream;
import android.content.Context;
import android.widget.Button;
import androidx.annotation.Nullable;
import androidx.preference.Preference;
import androidx.recyclerview.widget.GridLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import com.android.settings.R;
import com.android.settings.core.BasePreferenceController;
import com.android.settings.dream.DreamPickerAdapter.OnItemClickListener;
import com.android.settingslib.dream.DreamBackend;
import com.android.settingslib.widget.LayoutPreference;
import java.util.List;
/**
* Controller for the dream picker where the user can select a screensaver.
*/
public class DreamPickerController extends BasePreferenceController {
public static final String KEY = "dream_picker";
private final DreamBackend mBackend;
private final List<DreamBackend.DreamInfo> mDreamInfos;
private Button mPreviewButton;
@Nullable
private DreamBackend.DreamInfo mActiveDream;
private final OnItemClickListener mItemClickListener =
new OnItemClickListener() {
@Override
public void onItemClicked(DreamBackend.DreamInfo dreamInfo) {
mActiveDream = dreamInfo;
mBackend.setActiveDream(
mActiveDream == null ? null : mActiveDream.componentName);
updatePreviewButtonState();
}
};
private final OnItemClickListener mCustomizeListener = new OnItemClickListener() {
@Override
public void onItemClicked(DreamBackend.DreamInfo dreamInfo) {
mBackend.launchSettings(mContext, dreamInfo);
}
};
public DreamPickerController(Context context, String preferenceKey) {
this(context, preferenceKey, DreamBackend.getInstance(context));
}
public DreamPickerController(Context context, String preferenceKey, DreamBackend backend) {
super(context, preferenceKey);
mBackend = backend;
mDreamInfos = mBackend.getDreamInfos();
}
@Override
public String getPreferenceKey() {
return KEY;
}
@Override
public int getAvailabilityStatus() {
return mDreamInfos.size() > 0 ? AVAILABLE : CONDITIONALLY_UNAVAILABLE;
}
@Override
public void updateState(Preference preference) {
super.updateState(preference);
mActiveDream = getActiveDreamInfo();
final DreamPickerAdapter adapter =
new DreamPickerAdapter(mDreamInfos, mItemClickListener, mCustomizeListener);
final RecyclerView recyclerView =
((LayoutPreference) preference).findViewById(R.id.dream_list);
recyclerView.setLayoutManager(new AutoFitGridLayoutManager(mContext));
recyclerView.setAdapter(adapter);
mPreviewButton = ((LayoutPreference) preference).findViewById(R.id.preview_button);
mPreviewButton.setOnClickListener(v -> mBackend.preview(mActiveDream));
updatePreviewButtonState();
}
private void updatePreviewButtonState() {
final boolean hasDream = mActiveDream != null;
mPreviewButton.setClickable(hasDream);
mPreviewButton.setEnabled(hasDream);
}
@Nullable
private DreamBackend.DreamInfo getActiveDreamInfo() {
return mDreamInfos
.stream()
.filter(d -> d.isActive)
.findFirst()
.orElse(null);
}
/** Grid layout manager that calculates the number of columns for the screen size. */
private static final class AutoFitGridLayoutManager extends GridLayoutManager {
private final float mColumnWidth;
AutoFitGridLayoutManager(Context context) {
super(context, /* spanCount= */ 1);
this.mColumnWidth = context
.getResources()
.getDimensionPixelSize(R.dimen.dream_item_min_column_width);
}
@Override
public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
final int totalSpace = getWidth() - getPaddingRight() - getPaddingLeft();
final int spanCount = Math.max(1, (int) (totalSpace / mColumnWidth));
setSpanCount(spanCount);
super.onLayoutChildren(recycler, state);
}
}
}

View File

@@ -1,72 +0,0 @@
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.dream;
import android.content.Context;
import android.widget.Button;
import androidx.preference.Preference;
import androidx.preference.PreferenceScreen;
import com.android.settings.R;
import com.android.settings.core.BasePreferenceController;
import com.android.settings.dashboard.DashboardFragment;
import com.android.settings.overlay.FeatureFactory;
import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
import com.android.settingslib.dream.DreamBackend;
import com.android.settingslib.widget.LayoutPreference;
/**
* Controller that used to enable screen saver
*/
public class StartNowPreferenceController extends BasePreferenceController {
private final DreamBackend mBackend;
private final MetricsFeatureProvider mMetricsFeatureProvider;
public StartNowPreferenceController(Context context, String preferenceKey) {
super(context, preferenceKey);
mBackend = DreamBackend.getInstance(context);
mMetricsFeatureProvider = FeatureFactory.getFactory(context).getMetricsFeatureProvider();
}
@Override
public int getAvailabilityStatus() {
return AVAILABLE;
}
@Override
public void displayPreference(PreferenceScreen screen) {
super.displayPreference(screen);
final LayoutPreference pref = screen.findPreference(getPreferenceKey());
final Button startButton = pref.findViewById(R.id.dream_start_now_button);
startButton.setOnClickListener(v -> {
mMetricsFeatureProvider.logClickedPreference(pref,
pref.getExtras().getInt(DashboardFragment.CATEGORY));
mBackend.startDreaming();
});
}
@Override
public void updateState(Preference preference) {
super.updateState(preference);
final Button startButton = ((LayoutPreference) preference)
.findViewById(R.id.dream_start_now_button);
startButton.setEnabled(mBackend.getWhenToDreamSetting() != DreamBackend.NEVER);
}
}

View File

@@ -1,92 +0,0 @@
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.dream;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.app.Activity;
import android.content.ComponentName;
import android.content.Context;
import android.os.UserManager;
import com.android.settings.testutils.FakeFeatureFactory;
import com.android.settingslib.dream.DreamBackend;
import com.android.settingslib.dream.DreamBackend.DreamInfo;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Answers;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.util.ReflectionHelpers;
import java.util.Collections;
@RunWith(RobolectricTestRunner.class)
public class CurrentDreamPickerTest {
private static String COMPONENT_KEY = "mocked_component_name_string";
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
private DreamBackend mBackend;
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
private Activity mActivity;
@Mock
private UserManager mUserManager;
private CurrentDreamPicker mPicker;
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
when(mActivity.getSystemService(Context.USER_SERVICE)).thenReturn(mUserManager);
FakeFeatureFactory.setupForTest();
mPicker = new CurrentDreamPicker();
mPicker.onAttach(mActivity);
ReflectionHelpers.setField(mPicker, "mBackend", mBackend);
}
@Test
public void getDefaultShouldReturnActiveDream() {
ComponentName mockComponentName = mock(ComponentName.class);
when(mockComponentName.flattenToString()).thenReturn(COMPONENT_KEY);
when(mBackend.getActiveDream()).thenReturn(mockComponentName);
assertThat(mPicker.getDefaultKey()).isEqualTo(COMPONENT_KEY);
}
@Test
public void setDefaultShouldUpdateActiveDream() {
DreamInfo mockInfo = mock(DreamInfo.class);
ComponentName mockName = mock(ComponentName.class);
mockInfo.componentName = mockName;
when(mockName.flattenToString()).thenReturn(COMPONENT_KEY);
when(mBackend.getDreamInfos()).thenReturn(Collections.singletonList(mockInfo));
mPicker.setDefaultKey(COMPONENT_KEY);
verify(mBackend).setActiveDream(mockName);
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright (C) 2017 The Android Open Source Project
* 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.
@@ -18,94 +18,92 @@ package com.android.settings.dream;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.mock;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.content.ComponentName;
import android.content.Context;
import android.widget.Button;
import com.android.settings.widget.GearPreference;
import androidx.preference.PreferenceScreen;
import androidx.recyclerview.widget.RecyclerView;
import androidx.test.core.app.ApplicationProvider;
import com.android.settings.R;
import com.android.settingslib.dream.DreamBackend;
import com.android.settingslib.dream.DreamBackend.DreamInfo;
import com.android.settingslib.widget.LayoutPreference;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Answers;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.util.ReflectionHelpers;
import java.util.ArrayList;
import java.util.Collections;
@RunWith(RobolectricTestRunner.class)
public class CurrentDreamPreferenceControllerTest {
private CurrentDreamPreferenceController mController;
public class DreamPickerControllerTest {
private DreamPickerController mController;
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
private DreamBackend mBackend;
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
private Context mContext;
@Mock
private DreamInfo mDreamInfo;
private PreferenceScreen mScreen;
private LayoutPreference mPreference;
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
mContext = ApplicationProvider.getApplicationContext();
mController = new CurrentDreamPreferenceController(mContext, "test");
ReflectionHelpers.setField(mController, "mBackend", mBackend);
mPreference = new LayoutPreference(mContext, R.layout.dream_picker_layout);
when(mScreen.findPreference(anyString())).thenReturn(mPreference);
mController = new DreamPickerController(
mContext,
/* preferenceKey= */ "test",
mBackend);
mController.displayPreference(mScreen);
}
@Test
public void isDisabledIfNoDreamsAvailable() {
when(mBackend.getDreamInfos()).thenReturn(new ArrayList<>(0));
assertThat(mController.isAvailable()).isFalse();
}
@Test
public void isEnabledIfDreamsAvailable() {
when(mBackend.getDreamInfos()).thenReturn(Collections.singletonList(mDreamInfo));
when(mBackend.getDreamInfos()).thenReturn(Collections.singletonList(new DreamInfo()));
assertThat(mController.isAvailable()).isTrue();
}
@Test
public void gearShowsIfActiveDreamInfoHasOptions() {
mDreamInfo.settingsComponentName = mock(ComponentName.class);
mDreamInfo.isActive = true;
public void testDreamDisplayedInList() {
when(mBackend.getDreamInfos()).thenReturn(Collections.singletonList(new DreamInfo()));
mController.updateState(mPreference);
when(mBackend.getDreamInfos()).thenReturn(Collections.singletonList(mDreamInfo));
GearPreference mockPref = mock(GearPreference.class);
ArgumentCaptor<GearPreference.OnGearClickListener> captor =
ArgumentCaptor.forClass(GearPreference.OnGearClickListener.class);
// verify that updateState sets a non-null gear click listener
mController.updateState(mockPref);
verify(mockPref).setOnGearClickListener(captor.capture());
captor.getAllValues().forEach(listener -> assertThat(listener).isNotNull());
RecyclerView view = mPreference.findViewById(R.id.dream_list);
assertThat(view.getAdapter().getItemCount()).isEqualTo(1);
}
@Test
public void gearHidesIfActiveDreamInfoHasNoOptions() {
mDreamInfo.settingsComponentName = null;
mDreamInfo.isActive = true;
public void testPreviewButton() {
final DreamInfo mockDreamInfo = new DreamInfo();
mockDreamInfo.componentName = new ComponentName("package", "class");
mockDreamInfo.isActive = true;
when(mBackend.getDreamInfos()).thenReturn(Collections.singletonList(mDreamInfo));
when(mBackend.getDreamInfos()).thenReturn(Collections.singletonList(mockDreamInfo));
mController.updateState(mPreference);
GearPreference mockPref = mock(GearPreference.class);
ArgumentCaptor<GearPreference.OnGearClickListener> captor =
ArgumentCaptor.forClass(GearPreference.OnGearClickListener.class);
// setting a null onGearClickListener removes the gear from view
mController.updateState(mockPref);
verify(mockPref).setOnGearClickListener(captor.capture());
captor.getAllValues().forEach(listener -> assertThat(listener).isNull());
Button view = mPreference.findViewById(R.id.preview_button);
view.performClick();
verify(mBackend).preview(mockDreamInfo);
}
}

View File

@@ -1,87 +0,0 @@
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.dream;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.content.Context;
import android.widget.Button;
import androidx.preference.PreferenceScreen;
import com.android.settings.R;
import com.android.settingslib.dream.DreamBackend;
import com.android.settingslib.widget.LayoutPreference;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.util.ReflectionHelpers;
@RunWith(RobolectricTestRunner.class)
public class StartNowPreferenceControllerTest {
private StartNowPreferenceController mController;
private Context mContext;
@Mock
private PreferenceScreen mScreen;
@Mock
private LayoutPreference mLayoutPref;
@Mock
private Button mButton;
@Mock
private DreamBackend mBackend;
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
mContext = spy(RuntimeEnvironment.application);
mController = new StartNowPreferenceController(mContext, "key");
when(mScreen.findPreference(mController.getPreferenceKey())).thenReturn(mLayoutPref);
when(mLayoutPref.findViewById(R.id.dream_start_now_button)).thenReturn(mButton);
ReflectionHelpers.setField(mController, "mBackend", mBackend);
}
@Test
public void updateState_neverDreaming_buttonShouldDidabled() {
when(mBackend.getWhenToDreamSetting()).thenReturn(DreamBackend.NEVER);
mController.displayPreference(mScreen);
mController.updateState(mLayoutPref);
verify(mButton).setEnabled(false);
}
@Test
public void updateState_dreamIsAvailable_buttonShouldEnabled() {
when(mBackend.getWhenToDreamSetting()).thenReturn(DreamBackend.EITHER);
mController.displayPreference(mScreen);
mController.updateState(mLayoutPref);
verify(mButton).setEnabled(true);
}
}