chromium/chrome/browser/download/internal/android/java/src/org/chromium/chrome/browser/download/home/filter/FilterCoordinator.java

// Copyright 2018 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

package org.chromium.chrome.browser.download.home.filter;

import android.content.Context;
import android.view.View;

import androidx.annotation.IntDef;

import org.chromium.base.ObserverList;
import org.chromium.base.supplier.Supplier;
import org.chromium.chrome.browser.download.home.filter.Filters.FilterType;
import org.chromium.chrome.browser.download.internal.R;
import org.chromium.components.browser_ui.widget.chips.ChipsCoordinator;
import org.chromium.ui.modelutil.PropertyModel;
import org.chromium.ui.modelutil.PropertyModelChangeProcessor;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

/** A Coordinator responsible for showing the tab filter selection UI for downloads home. */
public class FilterCoordinator {
    @IntDef({TabType.FILES, TabType.PREFETCH})
    @Retention(RetentionPolicy.SOURCE)
    public @interface TabType {
        int FILES = 0;
        int PREFETCH = 1;
    }

    /** An Observer to notify when the selected tab has changed. */
    public interface Observer {
        /** Called when the selected tab has changed. */
        void onFilterChanged(@FilterType int selectedTab);
    }

    private final ObserverList<Observer> mObserverList = new ObserverList<>();
    private final PropertyModel mModel = new PropertyModel(FilterProperties.ALL_KEYS);
    private final FilterView mView;

    private final ChipsCoordinator mChipsCoordinator;
    private final FilterChipsProvider mChipsProvider;
    private final Supplier<Boolean> mExploreOfflineTabVisibilitySupplier;

    /**
     * Builds a new FilterCoordinator.
     * @param context The context to build the views and pull parameters from.
     * @param chipFilterSource The list of OfflineItems to use to generate the set of available
     *         filters.
     * @param exploreOfflineTabVisibilitySupplier A supplier that indicates whether or not explore
     *         offline tab is shown.
     */
    public FilterCoordinator(
            Context context,
            OfflineItemFilterSource chipFilterSource,
            Supplier<Boolean> exploreOfflineTabVisibilitySupplier) {
        mChipsProvider =
                new FilterChipsProvider(context, type -> handleChipSelected(), chipFilterSource);
        mChipsCoordinator = new ChipsCoordinator(context, mChipsProvider.getChips());
        mChipsCoordinator.setSpaceItemDecoration(
                context.getResources().getDimensionPixelSize(R.dimen.chip_list_chip_spacing),
                context.getResources().getDimensionPixelSize(R.dimen.chip_list_side_padding));
        mExploreOfflineTabVisibilitySupplier = exploreOfflineTabVisibilitySupplier;

        mView = new FilterView(context);
        PropertyModelChangeProcessor.create(mModel, mView, new FilterViewBinder());

        mModel.set(FilterProperties.CHANGE_LISTENER, this::handleTabSelected);
        selectTab(TabType.FILES);

        mModel.set(FilterProperties.SHOW_TABS, mExploreOfflineTabVisibilitySupplier.get());
    }

    /** Tears down this coordinator. */
    public void destroy() {}

    /** @return The {@link View} representing this widget. */
    public View getView() {
        return mView.getView();
    }

    /** Registers {@code observer} to be notified of tab selection changes. */
    public void addObserver(Observer observer) {
        mObserverList.addObserver(observer);
    }

    /** Unregisters {@code observer} from tab selection changes. */
    public void removeObserver(Observer observer) {
        mObserverList.removeObserver(observer);
    }

    /**
     * Pushes a selected filter onto this {@link FilterCoordinator}.  This is used when external
     * components might need to update the UI state.
     */
    public void setSelectedFilter(@FilterType int filter) {
        @TabType int tabSelected;
        if (filter == Filters.FilterType.PREFETCHED && mExploreOfflineTabVisibilitySupplier.get()) {
            tabSelected = TabType.PREFETCH;
        } else {
            mChipsProvider.setFilterSelected(filter);
            tabSelected = TabType.FILES;
        }

        handleTabSelected(tabSelected);
    }

    /** @return The currently selected filter. */
    public @FilterType int getSelectedFilter() {
        if (mModel.get(FilterProperties.SELECTED_TAB) == TabType.PREFETCH) {
            return FilterType.PREFETCHED;
        } else {
            return mChipsProvider.getSelectedFilter();
        }
    }

    private void selectTab(@TabType int selectedTab) {
        mModel.set(FilterProperties.SELECTED_TAB, selectedTab);

        if (selectedTab == TabType.FILES) {
            mModel.set(FilterProperties.CONTENT_VIEW, mChipsCoordinator.getView());
        } else if (selectedTab == TabType.PREFETCH) {
            mModel.set(FilterProperties.CONTENT_VIEW, null);
        }
    }

    private void handleTabSelected(@TabType int selectedTab) {
        selectTab(selectedTab);

        @FilterType int filterType;
        if (selectedTab == TabType.FILES) {
            filterType = mChipsProvider.getSelectedFilter();
        } else {
            filterType = Filters.FilterType.PREFETCHED;
        }

        notifyFilterChanged(filterType);
    }

    private void notifyFilterChanged(@FilterType int filter) {
        for (Observer observer : mObserverList) observer.onFilterChanged(filter);
    }

    private void handleChipSelected() {
        handleTabSelected(mModel.get(FilterProperties.SELECTED_TAB));
    }
}