ldjesper 39f4a36d0b Remove Abstract Calendar Pickers in favor of options
PiperOrigin-RevId: 256402016
2019-07-03 14:16:01 -04:00

164 lines
6.0 KiB
Java

/*
* Copyright 2019 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.google.android.material.picker;
import androidx.lifecycle.DefaultLifecycleObserver;
import androidx.lifecycle.Lifecycle;
import androidx.lifecycle.LifecycleOwner;
import android.content.Context;
import androidx.annotation.NonNull;
import androidx.fragment.app.FragmentManager;
import androidx.recyclerview.widget.RecyclerView;
import androidx.recyclerview.widget.RecyclerView.AdapterDataObserver;
import androidx.recyclerview.widget.RecyclerView.LayoutParams;
import android.util.SparseArray;
import androidx.viewpager2.adapter.FragmentStateAdapter;
import androidx.viewpager2.adapter.FragmentViewHolder;
import com.google.android.material.picker.MaterialCalendar.OnDayClickListener;
import java.util.List;
/**
* Manages the instances of {@link MonthFragment}, capping memory usage.
*
* @hide
*/
class MonthsPagerAdapter extends FragmentStateAdapter {
private final CalendarBounds calendarBounds;
private final int startIndex;
private final GridSelector<?> gridSelector;
private final SparseArray<AdapterDataObserver> observingFragments = new SparseArray<>();
private final OnDayClickListener onDayClickListener;
private final int itemHeight;
/**
* Creates a new {@link FragmentStateAdapter} that manages instances of {@link MonthFragment}.
*
* @param context The {@link Context} with the calendar theme and dimensions.
* @param fragmentManager A {@link FragmentManager} for the {@link MonthFragment} objects. {@see
* Fragment#getChildFragmentManager()} and {@see
* FragmentActivity#getSupportFragmentManager()}.
* @param lifecycle The {@link Lifecycle} to manage each {@link MonthFragment}. {@see
* Fragment#getLifecycle()} and {@see FragmentActivity#getLifecycle()}.
* @param gridSelector The {@link GridSelector} that controls selection and highlights for all
* {@link MonthFragment} objects.
* @param calendarBounds The {@link CalendarBounds} that specifies the valid range and starting
* point for selection.
*/
MonthsPagerAdapter(
Context context,
FragmentManager fragmentManager,
Lifecycle lifecycle,
GridSelector<?> gridSelector,
CalendarBounds calendarBounds,
OnDayClickListener onDayClickListener) {
super(fragmentManager, lifecycle);
Month firstPage = calendarBounds.getStart();
Month lastPage = calendarBounds.getEnd();
Month currentPage = calendarBounds.getCurrent();
if (firstPage.compareTo(currentPage) > 0) {
throw new IllegalArgumentException("firstPage cannot be after currentPage");
}
if (currentPage.compareTo(lastPage) > 0) {
throw new IllegalArgumentException("currentPage cannot be after lastPage");
}
int daysHeight = MonthAdapter.MAXIMUM_WEEKS * MaterialCalendar.getDayHeight(context);
int labelHeight =
MaterialDatePicker.isFullscreen(context) ? MaterialCalendar.getDayHeight(context) : 0;
this.itemHeight = daysHeight + labelHeight;
this.calendarBounds = calendarBounds;
startIndex = firstPage.monthsUntil(currentPage);
this.gridSelector = gridSelector;
this.onDayClickListener = onDayClickListener;
}
@Override
public void onAttachedToRecyclerView(@NonNull RecyclerView recyclerView) {
super.onAttachedToRecyclerView(recyclerView);
// TODO: Remove ViewPager2 workaround
recyclerView.clearOnChildAttachStateChangeListeners();
}
@Override
public void onBindViewHolder(
@NonNull FragmentViewHolder holder, int position, @NonNull List<Object> payloads) {
super.onBindViewHolder(holder, position, payloads);
holder.itemView.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, itemHeight));
}
@Override
public int getItemCount() {
return calendarBounds.getMonthSpan();
}
@Override
public MonthFragment createFragment(final int position) {
final MonthFragment monthFragment =
MonthFragment.newInstance(calendarBounds.getStart().monthsLater(position), gridSelector);
monthFragment
.getLifecycle()
.addObserver(
new DefaultLifecycleObserver() {
@Override
public void onCreate(LifecycleOwner owner) {
monthFragment.setOnDayClickListener(onDayClickListener);
AdapterDataObserver dataSetObserver =
new AdapterDataObserver() {
@Override
public void onChanged() {
monthFragment.notifyDataSetChanged();
}
};
registerAdapterDataObserver(dataSetObserver);
observingFragments.put(position, dataSetObserver);
}
@Override
public void onDestroy(LifecycleOwner owner) {
AdapterDataObserver dataSetObserver = observingFragments.get(position);
if (dataSetObserver != null) {
observingFragments.remove(position);
unregisterAdapterDataObserver(dataSetObserver);
}
}
});
return monthFragment;
}
/** Returns the position index of the {@link Month} startPage provided on construction. */
int getStartPosition() {
return startIndex;
}
@NonNull
CharSequence getPageTitle(int position) {
return getPageMonth(position).getLongName();
}
Month getPageMonth(int position) {
return calendarBounds.getStart().monthsLater(position);
}
int getPosition(Month month) {
return calendarBounds.getStart().monthsUntil(month);
}
}