package org.openintents.filemanager.view; import java.io.File; import java.util.HashMap; import java.util.Map; import org.openintents.filemanager.R; import org.openintents.filemanager.view.PathBar.Mode; import android.content.Context; import android.content.res.TypedArray; import android.graphics.drawable.Drawable; import android.os.Environment; import android.util.AttributeSet; import android.util.TypedValue; import android.view.View; import android.view.ViewGroup; import android.view.View.OnLongClickListener; import android.widget.Button; import android.widget.ImageButton; import android.widget.LinearLayout; /** * This class handles the displaying of children in {@link Mode.STANDARD_INPUT}, including choosing which children to display, how, and where. It automatically uses the {@link PathBar#mCurrentDirectory} field. <b>Note: </b> Never use this with * a width of WRAP_CONTENT. */ class PathButtonLayout extends LinearLayout implements OnLongClickListener { private PathBar mPathBar = null; /** <absolute path, R.drawable id of image to use> */ public static Map<String, Integer> mPathDrawables = new HashMap<>(); public PathButtonLayout(Context context) { super(context); init(); } public PathButtonLayout(Context context, AttributeSet attrs) { super(context, attrs); init(); } private void init() { this.setOrientation(LinearLayout.HORIZONTAL); this.setOnLongClickListener(this); mPathDrawables.put(Environment.getExternalStorageDirectory().getAbsolutePath(), R.drawable.ic_navbar_sdcard); mPathDrawables.put("/sdcard", R.drawable.ic_navbar_sdcard); mPathDrawables.put("/mnt/sdcard", R.drawable.ic_navbar_sdcard); mPathDrawables.put("/mnt/sdcard-ext", R.drawable.ic_navbar_sdcard); mPathDrawables.put("/mnt/sdcard0", R.drawable.ic_navbar_sdcard); mPathDrawables.put("/mnt/sdcard1", R.drawable.ic_navbar_sdcard); mPathDrawables.put("/mnt/sdcard2", R.drawable.ic_navbar_sdcard); mPathDrawables.put("/storage/sdcard0", R.drawable.ic_navbar_sdcard); mPathDrawables.put("/storage/sdcard1", R.drawable.ic_navbar_sdcard); mPathDrawables.put("/storage/sdcard2", R.drawable.ic_navbar_sdcard); mPathDrawables.put("/", R.drawable.ic_navbar_home); } public void setNavigationBar(PathBar pathbar) { mPathBar = pathbar; } /** * Call to properly refresh this {@link PathButtonLayout}'s contents based on the fPath parameter. */ public void refresh(File fPath) { // Reload buttons. this.removeAllViews(); addPathButtons(fPath); // Redraw. invalidate(); } /** * Adds the proper buttons according to the fPath parameter. */ private void addPathButtons(File fPath) { StringBuilder cPath = new StringBuilder(); char cChar; String path = fPath.getAbsolutePath(); for (int i = 0; i < path.length(); i++) { cChar = path.charAt(i); cPath.append(cChar); if (cChar == '/' || i == path.length() - 1) { // if folder name ended, or path string ended but not if we 're on root // add a button this.addView(PathButtonFactory.newButton(cPath.toString(), mPathBar)); } } } /** * Provides a modified implementation of the layoutHorizontal() method of LinearLayout. Removes all children that don't fully fit in this {@link PathButtonLayout}. */ @Override protected void onLayout(boolean changed, int l, int t, int r, int b) { final boolean isLayoutRtl = false; final int paddingTop = getPaddingTop(); if (this.getChildCount() > 0) keepFittingChildren(); int childTop; int childLeft; // Where bottom of child should go final int height = getBottom() - getTop(); // Space available for child int childSpace = height - paddingTop - getPaddingBottom(); final int count = getChildCount(); childLeft = getPaddingLeft(); int start = 0; int dir = 1; // In case of RTL, start drawing from the last child. if (isLayoutRtl) { start = count - 1; dir = -1; } for (int i = 0; i < count; i++) { int childIndex = start + dir * i; final View child = getChildAt(childIndex); if (child == null) { childLeft += 0; } else if (child.getVisibility() != GONE) { final int childWidth = child.getMeasuredWidth(); final int childHeight = child.getMeasuredHeight(); final LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) child .getLayoutParams(); childTop = paddingTop + ((childSpace - childHeight) / 2) + lp.topMargin - lp.bottomMargin; childLeft += lp.leftMargin; setChildFrame(child, childLeft, // originally childLeft + getLocationOffset(child) childTop, childWidth, childHeight); childLeft += childWidth + lp.rightMargin; // originally childLeft += childWidth + lp.rightMargin + getNextLocationOffset(child); i += 0; // originally getChildrenSkipCount(child, childIndex); } } } @Override protected void measureChildWithMargins(View child, int parentWidthMeasureSpec, int widthUsed, int parentHeightMeasureSpec, int heightUsed) { final MarginLayoutParams lp = (MarginLayoutParams) child .getLayoutParams(); final int childWidthMeasureSpec = getChildMeasureSpec( parentWidthMeasureSpec, getPaddingLeft() + getPaddingRight() + lp.leftMargin + lp.rightMargin, lp.width); final int childHeightMeasureSpec = getChildMeasureSpec( parentHeightMeasureSpec, getPaddingTop() + getPaddingBottom() + lp.topMargin + lp.bottomMargin, lp.height); child.measure(childWidthMeasureSpec, childHeightMeasureSpec); } private void setChildFrame(View child, int left, int top, int width, int height) { child.layout(left, top, left + width, top + height); } /** * Checks this {@link ViewGroup}'s children and keeps the ones that fit inside it. */ private void keepFittingChildren() { View child; int childrenToDraw = 0; int sumWidth = 0; int index = this.getChildCount() - 1; do { child = this.getChildAt(index); sumWidth += child.getMeasuredWidth(); childrenToDraw++; index--; } while (sumWidth <= this.getMeasuredWidth() && index >= 0); if (sumWidth > this.getMeasuredWidth()) { // if the view width has been passed // keep one child less childrenToDraw--; } int i; int childrenCount = this.getChildCount(); for (i = 0; i < childrenCount - childrenToDraw; i++) { this.removeViewAt(0); } } /** * Add an icon to be shown instead of a the directory name. * * @param path * The path on which to display the icon. * @param drawableResourceId * The icon' resource id. */ public void addPathDrawable(String path, int drawableResourceId) { mPathDrawables.put(path, drawableResourceId); } public static Map<String, Integer> getPathDrawables() { return mPathDrawables; } private static class PathButtonFactory { /** * Creates a Button or ImageButton according to the path. e.g. {@code if(file.getAbsolutePath() == '/')}, it should return an ImageButton with the home drawable on it. * * @param file The directory this button will represent. * @param navbar The {@link PathBar} which will contain the created buttons. * @return An {@link ImageButton} or a {@link Button}. */ private static View newButton(File file, final PathBar navbar) { View btn; if (mPathDrawables.containsKey(file.getAbsolutePath())) { btn = new ImageButton(navbar.getContext()); ((ImageButton) btn).setImageResource(mPathDrawables.get(file.getAbsolutePath())); } else { btn = new Button(navbar.getContext()); ((Button) btn).setText(file.getName()); ((Button) btn).setMaxLines(1); ((Button) btn).setTextColor(navbar.getResources().getColor(R.color.navbar_details)); ((Button) btn).setTextSize(TypedValue.COMPLEX_UNIT_SP, 18); } android.widget.LinearLayout.LayoutParams params = new android.widget.LinearLayout.LayoutParams( LayoutParams.WRAP_CONTENT, LayoutParams.MATCH_PARENT); params.rightMargin = (int) TypedValue.applyDimension( TypedValue.COMPLEX_UNIT_DIP, 4, navbar.getResources() .getDisplayMetrics()); btn.setLayoutParams(params); btn.setTag(file); btn.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { navbar.cd((File) v.getTag()); } }); btn.setOnLongClickListener(navbar.getPathButtonLayout()); btn.setBackgroundDrawable(navbar.getItemBackground()); // We have to set this after adding the background as it'll cancel the padding out. if(btn instanceof Button) { int sidePadding = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 8, navbar.getContext().getResources().getDisplayMetrics()); btn.setPadding(sidePadding, btn.getPaddingTop(), sidePadding, btn.getPaddingBottom()); } return btn; } /** * @see {@link #newButton(File)} */ private static View newButton(String path, PathBar navbar) { return newButton(new File(path), navbar); } } @Override public boolean onLongClick(View v) { mPathBar.switchToManualInput(); return true; } }