Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit ac1535ce authored by Santiago Etchebehere's avatar Santiago Etchebehere
Browse files

ThemePicker: Add an options selector widget

Create a controller class to wrap a RecyclerView Adapter
used to display the thumbnail tiles with customization
options in each section

Bug: 120559294
Change-Id: I67a99e5c1caaf03ea96a02a7ffbadd15d0b14b7e
parent 6d6e84d8
Loading
Loading
Loading
Loading

res/values/config.xml

0 → 100644
+22 −0
Original line number Diff line number Diff line
<?xml version="1.0" encoding="utf-8"?>
<!--
     Copyright (C) 2018 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.
-->
<resources>
    <!-- ID for the view for a tile in the options strip -->
    <item type="id" name="option_tile" />
    <!-- ID for the label of an option tile -->
    <item type="id" name="option_label" />
</resources>
 No newline at end of file
+56 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2018 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.customization.model;

import android.view.View;

import com.android.wallpaper.R;

import androidx.annotation.LayoutRes;


/**
 * Represents an option of customization (eg, a Theme, a Clock face, a Grid size)
 */
public interface CustomizationOption {

    /**
     * Optional name or label for this option
     */
    String getTitle();

    /**
     * Will be called to bind the thumbnail tile to be displayed in the picker.
     *
     * @param view the View to bind, corresponding to a view inside the layout specified in
     *        {@link #getLayoutResId()} with id {@link R.id#option_tile}
     */
    void bindThumbnailTile(View view);

    /**
     * Returns whether this option is the one currently set in the System.
     */
    boolean isCurrentlySet();

    /**
     * Return the id of the layout used to show this option in the UI. It must contain a view with
     * id {@link com.android.wallpaper.R.id#option_tile} that will be passed to
     * {@link #bindThumbnailTile(View)} on bind time, and optionally a TextView with id
     * {@link R.id#option_label} that will be populated with the value from {@link #getTitle()} if
     * present.
     */
    @LayoutRes int getLayoutResId();
}
 No newline at end of file
+144 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2018 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.customization.widget;

import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;

import com.android.customization.model.CustomizationOption;
import com.android.wallpaper.R;

import java.util.HashSet;
import java.util.List;
import java.util.Set;

import androidx.annotation.NonNull;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;

/**
 * Simple controller for a RecyclerView-based widget to hold the options for each customization
 * section (eg, thumbnails for themes, clocks, grid sizes).
 * To use, just pass the RV that will contain the tiles and the list of {@link CustomizationOption}
 * representing each option, and call {@link #initOptions()} to populate the widget.
 */
public class OptionSelectorController {

    /**
     * Interface to be notified when an option is selected by the user.
     */
    public interface OptionSelectedListener {
        /**
         * Called when an option has been selected (and marked as such in the UI)
         */
        void onOptionSelected(CustomizationOption selected);
    }

    private final RecyclerView mContainer;
    private final List<CustomizationOption> mOptions;

    private final Set<OptionSelectedListener> mListeners = new HashSet<>();
    private RecyclerView.Adapter<TileViewHolder> mAdapter;
    private CustomizationOption mSelectedOption;

    public OptionSelectorController(RecyclerView container, List<CustomizationOption> options) {
        mContainer = container;
        mOptions = options;
    }

    public void addListener(OptionSelectedListener listener) {
        mListeners.add(listener);
    }

    public void removeListener(OptionSelectedListener listener) {
        mListeners.remove(listener);
    }

    public void setSelectedOption(CustomizationOption option) {
        if (!mOptions.contains(option)) {
            throw new IllegalArgumentException("Invalid option");
        }
        mSelectedOption = option;
        mAdapter.notifyDataSetChanged();
        notifyListeners();
    }

    /**
     * Initializes the UI for the options passed in the constructor of this class.
     */
    public void initOptions() {
        mAdapter = new RecyclerView.Adapter<TileViewHolder>() {
            @Override
            public int getItemViewType(int position) {
                return mOptions.get(position).getLayoutResId();
            }

            @NonNull
            @Override
            public TileViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
                View v = LayoutInflater.from(parent.getContext()).inflate(viewType, parent, false);
                return new TileViewHolder(v);
            }

            @Override
            public void onBindViewHolder(@NonNull TileViewHolder holder, int position) {
                CustomizationOption option = mOptions.get(position);
                if (mSelectedOption == null && option.isCurrentlySet()) {
                    mSelectedOption = option;
                }
                if (holder.labelView != null) {
                    holder.labelView.setText(option.getTitle());
                }
                option.bindThumbnailTile(holder.tileView);
                holder.itemView.setSelected(option.equals(mSelectedOption));
                holder.itemView.setOnClickListener(view -> setSelectedOption(option));
            }

            @Override
            public int getItemCount() {
                return mOptions.size();
            }
        };

        mContainer.setLayoutManager(new LinearLayoutManager(mContainer.getContext(),
                LinearLayoutManager.HORIZONTAL, false));
        mContainer.setAdapter(mAdapter);
    }

    private void notifyListeners() {
        if (mListeners.isEmpty()) {
            return;
        }
        CustomizationOption option = mSelectedOption;
        Set<OptionSelectedListener> iterableListeners = new HashSet<>(mListeners);
        for (OptionSelectedListener listener : iterableListeners) {
            listener.onOptionSelected(option);
        }
    }

    private static class TileViewHolder extends RecyclerView.ViewHolder {
        TextView labelView;
        View tileView;

        TileViewHolder(@NonNull View itemView) {
            super(itemView);
            labelView = itemView.findViewById(R.id.option_label);
            tileView = itemView.findViewById(R.id.option_tile);
        }
    }
}