package com.alorma.github.ui.view;
import android.annotation.TargetApi;
import android.app.Activity;
import android.content.Context;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.support.annotation.NonNull;
import android.support.v4.view.ViewCompat;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.HorizontalScrollView;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
import com.alorma.github.R;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
/**
* @author Aidan Follestad (afollestad)
*/
public class LinearBreadcrumb extends HorizontalScrollView implements View.OnClickListener {
private List<Crumb> mCrumbs;
private LinearLayout mChildFrame;
private int mActive;
private SelectionCallback mCallback;
public LinearBreadcrumb(Context context) {
super(context);
init();
}
public LinearBreadcrumb(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public LinearBreadcrumb(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
private void init() {
setMinimumHeight((int) getResources().getDimension(R.dimen.breadcrumb_height));
setClipToPadding(false);
mCrumbs = new ArrayList<>();
mChildFrame = new LinearLayout(getContext());
addView(mChildFrame, new ViewGroup.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.MATCH_PARENT));
}
@TargetApi(Build.VERSION_CODES.JELLY_BEAN)
private void setAlpha(View view, int alpha) {
if (view instanceof ImageView && Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
((ImageView) view).setImageAlpha(alpha);
} else {
ViewCompat.setAlpha(view, alpha);
}
}
public void addPath(@NonNull String path, @NonNull String separator) {
clearCrumbs();
initRootCrumb();
String[] paths = path.split(separator);
Crumb lastCrumb = null;
for (String splitPath : paths) {
lastCrumb = new Crumb(splitPath);
addCrumb(lastCrumb, false);
}
if (lastCrumb != null) {
setActive(lastCrumb);
}
}
public void addCrumb(@NonNull Crumb crumb, boolean refreshLayout) {
LinearLayout view = (LinearLayout) LayoutInflater.from(getContext()).inflate(R.layout.bread_crumb, this, false);
view.setTag(mCrumbs.size());
view.setOnClickListener(this);
ImageView iv = (ImageView) view.getChildAt(1);
Drawable arrow = getResources().getDrawable(R.drawable.ic_right_arrow);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
if (arrow != null) {
arrow.setAutoMirrored(true);
}
}
iv.setImageDrawable(arrow);
iv.setVisibility(View.GONE);
mChildFrame.addView(view, new ViewGroup.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT));
mCrumbs.add(crumb);
if (refreshLayout) {
mActive = mCrumbs.size() - 1;
requestLayout();
}
invalidateActivatedAll();
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
super.onLayout(changed, l, t, r, b);
//RTL works fine like this
View child = mChildFrame.getChildAt(mActive);
if (child != null) smoothScrollTo(child.getLeft(), 0);
}
public Crumb findCrumb(@NonNull String forDir) {
for (int i = 0; i < mCrumbs.size(); i++) {
if (mCrumbs.get(i).getPath().equals(forDir)) return mCrumbs.get(i);
}
return null;
}
public void clearCrumbs() {
try {
mCrumbs.clear();
mChildFrame.removeAllViews();
} catch (IllegalStateException e) {
e.printStackTrace();
}
}
public Crumb getCrumb(int index) {
return mCrumbs.get(index);
}
public void setCallback(SelectionCallback callback) {
mCallback = callback;
}
public boolean setActive(Crumb newActive) {
mActive = mCrumbs.indexOf(newActive);
invalidateActivatedAll();
boolean success = mActive > -1;
if (success) requestLayout();
return success;
}
private void invalidateActivatedAll() {
for (int i = 0; i < mCrumbs.size(); i++) {
Crumb crumb = mCrumbs.get(i);
invalidateActivated(mChildFrame.getChildAt(i), mActive == mCrumbs.indexOf(crumb), false, i < mCrumbs.size() - 1).setText(
crumb.getTitle());
}
}
private void removeCrumbAt(int index) {
mCrumbs.remove(index);
mChildFrame.removeViewAt(index);
}
private void updateIndices() {
for (int i = 0; i < mChildFrame.getChildCount(); i++)
mChildFrame.getChildAt(i).setTag(i);
}
private boolean isValidPath(String path) {
return path == null;
}
public int size() {
return mCrumbs.size();
}
private TextView invalidateActivated(View view, boolean isActive, boolean noArrowIfAlone, boolean allowArrowVisible) {
LinearLayout child = (LinearLayout) view;
TextView tv = (TextView) child.getChildAt(0);
tv.setTextColor(getResources().getColor(isActive ? R.color.crumb_active : R.color.crumb_inactive));
ImageView iv = (ImageView) child.getChildAt(1);
setAlpha(iv, isActive ? 255 : 109);
if (noArrowIfAlone && getChildCount() == 1) {
iv.setVisibility(View.GONE);
} else if (allowArrowVisible) iv.setVisibility(View.VISIBLE);
return tv;
}
public int getActiveIndex() {
return mActive;
}
@Override
public void onClick(View v) {
if (mCallback != null) {
int index = (Integer) v.getTag();
if (index >= 0 && index < size()) {
mCallback.onCrumbSelection(mCrumbs.get(index), getAbsolutePath(mCrumbs.get(index), "/"), mCrumbs.size(), index);
}
}
}
public SavedStateWrapper getStateWrapper() {
return new SavedStateWrapper(this);
}
public void restoreFromStateWrapper(SavedStateWrapper mSavedState, Activity context) {
if (mSavedState != null) {
mActive = mSavedState.mActive;
for (Crumb c : mSavedState.mCrumbs) {
addCrumb(c, false);
}
requestLayout();
setVisibility(mSavedState.mVisibility);
}
}
public String getAbsolutePath(Crumb crumb, @NonNull String separator) {
StringBuilder builder = new StringBuilder();
if (size() > 1) {
List<Crumb> crumbs = mCrumbs.subList(1, size());
for (Crumb mCrumb : crumbs) {
builder.append(mCrumb.getPath());
builder.append(separator);
if (mCrumb.equals(crumb)) {
break;
}
}
String path = builder.toString();
return path.substring(0, path.length() - 1);
} else {
return null;
}
}
public void initRootCrumb() {
clearCrumbs();
addCrumb(new Crumb("/"), true);
}
public interface SelectionCallback {
void onCrumbSelection(Crumb crumb, String absolutePath, int count, int index);
}
public static class Crumb implements Serializable {
private final String mPath;
private int mScrollY;
private int mScrollOffset;
public Crumb(String path) {
mPath = path;
}
public int getScrollY() {
return mScrollY;
}
public void setScrollY(int scrollY) {
this.mScrollY = scrollY;
}
public int getScrollOffset() {
return mScrollOffset;
}
public void setScrollOffset(int scrollOffset) {
this.mScrollOffset = scrollOffset;
}
public String getPath() {
return mPath;
}
public String getTitle() {
return (mPath != null && mPath.equals("/")) ? "ROOT" : mPath;
}
@Override
public boolean equals(Object o) {
return (o instanceof Crumb) && ((Crumb) o).getPath().equals(getPath());
}
@Override
public String toString() {
return getPath();
}
}
public static class SavedStateWrapper implements Serializable {
public final int mActive;
public final List<Crumb> mCrumbs;
public final int mVisibility;
public SavedStateWrapper(LinearBreadcrumb view) {
mActive = view.mActive;
mCrumbs = view.mCrumbs;
mVisibility = view.getVisibility();
}
}
}