package com.sparseboolean.ifexplorer.ui; import gem.kevin.innov.FilePathUrlManager; import gem.kevin.util.FileUtil; import gem.kevin.util.ui.ViewUtil; import gem.kevin.widget.DropAcceptable; import gem.kevin.widget.OnDropDelegate; import gem.kevin.widget.OpenDelegate; import java.util.ArrayList; import com.sparseboolean.ifexplorer.R; import com.sparseboolean.ifexplorer.ui.FilePathButton.FilePathButtonCallback; import android.content.Context; import android.graphics.PorterDuff; import android.graphics.Rect; import android.os.CountDownTimer; import android.util.AttributeSet; import android.util.Log; import android.view.DragEvent; import android.view.LayoutInflater; import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup; import android.view.animation.ScaleAnimation; import android.widget.ImageButton; import android.widget.LinearLayout; import android.widget.TextView; public class FilePathNavigator extends LinearLayout implements FilePathButtonCallback, DropAcceptable { public interface NavigationCallback { public void navigateToPath(String path); } private static final String TAG = "FilePathNavigator"; public static int MAX_BUTTON_NUM = 5; private ViewGroup mNavigatorContainer; private ViewGroup mStatusLayout; private TextView mStatusInfoText; private ImageButton mPreviousButton; private ImageButton mNextButton; private ImageButton mForwardButton; private ImageButton mBackwardButton; private String mCurrentPath; private ArrayList<String> mPathTree; private boolean mSimpleMode = false; private int mRootNodeIconResId = -1; private int mStartPosition = 0; private NavigationCallback mNavigationCallback; private OnDropDelegate mPathButtonOnDropDelegate; private OpenDelegate mPathButtonOpenDelegate; public static final int MODE_NAVIGATION = 1; public static final int MODE_INDICATION = 2; private int mMode = MODE_NAVIGATION; private int[] mLocationOnScreen = new int[2]; private int[] mDragLocationOnScreen = new int[2]; private boolean mChildCanGetDragEvent = true; private View mCurrentDropHandleView; private OnDropDelegate mOnDropDelegate; public FilePathNavigator(Context context, AttributeSet attrs) { this(context, attrs, 0); } public FilePathNavigator(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); LayoutInflater.from(context) .inflate(R.layout.file_path_navigator, this); mNavigatorContainer = (ViewGroup) findViewById(R.id.button_navigator); mStatusLayout = (ViewGroup) findViewById(R.id.pathStatusLayout); mStatusInfoText = (TextView) findViewById(R.id.pathStatusInfo); mPreviousButton = (ImageButton) findViewById(R.id.previous_button); mPreviousButton.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { shiftPreviousNode(); } }); mNextButton = (ImageButton) findViewById(R.id.next_button); mNextButton.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { shfitNextNode(); } }); mBackwardButton = (ImageButton) findViewById(R.id.navigation_back); mForwardButton = (ImageButton) findViewById(R.id.navigation_previous); } @Override public boolean canAcceptDrop() { // TODO Auto-generated method stub return false; } @Override public void onButtonClicked(FilePathButton button) { if (mNavigationCallback != null) { mNavigationCallback.navigateToPath(button.getFilePath()); } } @Override public boolean onDragEnded(DragEvent event) { // do nothing return true; } @Override public boolean onDragEntered(DragEvent event) { getLocationOnScreen(mLocationOnScreen); return true; } @Override public boolean onDragEvent(DragEvent event) { switch (event.getAction()) { case DragEvent.ACTION_DRAG_STARTED: Log.i(TAG, "FPN Drag Started."); return onDragStarted(event); case DragEvent.ACTION_DRAG_ENTERED: Log.i(TAG, "FPN Drag entered."); return onDragEntered(event); case DragEvent.ACTION_DRAG_LOCATION: Log.i(TAG, "FPN Drag location absolute, x: " + event.getX() + " y: " + event.getY()); return onDragMoveOn(event); case DragEvent.ACTION_DRAG_EXITED: Log.i(TAG, "FPN Drag exited."); return onDragExited(event); case DragEvent.ACTION_DROP: Log.i(TAG, "FPN Drag droped."); return onDrop(event); case DragEvent.ACTION_DRAG_ENDED: Log.i(TAG, "FPN Drag ended."); return onDragEnded(event); } return super.onDragEvent(event); } @Override public boolean onDragExited(DragEvent event) { // do nothing return true; } @Override public boolean onDragMoveOn(DragEvent event) { Log.i(TAG, "onDragMoveOn"); if (mChildCanGetDragEvent) { // Let navigators handle the drag event if they can. return true; } else { findAppropriateDragMoveHandle(event); return true; } } @Override public boolean onDragStarted(DragEvent event) { // A drag started event indicates a new drag operation happens, // when all the created visible navigators should be able to get // the following drag events markChildCanGetDragEvent(true); return true; } @Override public boolean onDrop(DragEvent event) { if (!mChildCanGetDragEvent) { if (findAppropriateDropHandle(event)) { Log.i(TAG, "mCurrentDropHandleView is handling drop."); return true; } } if (mOnDropDelegate != null) { Object data = mOnDropDelegate.generateDropData(); boolean result = true; if (mOnDropDelegate.checkDropDataAcceptable(data, this)) { result = mOnDropDelegate.handleDrop(data, this); if (result) { mOnDropDelegate.onDropSuccess(this); } else { mOnDropDelegate.onDropFailed(this); } } else { if (mOnDropDelegate.shouldDispathUnacceptableDropToParent(this)) { result = super.onDragEvent(event); } else { // Because a drop could be obscure when it happens on a GridView // so we always need to notify the result if it is denied. mOnDropDelegate.notifyDropDataDenied(data, this); mOnDropDelegate.onDropFailed(this); } } return result; } return false; } @Override public void onHover() { // TODO Auto-generated method stub } @Override public void markChildCanGetDragEvent(boolean childCanGetDragEvent) { mChildCanGetDragEvent = childCanGetDragEvent; } public void setNavigationCallback(NavigationCallback callback) { mNavigationCallback = callback; } public void setOnDropDelegate(OnDropDelegate delegate) { mOnDropDelegate = delegate; } public void setPathButtonOnDropDelegate(OnDropDelegate delegate) { mPathButtonOnDropDelegate = delegate; } public void setPathButtonOpenDelegate(OpenDelegate delegate) { mPathButtonOpenDelegate = delegate; } public void setRootNodeIconResId(int resId) { mRootNodeIconResId = resId; } public void setStatusInfo(String statusInfo) { mStatusInfoText.setText(statusInfo); } public void shfitNextNode() { if (mPathTree != null) { int size = mPathTree.size(); if (mStartPosition < size - 1) { mStartPosition++; updatePathNodes(mCurrentPath, false); } } } public void shiftPreviousNode() { if (mStartPosition > 0) { mStartPosition--; updatePathNodes(mCurrentPath, false); } } public void showInSimpleMode(boolean simpeMode) { mSimpleMode = simpeMode; if (mSimpleMode) { mBackwardButton.setVisibility(View.GONE); mForwardButton.setVisibility(View.GONE); } } public void switchToIndicateMode() { mMode = MODE_INDICATION; mNavigatorContainer.setVisibility(View.GONE); mStatusLayout.setVisibility(View.VISIBLE); updateButtonVisibility(); } public void switchToNavigateMode() { mMode = MODE_NAVIGATION; mNavigatorContainer.setVisibility(View.VISIBLE); mStatusLayout.setVisibility(View.GONE); updateButtonVisibility(); } public void updatePathNodes(String path) { updatePathNodes(path, true); } private boolean findAppropriateDragMoveHandle(DragEvent event) { Log.i(TAG, "Loc of parent: x:" + mLocationOnScreen[0] + " y:" + mLocationOnScreen[1]); mDragLocationOnScreen[0] = (int) (mLocationOnScreen[0] + event.getX()); mDragLocationOnScreen[1] = (int) (mLocationOnScreen[1] + event.getY()); Log.i(TAG, "FPN Drag location relative, x: " + mDragLocationOnScreen[0] + " y: " + mDragLocationOnScreen[1]); boolean moveOnHandleView; if (mCurrentDropHandleView != null) { Log.i(TAG, "There is drop handle view, test it!"); // Test if still move on the drop handle view moveOnHandleView = ViewUtil.isViewContained(mCurrentDropHandleView, mDragLocationOnScreen[0], mDragLocationOnScreen[1]); if (!moveOnHandleView) { // Exit ((DropAcceptable) mCurrentDropHandleView).onDragExited(event); mCurrentDropHandleView = null; return false; } else { return true; } } else { // Test if there is a new view become drop handle boolean foundNewDropHandle = false; for (int i = 0; i < mNavigatorContainer.getChildCount(); i++) { View view = mNavigatorContainer.getChildAt(i); moveOnHandleView = ViewUtil.isViewContained(view, mDragLocationOnScreen[0], mDragLocationOnScreen[1]); if (moveOnHandleView) { Log.i(TAG, "Drag enter child view: " + i + " class: " + view.getClass().getSimpleName() + " l: " + view.getLeft() + " width: " + view.getWidth() + " t: " + view.getTop() + " height: " + view.getHeight() + " r: " + view.getRight() + " b: " + view.getBottom()); if (view instanceof DropAcceptable) { mCurrentDropHandleView = view; foundNewDropHandle = true; ((DropAcceptable) mCurrentDropHandleView) .onDragEntered(event); } if (foundNewDropHandle) { Log.i(TAG, "Find new drop handle view!"); return true; } else { Log.i(TAG, "Not find new drop handle view yet!"); } } } return false; } } private boolean findAppropriateDropHandle(DragEvent event) { Log.i(TAG, "Loc of parent: x:" + mLocationOnScreen[0] + " y:" + mLocationOnScreen[1]); mDragLocationOnScreen[0] = (int) (mLocationOnScreen[0] + event.getX()); mDragLocationOnScreen[1] = (int) (mLocationOnScreen[1] + event.getY()); Log.i(TAG, "FPN Drag location relative, x: " + mDragLocationOnScreen[0] + " y: " + mDragLocationOnScreen[1]); if (mCurrentDropHandleView != null) { ((DropAcceptable) mCurrentDropHandleView).onDrop(event); mCurrentDropHandleView = null; return true; } else { return false; } } private void updateButtonVisibility() { switch (mMode) { case MODE_NAVIGATION: if (mPathTree != null) { int size = mPathTree.size(); Log.i(TAG, "updateButtonVisibility - path tree size: " + size + " start position: " + mStartPosition); mPreviousButton.setVisibility(mStartPosition > 0 ? View.VISIBLE : View.INVISIBLE); mNextButton .setVisibility((mStartPosition + MAX_BUTTON_NUM < size) ? View.VISIBLE : View.INVISIBLE); } break; case MODE_INDICATION: mPreviousButton.setVisibility(View.GONE); mNextButton.setVisibility(View.GONE); break; } } private void updatePathNodes(String path, boolean newPath) { Log.i(TAG, "updatePathNodes - " + " path:" + path + " newPath:" + newPath); if (FilePathUrlManager.isExistingDirPath(path)) { mCurrentPath = path; mPathTree = FileUtil.getFilePathTree(path); } else { if (FilePathUrlManager.isSearchUrl(path)) { String dirPath = FilePathUrlManager.getDir(path); mCurrentPath = dirPath; mPathTree = FileUtil.getFilePathTree(dirPath); mPathTree.add(path); } } if (mPathTree != null) { // Remove old buttons mNavigatorContainer.removeAllViews(); // New inflated navigators can't get drag events from last drag operation, // because they haven't been created when THE drag started. markChildCanGetDragEvent(false); LinearLayout.LayoutParams params = new LinearLayout.LayoutParams( LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT); params.setMargins(2, 8, 4, 8); int size = mPathTree.size(); int startIndex; if (newPath) { startIndex = (size > MAX_BUTTON_NUM) ? size - MAX_BUTTON_NUM : 0; mStartPosition = startIndex; } else { startIndex = mStartPosition; } // Update new buttons for (int index = startIndex; (index < startIndex + MAX_BUTTON_NUM && index < size); index++) { String nodePath = mPathTree.get(index); DropableFilePathButton filePathButton = new DropableFilePathButton( getContext(), nodePath, this); if (mPathButtonOnDropDelegate != null) { filePathButton.setOnDropDelegate(mPathButtonOnDropDelegate); } if (mPathButtonOpenDelegate != null) { filePathButton.setOpenDelegate(mPathButtonOpenDelegate); } String label = FileUtil.getFileName(nodePath); if (label != null && label.length() > 20) { label = label.substring(0, 20) + "..."; } if (label != null) { if (label.equals("/")) { filePathButton.setLabel(""); if (mRootNodeIconResId > 0) { filePathButton.setIcon(mRootNodeIconResId); } else { filePathButton.setIcon(R.drawable.home_dir); } } else { if (label.equals(FilePathUrlManager.URL_PATH_APK)) { filePathButton.setLabel(""); filePathButton.setIcon(R.drawable.category_apk); } else if (label .equals(FilePathUrlManager.URL_PATH_MOVIES)) { filePathButton.setLabel(""); filePathButton.setIcon(R.drawable.category_movie); } else if (label .equals(FilePathUrlManager.URL_PATH_MUSIC)) { filePathButton.setLabel(""); filePathButton.setIcon(R.drawable.category_music); } else if (label .equals(FilePathUrlManager.URL_PATH_IMAGE)) { filePathButton.setLabel(""); filePathButton.setIcon(R.drawable.category_picture); } else { filePathButton.setLabel(label); } } } mNavigatorContainer.addView(filePathButton, params); } updateButtonVisibility(); if (FilePathUrlManager.isExistingDirPath(path)) { mStatusInfoText.setText(mPathTree.get(size - 1)); } } } }