diff --git a/AndroidManifest.xml b/AndroidManifest.xml index b02439ea1c9..bf64ebe7b35 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -1,10 +1,9 @@ + package="com.android.settings" + coreApp="true" + android:sharedUserId="android.uid.system"> - + @@ -2921,6 +2920,24 @@ android:value="true" /> + + + + + + + + + + + - + \ No newline at end of file diff --git a/res/layout/dream_setup_layout.xml b/res/layout/dream_setup_layout.xml new file mode 100644 index 00000000000..31a0fb12ac8 --- /dev/null +++ b/res/layout/dream_setup_layout.xml @@ -0,0 +1,39 @@ + + + + + + + + + + \ No newline at end of file diff --git a/res/values/config.xml b/res/values/config.xml index 9069340be09..6a7c15b0d01 100755 --- a/res/values/config.xml +++ b/res/values/config.xml @@ -590,4 +590,7 @@ Take the "Unrestricted data" page as the example, the visible app icons could be 15 on 6.4 inches screen size whether the font size and display size are both small. --> 20 + + + false diff --git a/res/values/strings.xml b/res/values/strings.xml index aeb44f2bed3..fc33a8bf956 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -13850,6 +13850,10 @@ Choose more options More settings + + Customize your screen saver + + Choose what you’ll see on your screen when your device is not in use. Customize diff --git a/src/com/android/settings/dream/DreamAdapter.java b/src/com/android/settings/dream/DreamAdapter.java index 87277338494..e6745464ed2 100644 --- a/src/com/android/settings/dream/DreamAdapter.java +++ b/src/com/android/settings/dream/DreamAdapter.java @@ -39,11 +39,12 @@ import java.util.List; */ class DreamAdapter extends RecyclerView.Adapter { private final List mItemList; + private int mLastSelectedPos = -1; /** * View holder for each {@link IDreamItem}. */ - private static class DreamViewHolder extends RecyclerView.ViewHolder { + private class DreamViewHolder extends RecyclerView.ViewHolder { private final ImageView mIconView; private final TextView mTitleView; private final ImageView mPreviewView; @@ -62,7 +63,7 @@ class DreamAdapter extends RecyclerView.Adapter { /** * Bind the view at the given position, populating the view with the provided data. */ - public void bindView(IDreamItem item) { + public void bindView(IDreamItem item, int position) { mTitleView.setText(item.getTitle()); final Drawable previewImage = item.getPreviewImage(); @@ -73,20 +74,32 @@ class DreamAdapter extends RecyclerView.Adapter { mPreviewView.setVisibility(View.GONE); } - final Drawable icon = item.getIcon(); + final Drawable icon = item.isActive() + ? mContext.getDrawable(R.drawable.ic_dream_check_circle) + : item.getIcon(); if (icon instanceof VectorDrawable) { icon.setTint(Utils.getColorAttrDefaultColor(mContext, com.android.internal.R.attr.colorAccentPrimaryVariant)); } mIconView.setImageDrawable(icon); - if (item.allowCustomization()) { - mCustomizeButton.setVisibility(View.VISIBLE); - mCustomizeButton.setOnClickListener(v -> item.onCustomizeClicked()); + + if (item.isActive()) { + mLastSelectedPos = position; + itemView.setSelected(true); } else { - mCustomizeButton.setVisibility(View.GONE); + itemView.setSelected(false); } - itemView.setOnClickListener(v -> item.onItemClicked()); - itemView.setActivated(item.isActive()); + + mCustomizeButton.setOnClickListener(v -> item.onCustomizeClicked()); + mCustomizeButton.setVisibility(item.allowCustomization() ? View.VISIBLE : View.GONE); + + itemView.setOnClickListener(v -> { + item.onItemClicked(); + if (mLastSelectedPos > -1 && mLastSelectedPos != position) { + notifyItemChanged(mLastSelectedPos); + } + notifyItemChanged(position); + }); } } @@ -104,7 +117,7 @@ class DreamAdapter extends RecyclerView.Adapter { @Override public void onBindViewHolder(@NonNull RecyclerView.ViewHolder viewHolder, int i) { - ((DreamViewHolder) viewHolder).bindView(mItemList.get(i)); + ((DreamViewHolder) viewHolder).bindView(mItemList.get(i), i); } @Override diff --git a/src/com/android/settings/dream/DreamComplicationPickerController.java b/src/com/android/settings/dream/DreamComplicationPickerController.java index d4dcc5e9c47..09ed8da40d4 100644 --- a/src/com/android/settings/dream/DreamComplicationPickerController.java +++ b/src/com/android/settings/dream/DreamComplicationPickerController.java @@ -37,9 +37,7 @@ public class DreamComplicationPickerController extends BasePreferenceController private static final String KEY = "dream_complication_picker"; private final DreamBackend mBackend; - private final Drawable mEnabledDrawable; private final Set mSupportedComplications; - private DreamAdapter mAdapter; private class ComplicationItem implements IDreamItem { private final int mComplicationType; @@ -58,14 +56,13 @@ public class DreamComplicationPickerController extends BasePreferenceController @Override public Drawable getIcon() { // TODO(b/215703483): add icon for each complication - return mEnabled ? mEnabledDrawable : null; + return null; } @Override public void onItemClicked() { mEnabled = !mEnabled; mBackend.setComplicationEnabled(mComplicationType, mEnabled); - mAdapter.notifyDataSetChanged(); } @Override @@ -84,7 +81,6 @@ public class DreamComplicationPickerController extends BasePreferenceController super(context, KEY); mBackend = DreamBackend.getInstance(context); mSupportedComplications = mBackend.getSupportedComplications(); - mEnabledDrawable = context.getDrawable(R.drawable.ic_dream_check_circle); } @Override @@ -102,7 +98,7 @@ public class DreamComplicationPickerController extends BasePreferenceController public void displayPreference(PreferenceScreen screen) { super.displayPreference(screen); - mAdapter = new DreamAdapter(mSupportedComplications.stream() + DreamAdapter adapter = new DreamAdapter(mSupportedComplications.stream() .map(ComplicationItem::new) .collect(Collectors.toList())); @@ -110,7 +106,8 @@ public class DreamComplicationPickerController extends BasePreferenceController if (pref != null) { final RecyclerView recyclerView = pref.findViewById(R.id.dream_list); recyclerView.setLayoutManager(new AutoFitGridLayoutManager(mContext)); - recyclerView.setAdapter(mAdapter); + recyclerView.setHasFixedSize(true); + recyclerView.setAdapter(adapter); } } } diff --git a/src/com/android/settings/dream/DreamPickerController.java b/src/com/android/settings/dream/DreamPickerController.java index 60e2a34afde..e4081ac6647 100644 --- a/src/com/android/settings/dream/DreamPickerController.java +++ b/src/com/android/settings/dream/DreamPickerController.java @@ -31,6 +31,7 @@ import com.android.settings.core.BasePreferenceController; import com.android.settings.overlay.FeatureFactory; import com.android.settingslib.core.instrumentation.MetricsFeatureProvider; import com.android.settingslib.dream.DreamBackend; +import com.android.settingslib.dream.DreamBackend.DreamInfo; import com.android.settingslib.widget.LayoutPreference; import java.util.List; @@ -44,11 +45,10 @@ public class DreamPickerController extends BasePreferenceController { private final DreamBackend mBackend; private final MetricsFeatureProvider mMetricsFeatureProvider; - private final List mDreamInfos; - private final Drawable mActiveDrawable; + private final List mDreamInfos; private Button mPreviewButton; @Nullable - private DreamBackend.DreamInfo mActiveDream; + private DreamInfo mActiveDream; private DreamAdapter mAdapter; public DreamPickerController(Context context) { @@ -59,7 +59,7 @@ public class DreamPickerController extends BasePreferenceController { super(context, KEY); mBackend = backend; mDreamInfos = mBackend.getDreamInfos(); - mActiveDrawable = context.getDrawable(R.drawable.ic_dream_check_circle); + mActiveDream = getActiveDreamInfo(mDreamInfos); mMetricsFeatureProvider = FeatureFactory.getFactory(context).getMetricsFeatureProvider(); } @@ -77,8 +77,6 @@ public class DreamPickerController extends BasePreferenceController { public void updateState(Preference preference) { super.updateState(preference); - mActiveDream = getActiveDreamInfo(); - mAdapter = new DreamAdapter(mDreamInfos.stream() .map(DreamItem::new) .collect(Collectors.toList())); @@ -86,6 +84,7 @@ public class DreamPickerController extends BasePreferenceController { final RecyclerView recyclerView = ((LayoutPreference) preference).findViewById(R.id.dream_list); recyclerView.setLayoutManager(new AutoFitGridLayoutManager(mContext)); + recyclerView.setHasFixedSize(true); recyclerView.setAdapter(mAdapter); mPreviewButton = ((LayoutPreference) preference).findViewById(R.id.preview_button); @@ -101,8 +100,8 @@ public class DreamPickerController extends BasePreferenceController { } @Nullable - private DreamBackend.DreamInfo getActiveDreamInfo() { - return mDreamInfos + private static DreamInfo getActiveDreamInfo(List dreamInfos) { + return dreamInfos .stream() .filter(d -> d.isActive) .findFirst() @@ -110,9 +109,9 @@ public class DreamPickerController extends BasePreferenceController { } private class DreamItem implements IDreamItem { - DreamBackend.DreamInfo mDreamInfo; + DreamInfo mDreamInfo; - DreamItem(DreamBackend.DreamInfo dreamInfo) { + DreamItem(DreamInfo dreamInfo) { mDreamInfo = dreamInfo; } @@ -123,14 +122,13 @@ public class DreamPickerController extends BasePreferenceController { @Override public Drawable getIcon() { - return isActive() ? mActiveDrawable : mDreamInfo.icon; + return mDreamInfo.icon; } @Override public void onItemClicked() { mActiveDream = mDreamInfo; mBackend.setActiveDream(mDreamInfo.componentName); - mAdapter.notifyDataSetChanged(); updatePreviewButtonState(); mMetricsFeatureProvider.action( mContext, diff --git a/src/com/android/settings/dream/DreamSetupActivity.java b/src/com/android/settings/dream/DreamSetupActivity.java new file mode 100644 index 00000000000..6a126713686 --- /dev/null +++ b/src/com/android/settings/dream/DreamSetupActivity.java @@ -0,0 +1,177 @@ +/* + * 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.app.settings.SettingsEnums; +import android.content.Intent; +import android.graphics.drawable.Drawable; +import android.os.Bundle; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; + +import androidx.recyclerview.widget.RecyclerView; + +import com.android.settings.R; +import com.android.settings.SettingsActivity; +import com.android.settings.SettingsPreferenceFragment; +import com.android.settingslib.dream.DreamBackend; + +import com.google.android.setupcompat.template.FooterBarMixin; +import com.google.android.setupcompat.template.FooterButton; +import com.google.android.setupcompat.util.WizardManagerHelper; +import com.google.android.setupdesign.GlifLayout; +import com.google.android.setupdesign.util.ThemeHelper; +import com.google.android.setupdesign.util.ThemeResolver; + +import java.util.List; +import java.util.stream.Collectors; + +/** + * The setup activity for dreams which is displayed during setup wizard. + */ +public class DreamSetupActivity extends SettingsActivity { + @Override + public Intent getIntent() { + Intent modIntent = new Intent(super.getIntent()); + modIntent.putExtra(EXTRA_SHOW_FRAGMENT, DreamSetupFragment.class.getName()); + return modIntent; + } + + @Override + protected boolean isValidFragment(String fragmentName) { + return DreamSetupFragment.class.getName().equals(fragmentName); + } + + @Override + protected void onCreate(Bundle savedInstance) { + setTheme(ThemeResolver.getDefault().resolve(getIntent())); + ThemeHelper.trySetDynamicColor(this); + super.onCreate(savedInstance); + } + + @Override + protected boolean isToolbarEnabled() { + return false; + } + + /** + * Fragment used to control the active dream. + */ + public static class DreamSetupFragment extends SettingsPreferenceFragment { + private DreamBackend mBackend; + private DreamBackend.DreamInfo mActiveDream; + private FooterButton mFooterButton; + + @Override + public int getMetricsCategory() { + return SettingsEnums.DREAM; + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + return inflater.inflate(R.layout.dream_setup_layout, container, false); + } + + @Override + public void onViewCreated(View view, Bundle savedInstanceState) { + super.onViewCreated(view, savedInstanceState); + + mBackend = DreamBackend.getInstance(getContext()); + final List dreamInfos = mBackend.getDreamInfos(); + mActiveDream = dreamInfos.stream().filter(d -> d.isActive).findFirst().orElse(null); + DreamAdapter dreamAdapter = new DreamAdapter(dreamInfos.stream() + .map(DreamItem::new) + .collect(Collectors.toList())); + + final RecyclerView recyclerView = view.findViewById(R.id.dream_setup_list); + recyclerView.setLayoutManager(new AutoFitGridLayoutManager(getContext())); + recyclerView.setHasFixedSize(true); + recyclerView.setAdapter(dreamAdapter); + + final GlifLayout layout = view.findViewById(R.id.setup_wizard_layout); + final FooterBarMixin mixin = layout.getMixin(FooterBarMixin.class); + mFooterButton = new FooterButton.Builder(getContext()) + .setListener(this::onPrimaryButtonClicked) + .setButtonType(FooterButton.ButtonType.NEXT) + .setTheme(R.style.SudGlifButton_Primary) + .build(); + updateFooterButtonText(); + mixin.setPrimaryButton(mFooterButton); + } + + private void updateFooterButtonText() { + final int res = canCustomizeDream() ? R.string.wizard_next : R.string.wizard_finish; + mFooterButton.setText(getContext().getString(res)); + } + + private boolean canCustomizeDream() { + return mActiveDream != null && mActiveDream.settingsComponentName != null; + } + + private void onPrimaryButtonClicked(View view) { + if (canCustomizeDream()) { + final Intent intent = new Intent().setComponent(mActiveDream.settingsComponentName); + WizardManagerHelper.copyWizardManagerExtras(getIntent(), intent); + startActivity(intent); + } + + // Use RESULT_CANCELED here so that the user may go back and change this if they wish. + setResult(RESULT_CANCELED); + finish(); + } + + private class DreamItem implements IDreamItem { + private final DreamBackend.DreamInfo mDreamInfo; + + private DreamItem(DreamBackend.DreamInfo dreamInfo) { + mDreamInfo = dreamInfo; + } + + @Override + public CharSequence getTitle() { + return mDreamInfo.caption; + } + + @Override + public Drawable getIcon() { + return mDreamInfo.icon; + } + + @Override + public void onItemClicked() { + mActiveDream = mDreamInfo; + mBackend.setActiveDream(mDreamInfo.componentName); + updateFooterButtonText(); + } + + @Override + public Drawable getPreviewImage() { + return mDreamInfo.previewImage; + } + + @Override + public boolean isActive() { + if (mActiveDream == null) { + return false; + } + return mDreamInfo.componentName.equals(mActiveDream.componentName); + } + } + } +}