package com.gh4a.fragment;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentTransaction;
import android.support.v4.app.LoaderManager;
import android.support.v4.content.Loader;
import android.text.TextUtils;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import com.gh4a.BaseActivity;
import com.gh4a.R;
import com.gh4a.activities.FileViewerActivity;
import com.gh4a.activities.RepositoryActivity;
import com.gh4a.loader.GitModuleParserLoader;
import com.gh4a.loader.LoaderCallbacks;
import com.gh4a.loader.LoaderResult;
import com.gh4a.widget.PathBreadcrumbs;
import com.gh4a.widget.SwipeRefreshLayout;
import org.eclipse.egit.github.core.Repository;
import org.eclipse.egit.github.core.RepositoryContents;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Stack;
public class ContentListContainerFragment extends Fragment implements
ContentListFragment.ParentCallback, PathBreadcrumbs.SelectionCallback,
LoaderCallbacks.ParentCallback, SwipeRefreshLayout.ChildScrollDelegate {
private static final int LOADER_MODULEMAP = 100;
private static final String STATE_KEY_DIR_STACK = "dir_stack";
private static final String STATE_KEY_CONTENT_CACHE_PREFIX = "content_cache_";
private static final String STATE_KEY_INITIAL_PATH = "initial_path";
private PathBreadcrumbs mBreadcrumbs;
private ContentListFragment mContentListFragment;
private Repository mRepository;
private String mSelectedRef;
private Map<String, String> mGitModuleMap;
private final Stack<String> mDirStack = new Stack<>();
private ArrayList<String> mInitialPathToLoad;
private boolean mStateSaved;
private final Map<String, ArrayList<RepositoryContents>> mContentCache =
new LinkedHashMap<String, ArrayList<RepositoryContents>>() {
private static final long serialVersionUID = -2379579224736389357L;
private static final int MAX_CACHE_ENTRIES = 100;
@Override
protected boolean removeEldestEntry(Entry<String, ArrayList<RepositoryContents>> eldest) {
return size() > MAX_CACHE_ENTRIES;
}
};
private final LoaderCallbacks<Map<String, String>> mGitModuleCallback =
new LoaderCallbacks<Map<String, String>>(this) {
@Override
protected Loader<LoaderResult<Map<String, String>>> onCreateLoader() {
return new GitModuleParserLoader(getActivity(), mRepository.getOwner().getLogin(),
mRepository.getName(), mSelectedRef);
}
@Override
protected void onResultReady(Map<String, String> result) {
mGitModuleMap = result;
if (mContentListFragment != null) {
mContentListFragment.onSubModuleNamesChanged(getSubModuleNames(mContentListFragment));
}
}
};
public static ContentListContainerFragment newInstance(Repository repository,
String ref, String initialPath) {
ContentListContainerFragment f = new ContentListContainerFragment();
Bundle args = new Bundle();
args.putSerializable("repository", repository);
args.putString("ref", ref);
args.putString("initialpath", initialPath);
f.setArguments(args);
return f;
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mRepository = (Repository) getArguments().getSerializable("repository");
mSelectedRef = getArguments().getString("ref");
mStateSaved = false;
if (savedInstanceState != null) {
for (String entry : savedInstanceState.getStringArrayList(STATE_KEY_DIR_STACK)) {
mDirStack.add(entry);
}
int prefixLen = STATE_KEY_CONTENT_CACHE_PREFIX.length();
for (String key : savedInstanceState.keySet()) {
if (key.startsWith(STATE_KEY_CONTENT_CACHE_PREFIX)) {
String cacheKey = key.substring(prefixLen);
if (cacheKey.equals("/")) {
cacheKey = null;
}
mContentCache.put(cacheKey,
(ArrayList<RepositoryContents>) savedInstanceState.getSerializable(key));
}
}
mInitialPathToLoad = savedInstanceState.getStringArrayList(STATE_KEY_INITIAL_PATH);
} else {
mDirStack.push("");
String initialPath = getArguments().getString("initialpath");
if (initialPath != null) {
mInitialPathToLoad = new ArrayList<>();
int pos = initialPath.indexOf("/");
while (pos > 0) {
mInitialPathToLoad.add(initialPath.substring(0, pos));
pos = initialPath.indexOf("/", pos + 1);
}
mInitialPathToLoad.add(initialPath);
}
}
}
@Override
public boolean canChildScrollUp() {
if (mContentListFragment != null) {
return mContentListFragment.canChildScrollUp();
}
return false;
}
@Override
public void onRefresh() {
setRef(mSelectedRef);
}
@Override
public BaseActivity getBaseActivity() {
return (BaseActivity) getActivity();
}
public void setRef(String ref) {
getArguments().putString("ref", ref);
mSelectedRef = ref;
mGitModuleMap = null;
mInitialPathToLoad = new ArrayList<>();
for (int i = 1; i < mDirStack.size(); i++) {
mInitialPathToLoad.add(mDirStack.get(i));
}
mDirStack.clear();
mDirStack.push("");
mContentCache.clear();
mContentListFragment = null;
getChildFragmentManager().popBackStackImmediate(null,
FragmentManager.POP_BACK_STACK_INCLUSIVE);
addFragmentForTopOfStack();
updateBreadcrumbs();
}
public boolean handleBackPress() {
if (mDirStack.size() > 1) {
mDirStack.pop();
getChildFragmentManager().popBackStackImmediate();
mContentListFragment = (ContentListFragment)
getChildFragmentManager().findFragmentById(R.id.content_list_container);
updateBreadcrumbs();
return true;
}
return false;
}
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
return inflater.inflate(R.layout.content_list, container, false);
}
@Override
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
mBreadcrumbs = (PathBreadcrumbs) view.findViewById(R.id.breadcrumbs);
mBreadcrumbs.setCallback(this);
mStateSaved = false;
updateBreadcrumbs();
addFragmentForTopOfStack();
}
@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putStringArrayList(STATE_KEY_DIR_STACK, new ArrayList<>(mDirStack));
outState.putStringArrayList(STATE_KEY_INITIAL_PATH, mInitialPathToLoad);
for (Map.Entry<String, ArrayList<RepositoryContents>> entry : mContentCache.entrySet()) {
String key = entry.getKey();
outState.putSerializable(STATE_KEY_CONTENT_CACHE_PREFIX + key, entry.getValue());
}
mStateSaved = true;
}
@Override
public void onContentsLoaded(ContentListFragment fragment, List<RepositoryContents> contents) {
if (contents == null) {
return;
}
mContentCache.put(fragment.getPath(), new ArrayList<>(contents));
if (fragment.getPath() == null) {
for (RepositoryContents content : contents) {
if (RepositoryContents.TYPE_FILE.equals(content.getType())) {
if (content.getName().equals(".gitmodules")) {
LoaderManager lm = getActivity().getSupportLoaderManager();
lm.restartLoader(LOADER_MODULEMAP, null, mGitModuleCallback);
break;
}
}
}
}
if (mInitialPathToLoad != null && !mInitialPathToLoad.isEmpty() && !mStateSaved) {
String itemToLoad = mInitialPathToLoad.get(0);
boolean found = false;
for (RepositoryContents content : contents) {
if (RepositoryContents.TYPE_DIR.equals(content.getType())) {
if (content.getPath().equals(itemToLoad)) {
onTreeSelected(content);
found = true;
break;
}
}
}
if (found) {
mInitialPathToLoad.remove(0);
} else {
mInitialPathToLoad = null;
}
}
}
@Override
public void onTreeSelected(RepositoryContents content) {
String path = content.getPath();
if (RepositoryContents.TYPE_DIR.equals(content.getType())) {
mDirStack.push(path);
updateBreadcrumbs();
addFragmentForTopOfStack();
} else if (mGitModuleMap != null && mGitModuleMap.get(path) != null) {
String[] userRepo = mGitModuleMap.get(path).split("/");
startActivity(RepositoryActivity.makeIntent(getActivity(), userRepo[0], userRepo[1]));
} else {
startActivity(FileViewerActivity.makeIntent(getActivity(),
mRepository.getOwner().getLogin(), mRepository.getName(),
getCurrentRef(), content.getPath()));
}
}
@Override
public Set<String> getSubModuleNames(ContentListFragment fragment) {
if (mGitModuleMap == null) {
return null;
}
String prefix = fragment.getPath() == null ? null : (fragment.getPath() + "/");
Set<String> names = new HashSet<>();
for (String name : mGitModuleMap.keySet()) {
if (prefix == null && !name.contains("/")) {
names.add(name);
} else if (prefix != null && name.startsWith(prefix)) {
names.add(name.substring(prefix.length()));
}
}
return names;
}
@Override
public void onCrumbSelection(String absolutePath, int index, int count) {
FragmentManager fm = getChildFragmentManager();
boolean poppedAny = false;
while (mDirStack.size() > 1 && !TextUtils.equals(absolutePath, mDirStack.peek())) {
mDirStack.pop();
fm.popBackStack();
poppedAny = true;
}
if (poppedAny) {
fm.executePendingTransactions();
updateBreadcrumbs();
}
}
private void addFragmentForTopOfStack() {
String path = mDirStack.peek();
mContentListFragment = ContentListFragment.newInstance(mRepository,
TextUtils.isEmpty(path) ? null : path,
mContentCache.get(path), mSelectedRef);
FragmentTransaction ft = getChildFragmentManager().beginTransaction();
if (path != null) {
ft.addToBackStack(null);
}
ft.replace(R.id.content_list_container, mContentListFragment);
ft.commit();
}
private void updateBreadcrumbs() {
String path = mDirStack.peek();
mBreadcrumbs.setPath(path);
}
private String getCurrentRef() {
if (!TextUtils.isEmpty(mSelectedRef)) {
return mSelectedRef;
}
return mRepository.getDefaultBranch();
}
}