package org.commcare.android.framework;
import java.util.Vector;
import org.commcare.android.database.user.models.ACase;
import org.commcare.android.util.SessionUnavailableException;
import org.commcare.dalvik.R;
import org.commcare.dalvik.application.CommCareApplication;
import org.commcare.util.SessionFrame;
import android.annotation.TargetApi;
import android.app.ActionBar;
import android.app.ActionBar.LayoutParams;
import android.app.Activity;
import android.os.Build;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.FrameLayout;
import android.widget.HorizontalScrollView;
import android.widget.RelativeLayout;
import android.widget.TextView;
/**
* @author ctsims
*
*/
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
public class BreadcrumbBarFragment extends Fragment {
private boolean isTopNavEnabled = false;
private int localIdPart = -1;
/*
* (non-Javadoc)
* @see android.support.v4.app.Fragment#onCreate(android.os.Bundle)
*
* This method will only be called once when the retained
* Fragment is first created.
*/
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Retain this fragment across configuration changes.
setRetainInstance(true);
}
/*
* (non-Javadoc)
* @see android.support.v4.app.Fragment#onAttach(android.app.Activity)
*
* Hold a reference to the parent Activity so we can report the
* task's current progress and results. The Android framework
* will pass us a reference to the newly created Activity after
* each configuration change.
*/
@Override
public void onAttach(Activity activity) {
super.onAttach(activity);
String activityTitle = null;
if(activity instanceof CommCareActivity) {
activityTitle = ((CommCareActivity)activity).getActivityTitle();
isTopNavEnabled = ((CommCareActivity)activity).isTopNavEnabled();
}
ActionBar actionBar = activity.getActionBar();
//We need to get the amount that each item should "bleed" over to the left, and move the whole widget that
//many pixels. This replicates the "overlap" space that each piece of the bar has on the next piece for
//the left-most element.
int buffer = Math.round(activity.getResources().getDimension(R.dimen.title_round_bleed));
LayoutParams p = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.MATCH_PARENT);
p.leftMargin = buffer;
actionBar.setCustomView(getTitleView(activity, activityTitle), p);
activity.setTitle("");
actionBar.setDisplayShowHomeEnabled(false);
}
/* (non-Javadoc)
* @see android.support.v4.app.Fragment#onResume()
*/
@Override
public void onResume() {
super.onResume();
if(this.getActivity() instanceof CommCareActivity) {
String title = ((CommCareActivity)this.getActivity()).getActivityTitle();
//This part can change more dynamically
if(localIdPart != -1 && title != null) {
TextView text = (TextView)this.getActivity().getActionBar().getCustomView().findViewById(localIdPart);
text.setText(title);
}
}
}
public View getTitleView(final Activity activity, String local) {
RelativeLayout layout = new RelativeLayout(activity);
HorizontalScrollView scroller = new HorizontalScrollView(activity) {
/*
* (non-Javadoc)
* @see android.widget.HorizontalScrollView#onLayout(boolean, int, int, int, int)
*/
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
super.onLayout(changed, l, t, r, b);
this.fullScroll(HorizontalScrollView.FOCUS_RIGHT);
}
};
scroller.setHorizontalScrollBarEnabled(false);
scroller.addView(layout, new HorizontalScrollView.LayoutParams(LayoutParams.WRAP_CONTENT,HorizontalScrollView.LayoutParams.MATCH_PARENT));
scroller.setFillViewport(true);
RelativeLayout fullTopBar = new RelativeLayout(activity);
RelativeLayout.LayoutParams topBarParams = new RelativeLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.MATCH_PARENT);
topBarParams.addRule(RelativeLayout.ALIGN_PARENT_LEFT);
//layout.setGravity(Gravity.CENTER_VERTICAL);
// we have to do this walk backwards, actually
String topLevel = CommCareActivity.getTopLevelTitleName(activity);
LayoutInflater li = activity.getLayoutInflater();
int currentId = -1;
//We don't actually want this one to look the same
int newId = org.commcare.dalvik.R.id.component_title_breadcrumb_text + layout.getChildCount() + 1;
if(local != null) {
localIdPart = newId;
View titleBreadcrumb = li.inflate(org.commcare.dalvik.R.layout.component_title_uncrumb, fullTopBar, true);
TextView text = (TextView)titleBreadcrumb.findViewById(org.commcare.dalvik.R.id.component_title_breadcrumb_text);
text.setText(local);
//Is there a "random ID" or something we can use for this?
text.setId(newId);
//RelativeLayout.LayoutParams layout = (RelativeLayout.LayoutParams)peerView.getLayoutParams();
RelativeLayout.LayoutParams params = (RelativeLayout.LayoutParams)text.getLayoutParams();
params.addRule(RelativeLayout.ALIGN_PARENT_RIGHT);
params.addRule(RelativeLayout.CENTER_VERTICAL);
text.setGravity(Gravity.RIGHT | Gravity.CENTER_VERTICAL);
text.setLayoutParams(params);
topBarParams.addRule(RelativeLayout.LEFT_OF, newId);
}
fullTopBar.addView(scroller, topBarParams);
if(newId != -1) { currentId = newId;}
String[] stepTitles = new String[0];
try {
stepTitles = CommCareApplication._().getCurrentSession().getHeaderTitles();
Vector<String[]> v = CommCareApplication._().getCurrentSession().getFrame().getSteps();
//So we need to work our way backwards through each "step" we've taken, since our RelativeLayout
//displays the Z-Order b insertion (so items added later are always "on top" of items added earlier
for(int i = v.size() -1 ; i >= 0; i--){
String[] step = v.elementAt(i);
//Keep track of how many "steps" (or choices made by the user/platform) have happened.
final int currentStep = i;
//How many "steps" have already been made to get to the step we're making a view for?
final int currentStepSize = v.size();
OnClickListener stepBackListener = new OnClickListener() {
/*
* (non-Javadoc)
* @see android.view.View.OnClickListener#onClick(android.view.View)
*/
@Override
public void onClick(View arg0) {
int stepsToTake = currentStepSize - currentStep - 1;
try{
//Try to take stepsToTake steps "Back"
for(int i = 0 ; i < stepsToTake ; i++) {
CommCareApplication._().getCurrentSession().stepBack();
//We need to check the current size, because sometimes a step back will end up taking
//two (if a value is computed instead of selected)
int currentStepSize = CommCareApplication._().getCurrentSession().getFrame().getSteps().size();
//Take at _most_ currentSteps back, or stop when we've reached
//current step minus 1
if(currentStepSize == 0 || currentStepSize < currentStep) {
break;
}
}
activity.finish();
} catch(SessionUnavailableException sue) {
}
}
};
try {
//It the current step was selecting a nodeset value...
if(SessionFrame.STATE_DATUM_VAL.equals(step[0])) {
//Haaack. We should replace this with a generalizable "What do you refer to your detail by", but for now this is 90% of cases
if(step[1] != null && step[1].contains("case_id")) {
ACase foundCase = CommCareApplication._().getUserStorage(ACase.STORAGE_KEY, ACase.class).getRecordForValue(ACase.INDEX_CASE_ID, step[2]);
stepTitles[i] = foundCase.getName();
newId = addElementToTitle(li, layout, stepTitles[i], org.commcare.dalvik.R.layout.component_title_breadcrumb_case, currentId, stepBackListener);
if(newId != -1) { currentId = newId;}
continue;
}
}
} catch(Exception e) {
//TODO: Your error handling is bad and you should feel bad
}
newId = addElementToTitle(li, layout, stepTitles[i], org.commcare.dalvik.R.layout.component_title_breadcrumb, currentId, stepBackListener);
if(newId != -1) { currentId = newId;}
}
} catch(SessionUnavailableException sue) {
}
//Finally add the "top level" breadcrumb that represents the application's home.
addElementToTitle(li, layout, topLevel, org.commcare.dalvik.R.layout.component_title_breadcrumb, currentId, new OnClickListener() {
/*
* (non-Javadoc)
* @see android.view.View.OnClickListener#onClick(android.view.View)
*/
@Override
public void onClick(View arg0) {
try{
CommCareApplication._().getCurrentSession().clearAllState();
} catch(SessionUnavailableException sue) {
}
activity.finish();
}
});
//Add the app icon
TextView iconBearer = ((TextView)layout.getChildAt(layout.getChildCount() - 1));
iconBearer.setCompoundDrawablesWithIntrinsicBounds(org.commcare.dalvik.R.drawable.ab_icon,0,0,0);
iconBearer.setCompoundDrawablePadding(this.getResources().getDimensionPixelSize(org.commcare.dalvik.R.dimen.title_logo_pad));
//Add an "Anchor" view to the left hand side of the bar. The relative layout doesn't work unless
//there's a view that isn't relative to the other views. The anchor is explicitly relative to
//only the parent layout.
currentId = currentId + 2343241;
View anchor = new FrameLayout(activity);
anchor.setId(currentId);
// The Anchor should be as wide as the bleed off the screen. This is necessary, because otherwise the layout
// starts _off_ the screen to the left (due to the margin on the last item here). We'll shift the parent
// layout itself back over so it doesn't look awkward.
int buffer = Math.round(activity.getResources().getDimension(R.dimen.title_round_depth));
layout.addView(anchor, buffer, LayoutParams.MATCH_PARENT);
((RelativeLayout.LayoutParams)iconBearer.getLayoutParams()).addRule(RelativeLayout.RIGHT_OF, currentId);
return fullTopBar;
}
private int addElementToTitle(LayoutInflater inflater, RelativeLayout title, String element, int type, int peer, OnClickListener action) {
int newViewId = org.commcare.dalvik.R.id.component_title_breadcrumb_text + title.getChildCount() + 1;
if(element != null) {
View titleBreadcrumb = inflater.inflate(type, title, true);
TextView text = (TextView)titleBreadcrumb.findViewById(org.commcare.dalvik.R.id.component_title_breadcrumb_text);
if(action != null && isTopNavEnabled) {
text.setOnClickListener(action);
}
text.setText(element);
//Is there a "random ID" or something we can use for this?
text.setId(newViewId);
text.getLayoutParams().width = LayoutParams.WRAP_CONTENT;
if(peer != -1) {
View peerView = title.findViewById(peer);
RelativeLayout.LayoutParams layout = (RelativeLayout.LayoutParams)peerView.getLayoutParams();
layout.addRule(RelativeLayout.RIGHT_OF, newViewId);
}
return newViewId;
}
return -1;
}
}