package com.dnielfe.manager.fragments; import android.app.Activity; import android.app.DialogFragment; import android.app.FragmentManager; import android.content.Intent; import android.os.Bundle; import android.os.FileObserver; import android.os.Handler; import android.util.Log; import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; import android.widget.AbsListView; import android.widget.AdapterView; import android.widget.ListView; import android.widget.PopupMenu; import android.widget.Toast; import com.dnielfe.manager.AbstractBrowserActivity; import com.dnielfe.manager.R; import com.dnielfe.manager.SearchActivity; import com.dnielfe.manager.adapters.BrowserListAdapter; import com.dnielfe.manager.controller.ActionModeController; import com.dnielfe.manager.dialogs.CreateFileDialog; import com.dnielfe.manager.dialogs.CreateFolderDialog; import com.dnielfe.manager.dialogs.DirectoryInfoDialog; import com.dnielfe.manager.dialogs.UnpackDialog; import com.dnielfe.manager.fileobserver.FileObserverCache; import com.dnielfe.manager.fileobserver.MultiFileObserver; import com.dnielfe.manager.settings.Settings; import com.dnielfe.manager.tasks.PasteTaskExecutor; import com.dnielfe.manager.utils.ClipBoard; import com.dnielfe.manager.utils.SimpleUtils; import com.melnykov.fab.FloatingActionButton; import java.io.File; import java.lang.ref.WeakReference; public abstract class AbstractBrowserFragment extends UserVisibleHintFragment implements MultiFileObserver.OnEventListener, PopupMenu.OnMenuItemClickListener { private Activity mActivity; private FragmentManager fm; private MultiFileObserver mObserver; private FileObserverCache mObserverCache; private Runnable mLastRunnable; private static Handler sHandler; private onUpdatePathListener mUpdatePathListener; private ActionModeController mActionController; private BrowserListAdapter mListAdapter; public String mCurrentPath; private AbsListView mListView; private boolean mUseBackKey = true; public interface onUpdatePathListener { void onUpdatePath(String path); } @Override public void onCreate(Bundle state) { setRetainInstance(true); setHasOptionsMenu(true); super.onCreate(state); } @Override public void onActivityCreated(Bundle state) { super.onActivityCreated(state); mActivity = getActivity(); Intent intent = mActivity.getIntent(); fm = getFragmentManager(); mObserverCache = FileObserverCache.getInstance(); mUpdatePathListener = (onUpdatePathListener) mActivity; mActionController = new ActionModeController(mActivity); mActionController.setListView(mListView); if (sHandler == null) { sHandler = new Handler(mActivity.getMainLooper()); } initDirectory(state, intent); } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View rootView = inflater.inflate(R.layout.fragment_browser, container, false); initList(inflater, rootView); initFab(rootView); return rootView; } @Override protected void onVisible() { final AbstractBrowserActivity activity = (AbstractBrowserActivity) getActivity(); // check for root Settings.rootAccess(); navigateTo(mCurrentPath); // this is only needed if you select "move/copy files" in SearchActivity and come back if (!ClipBoard.isEmpty()) activity.supportInvalidateOptionsMenu(); } @Override protected void onInvisible() { mObserver.stopWatching(); if (mActionController != null) { mActionController.finishActionMode(); } } @Override public void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); outState.putString("location", mCurrentPath); } public void initList(LayoutInflater inflater, View rootView) { final AbstractBrowserActivity context = (AbstractBrowserActivity) getActivity(); mListAdapter = new BrowserListAdapter(context, inflater); mListView = (ListView) rootView.findViewById(android.R.id.list); mListView.setEmptyView(rootView.findViewById(android.R.id.empty)); mListView.setAdapter(mListAdapter); mListView.setChoiceMode(AbsListView.CHOICE_MODE_MULTIPLE_MODAL); mListView.setOnItemClickListener(new AdapterView.OnItemClickListener() { @Override public void onItemClick(AdapterView<?> parent, View view, int position, long id) { final File file = new File((mListView.getAdapter() .getItem(position)).toString()); if (file.isDirectory()) { navigateTo(file.getAbsolutePath()); // go to the top of the ListView mListView.setSelection(0); } else { listItemAction(file); } } }); } protected void initFab(View rootView) { FloatingActionButton fab = (FloatingActionButton) rootView.findViewById(R.id.fabbutton); fab.attachToListView(mListView); fab.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { showMenu(view); } }); } private void showMenu(View v) { PopupMenu popup = new PopupMenu(mActivity, v); // This activity implements OnMenuItemClickListener popup.setOnMenuItemClickListener(this); popup.inflate(R.menu.fab_menu); popup.show(); } @Override public boolean onMenuItemClick(MenuItem item) { switch (item.getItemId()) { case R.id.createfile: final DialogFragment dialog1 = new CreateFileDialog(); dialog1.show(fm, AbstractBrowserActivity.TAG_DIALOG); return true; case R.id.createfolder: final DialogFragment dialog2 = new CreateFolderDialog(); dialog2.show(fm, AbstractBrowserActivity.TAG_DIALOG); return true; default: return false; } } // this will be overwritten in picker fragment public void listItemAction(File file) { if (SimpleUtils.isSupportedArchive(file)) { final DialogFragment dialog = UnpackDialog.instantiate(file); dialog.show(fm, AbstractBrowserActivity.TAG_DIALOG); } else { SimpleUtils.openFile(mActivity, file); } } public void navigateTo(String path) { mCurrentPath = path; if (!mUseBackKey) mUseBackKey = true; if (mObserver != null) { mObserver.stopWatching(); mObserver.removeOnEventListener(this); } mListAdapter.addFiles(path); mObserver = mObserverCache.getOrCreate(path); // add listener for FileObserver and start watching if (mObserver.listeners.isEmpty()) mObserver.addOnEventListener(this); mObserver.startWatching(); mUpdatePathListener.onUpdatePath(path); } @Override public void onEvent(int event, String path) { // this will automatically update the directory when an action like this // will be performed switch (event & FileObserver.ALL_EVENTS) { case FileObserver.CREATE: case FileObserver.CLOSE_WRITE: case FileObserver.MOVE_SELF: case FileObserver.MOVED_TO: case FileObserver.MOVED_FROM: case FileObserver.ATTRIB: case FileObserver.DELETE: case FileObserver.DELETE_SELF: sHandler.removeCallbacks(mLastRunnable); sHandler.post(mLastRunnable = new NavigateRunnable((AbstractBrowserActivity) getActivity(), path)); break; } } @Override public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { inflater.inflate(R.menu.main, menu); if (AbstractBrowserActivity.isDrawerOpen()) { menu.findItem(R.id.paste).setVisible(false); menu.findItem(R.id.folderinfo).setVisible(false); menu.findItem(R.id.search).setVisible(false); } else { menu.findItem(R.id.paste).setVisible(!ClipBoard.isEmpty()); } } @Override public boolean onOptionsItemSelected(MenuItem item) { final AbstractBrowserActivity activity = (AbstractBrowserActivity) getActivity(); final FragmentManager fm = getFragmentManager(); switch (item.getItemId()) { case R.id.folderinfo: final DialogFragment dirInfo = new DirectoryInfoDialog(); dirInfo.show(fm, AbstractBrowserActivity.TAG_DIALOG); return true; case R.id.search: Intent sintent = new Intent(activity, SearchActivity.class); startActivity(sintent); return true; case R.id.paste: final PasteTaskExecutor ptc = new PasteTaskExecutor(activity, mCurrentPath); ptc.start(); return true; default: return super.onOptionsItemSelected(item); } } public void onNavigate(String path) { // navigate to path when Navigation button is clicked if (mActionController.isActionMode()) { mActionController.finishActionMode(); } navigateTo(path); // go to the top of the ListView mListView.setSelection(0); } private void initDirectory(Bundle savedInstanceState, Intent intent) { String defaultdir; if (savedInstanceState != null) { // get directory when you rotate your phone defaultdir = savedInstanceState.getString("location"); } else { try { File dir = new File(intent.getStringExtra(AbstractBrowserActivity.EXTRA_SHORTCUT)); if (dir.exists() && dir.isDirectory()) { defaultdir = dir.getAbsolutePath(); } else { if (dir.exists() && dir.isFile()) listItemAction(dir); // you need to call it when shortcut-dir not exists defaultdir = Settings.getDefaultDir(); } } catch (Exception e) { defaultdir = Settings.getDefaultDir(); } } File dir = new File(defaultdir); if (dir.exists() && dir.isDirectory()) navigateTo(dir.getAbsolutePath()); } private static final class NavigateRunnable implements Runnable { private final WeakReference<AbstractBrowserActivity> abActivityWeakRef; private final String target; NavigateRunnable(final AbstractBrowserActivity abActivity, final String path) { this.abActivityWeakRef = new WeakReference<>(abActivity); this.target = path; } @Override public void run() { // TODO: Ensure WeakReference approach is free of both bugs and leaks //BrowserTabsAdapter.getCurrentBrowserFragment().navigateTo(target); AbstractBrowserActivity abActivity = abActivityWeakRef.get(); if (abActivity != null) { abActivity.getCurrentBrowserFragment().navigateTo(target); } else { Log.w(this.getClass().getName(), "NavigateRunnable: activity weakref returned null, can't navigate"); } } } public void onBookmarkClick(File file) { if (!file.exists()) { Toast.makeText(mActivity, getString(R.string.cantopenfile), Toast.LENGTH_SHORT).show(); return; } if (file.isDirectory()) { navigateTo(file.getAbsolutePath()); // go to the top of the ListView mListView.setSelection(0); } else { listItemAction(file); } } public boolean onBackPressed() { if (mUseBackKey && mActionController.isActionMode()) { mActionController.finishActionMode(); return true; } else if (mUseBackKey && !mCurrentPath.equals("/")) { File file = new File(mCurrentPath); navigateTo(file.getParent()); // get position of the previous folder in ListView mListView.setSelection(mListAdapter.getPosition(file.getPath())); return true; } else if (mUseBackKey && mCurrentPath.equals("/")) { Toast.makeText(mActivity, getString(R.string.pressbackagaintoquit), Toast.LENGTH_SHORT).show(); mUseBackKey = false; return false; } else if (!mUseBackKey && mCurrentPath.equals("/")) { mActivity.finish(); return false; } return true; } }