/*
* Copyright 2015. Appsi Mobile
*
* 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.appsimobile.appsii;
import android.content.Context;
import android.os.Bundle;
import android.support.annotation.StringRes;
import android.support.v4.util.CircularArray;
import android.support.v4.util.SimpleArrayMap;
import android.support.v4.view.PagerAdapter;
import android.util.SparseArray;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import com.appsimobile.appsii.annotation.VisibleForTesting;
import com.appsimobile.appsii.module.home.provider.HomeContract;
/**
* A pager adapter that holds client state. This is modeled after
* fragments and the FragmentStatePagerAdapter
* Created by nick on 10/08/14.
*/
public abstract class AbstractSidebarPagerAdapter extends PagerAdapter {
/**
* A controller frag that indicates that the background and
* other decorations should not be shown
*/
public static final int FLAG_NO_DECORATIONS = 1;
static final int CLOSE_ACTION_AUTO_CLOSE = PageController.CLOSE_ACTION_AUTO_CLOSE;
static final int CLOSE_ACTION_KEEP_OPEN = PageController.CLOSE_ACTION_KEEP_OPEN;
static final int CLOSE_ACTION_ASK = PageController.CLOSE_ACTION_ASK;
static final int CLOSE_ACTION_DONT_KNOW = PageController.CLOSE_ACTION_DONT_KNOW;
@VisibleForTesting
final SimpleArrayMap<HotspotPageEntry, PageController> mCachedControllers =
new SimpleArrayMap<>();
@VisibleForTesting
final SparseArray<HotspotPageEntry> mActivePageKeys = new SparseArray<>(8);
@VisibleForTesting
final SparseArray<PageController> mActivePageControllers = new SparseArray<>(8);
@VisibleForTesting
final SparseArray<PageController> mPrevPageControllers = new SparseArray<>(8);
final boolean mDontCachePages = true;
private final SimpleArrayMap<HotspotPageEntry, Bundle> mSavedControllerStates =
new SimpleArrayMap<>();
private final Context mContext;
FlagListener mFlagListener;
boolean mAttachedToWindow;
@VisibleForTesting
PageController mActivePageController;
boolean mSidebarOpening;
public AbstractSidebarPagerAdapter(
Context context) {
mContext = context;
}
protected String getString(@StringRes int stringResId) {
return mContext.getString(stringResId);
}
public void setFlagListener(FlagListener flagListener) {
mFlagListener = flagListener;
}
public void setPages(CircularArray<HotspotPageEntry> pages) {
// FIXME: this is a bug
if (mActivePageKeys.equals(pages)) return;
// temporary keep the old items in a separate list so we can
// figure out the exact change in getItemPosition
mPrevPageControllers.clear();
int count = mActivePageControllers.size();
for (int i = 0; i < count; i++) {
int key = mActivePageControllers.keyAt(i);
PageController controller = mActivePageControllers.valueAt(i);
mPrevPageControllers.put(key, controller);
}
mActivePageKeys.clear();
mActivePageControllers.clear();
for (int i = 0, position = 0; i < pages.size(); i++) {
HotspotPageEntry page = pages.get(i);
if (!page.mEnabled) continue;
if (page.mPageType == HomeContract.Pages.PAGE_SETTINGS) continue;
if (page.mPageType == HomeContract.Pages.PAGE_SMS) continue;
PageController controller = mCachedControllers.get(page);
if (controller == null) {
controller = instantiateAndCacheControllerForPage(page);
}
mActivePageKeys.put(position, page);
mActivePageControllers.put(position, controller);
position++;
}
notifyDataSetChanged();
}
private PageController instantiateAndCacheControllerForPage(HotspotPageEntry page) {
Bundle state = mSavedControllerStates.get(page);
PageController controller = createPageController(page, state);
controller.setDeferLoads(mSidebarOpening);
controller.setUserVisible(false);
mCachedControllers.put(page, controller);
return controller;
}
public final PageController createPageController(HotspotPageEntry page, Bundle state) {
return onCreatePageController(mContext, page);
}
protected abstract PageController onCreatePageController(Context context,
HotspotPageEntry page);
HotspotPageEntry getPageKey(int position) {
return mActivePageKeys.get(position);
}
@Override
public int getCount() {
return mActivePageKeys.size();
}
@Override
public final Object instantiateItem(ViewGroup container, int position) {
LayoutInflater inflater = LayoutInflater.from(mContext);
PageController controller = mActivePageControllers.get(position);
if (controller == null) {
HotspotPageEntry page = mActivePageKeys.get(position);
controller = instantiateAndCacheControllerForPage(page);
}
HotspotPageEntry page = mActivePageKeys.get(position);
controller.setPage(page);
Bundle state = mSavedControllerStates.get(page);
controller.performCreate(state);
View result = controller.performCreateView(inflater, container);
controller.performViewCreated(result);
if (mAttachedToWindow) {
controller.onSidebarAttached();
} else {
controller.onSidebarDetached();
}
container.addView(controller.getView());
controller.performOnAttach();
controller.performStart();
controller.performRestoreInstanceState();
controller.performResume();
return controller;
}
@Override
public void destroyItem(ViewGroup container, int position, Object object) {
PageController controller = (PageController) object;
HotspotPageEntry page = controller.getPage();
Bundle savedControllerState = mSavedControllerStates.get(page);
if (savedControllerState == null) {
savedControllerState = new Bundle();
mSavedControllerStates.put(page, savedControllerState);
} else {
savedControllerState.clear();
}
controller.performPause();
controller.performSaveInstanceState(savedControllerState);
controller.performStop();
controller.performOnDetach();
container.removeView(controller.getView());
controller.performDestroy();
if (mDontCachePages) {
mActivePageControllers.remove(position);
HotspotPageEntry e = controller.getPage();
mCachedControllers.remove(e);
}
}
@Override
public void setPrimaryItem(ViewGroup container, int position, Object object) {
super.setPrimaryItem(container, position, object);
if (mActivePageController != object) {
if (mActivePageController != null) {
mActivePageController.setUserVisible(false);
mActivePageController.onUserInvisible();
}
mActivePageController = (PageController) object;
// TODO: probably happens when the user disabled all pages (?)
if (mActivePageController != null) {
mActivePageController.setUserVisible(true);
mActivePageController.onUserVisible();
updatePageFlags(mActivePageController.getFlags());
} else {
updatePageFlags(0);
}
}
}
protected void updatePageFlags(int i) {
if (mFlagListener != null) {
mFlagListener.onFlagsChanged(i);
}
}
@Override
public boolean isViewFromObject(View view, Object object) {
PageController controller = (PageController) object;
return view == controller.getView();
}
@Override
public int getItemPosition(Object object) {
int oldPos = keyOf(mPrevPageControllers, (PageController) object);
int newIdx = keyOf(mActivePageControllers, (PageController) object);
if (oldPos != -1) {
mPrevPageControllers.remove(oldPos);
}
if (newIdx == -1) {
return POSITION_NONE;
} else if (newIdx == oldPos) {
return POSITION_UNCHANGED;
} else {
return newIdx;
}
}
static <T> int keyOf(SparseArray<T> array, T object) {
int length = array.size();
for (int i = 0; i < length; i++) {
T value = array.valueAt(i);
if (value == object) return array.keyAt(i);
}
return -1;
}
public void onTrimMemory(int level) {
int length = mActivePageControllers.size();
for (int i = 0; i < length; i++) {
PageController controller = mActivePageControllers.get(i);
if (controller != null) {
controller.onTrimMemory(level);
}
}
}
public void setSidebarClosed() {
if (mActivePageController != null) {
mActivePageController.onUserInvisible();
}
}
public void setSidebarOpening(boolean sidebarOpening) {
mSidebarOpening = sidebarOpening;
int length = mActivePageControllers.size();
for (int i = 0; i < length; i++) {
PageController controller = mActivePageControllers.get(i);
if (controller != null) {
controller.setDeferLoads(sidebarOpening);
}
}
if (mActivePageController != null) {
if (sidebarOpening) {
mActivePageController.onUserInvisible();
} else {
mActivePageController.onUserVisible();
}
// TODO: how come this is not properly called when after using try; loading home?
// seems like the new active page has not yet been added to the list of
// ActivePageControllers
mActivePageController.setDeferLoads(sidebarOpening);
}
}
public void onAttachedToWindow() {
mAttachedToWindow = true;
int length = mActivePageControllers.size();
for (int i = 0; i < length; i++) {
PageController controller = mActivePageControllers.get(i);
if (controller != null) {
controller.onSidebarAttached();
}
}
}
public void onDetachedFromWindow() {
mAttachedToWindow = false;
int length = mActivePageControllers.size();
for (int i = 0; i < length; i++) {
PageController controller = mActivePageControllers.get(i);
if (controller != null) {
controller.onSidebarDetached();
}
}
}
public int shouldClose(Bundle state) {
if (mActivePageController != null) {
return mActivePageController.shouldClose(state);
}
return PageController.CLOSE_ACTION_DONT_KNOW;
}
public void rememberCloseAction(Bundle state, int action) {
if (mActivePageController != null) {
mActivePageController.rememberCloseAction(state, action);
}
}
interface FlagListener {
void onFlagsChanged(int flags);
}
}