/*
* Copyright (c) 2014 OpenSilk Productions LLC
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.opensilk.common.ui.mortar;
import android.content.Context;
import android.os.Bundle;
import android.os.Parcelable;
import android.support.annotation.NonNull;
import android.support.v4.view.PagerAdapter;
import android.util.SparseArray;
import android.view.View;
import android.view.ViewGroup;
import org.opensilk.common.ui.util.ViewUtils;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import mortar.MortarScope;
/**
* View pager adapter that uses mortar magick to take care of inflating,
* scoping and saving views
*
* Screens should be annotated with @Layout and @WithComponent
*
* Created by drew on 11/16/14.
*/
public class MortarPagerAdapter<S extends Screen, V extends View> extends PagerAdapter {
protected static final class Page<SS extends Screen, VV extends View> {
public final SS screen;
public final VV view;
Page(SS screen, VV view) {
this.screen = screen;
this.view = view;
}
}
protected final LayoutCreator layoutCreater;
protected final MortarContextFactory contextFactory;
protected final SparseArray<Page<S,V>> activePages = new SparseArray<>();
protected Bundle savedState = new Bundle();
protected final Context context;
protected final ArrayList<S> screens;
public MortarPagerAdapter(Context context) {
this (context, new ArrayList<S>());
}
public MortarPagerAdapter(Context context, S[] screens) {
this (context, Arrays.asList(screens));
}
public MortarPagerAdapter(@NonNull Context context, @NonNull List<S> screens) {
this.context = context;
MortarScope scope = MortarScope.getScope(context);
if (scope.hasService(LayoutCreator.SERVICE_NAME)) {
layoutCreater = scope.getService(LayoutCreator.SERVICE_NAME);
} else {
layoutCreater = new LayoutCreator();
}
if (scope.hasService(ScreenScoper.SERVICE_NAME)) {
contextFactory = new MortarContextFactory(scope.<ScreenScoper>getService(ScreenScoper.SERVICE_NAME));
} else {
contextFactory = new MortarContextFactory();
}
this.screens = new ArrayList<>(screens);
}
@Override
public Object instantiateItem(ViewGroup container, int position) {
S screen = screens.get(position);
Context newChildContext = decorateContext(contextFactory.setUpContext(screen, context), position);
V newChild = ViewUtils.inflate(newChildContext, layoutCreater.getLayout(screens.get(position)), container, false);
ViewUtils.restoreState(newChild, savedState, screen.getName());
container.addView(newChild);
Page<S,V> newPage = new Page<S,V>(screen, newChild);
activePages.put(position, newPage);
return newPage;
}
@Override
public void destroyItem(ViewGroup container, int position, Object o) {
Page oldPage = (Page) o;
activePages.remove(position);
ViewUtils.saveState(oldPage.view, savedState, oldPage.screen.getName());
contextFactory.tearDownContext(oldPage.view.getContext());
container.removeView(oldPage.view);
}
@Override
public int getCount() {
return screens.size();
}
@Override
public boolean isViewFromObject(android.view.View view, Object o) {
return view == ((Page) o).view;
}
@Override
public Parcelable saveState() {
final int size = activePages.size();
for (int ii=0; ii<size; ii++) {
Page<S,V> p = activePages.valueAt(ii);
ViewUtils.saveState(p.view, savedState, p.screen.getName());
}
return savedState;
}
@Override
public void restoreState(Parcelable state, ClassLoader loader) {
if (state == null || !(state instanceof Bundle)) return;
Bundle b = (Bundle) state;
b.setClassLoader(loader);
savedState = b;
}
/**
* @return Modifiable list of screens, be sure to call
* {@link #notifyDataSetChanged()} to propagate any changes
*/
public List<S> screens() {
return screens;
}
/** Allows themeing views */
protected Context decorateContext(Context newChildContext, int position) {
return newChildContext;
}
}