Merge "[ThemePicker 4/N] Add a PreviewPager widget" into ub-launcher3-master
diff --git a/res/values/config.xml b/res/values/config.xml
new file mode 100644
index 0000000..d992bf3
--- /dev/null
+++ b/res/values/config.xml
@@ -0,0 +1,22 @@
+<?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
diff --git a/src/com/android/customization/model/CustomizationOption.java b/src/com/android/customization/model/CustomizationOption.java
new file mode 100644
index 0000000..8c668da
--- /dev/null
+++ b/src/com/android/customization/model/CustomizationOption.java
@@ -0,0 +1,56 @@
+/*
+ * 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
diff --git a/src/com/android/customization/widget/OptionSelectorController.java b/src/com/android/customization/widget/OptionSelectorController.java
new file mode 100644
index 0000000..91dbabb
--- /dev/null
+++ b/src/com/android/customization/widget/OptionSelectorController.java
@@ -0,0 +1,144 @@
+/*
+ * 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);
+        }
+    }
+}