/**
* This file is part of FileSpace for Android, an app for managing your server (files, talks...).
* <p/>
* Copyright (c) 2014-2015 FileSpace for Android contributors (http://mercandalli.com)
* <p/>
* LICENSE:
* <p/>
* FileSpace for Android is free software: you can redistribute it and/or modify it under the terms of the GNU General
* Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any
* later version.
* <p/>
* FileSpace for Android is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the
* implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
* details.
*
* @author Jonathan Mercandalli
* @license http://www.gnu.org/licenses/gpl.html
* @copyright 2014-2015 FileSpace for Android contributors (http://mercandalli.com)
*/
package com.mercandalli.android.apps.files.file.local;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.os.Build;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
import android.os.Parcelable;
import android.support.annotation.DrawableRes;
import android.support.annotation.IntRange;
import android.support.annotation.NonNull;
import android.support.design.widget.FloatingActionButton;
import android.support.v4.widget.SwipeRefreshLayout;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.GridLayoutManager;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ProgressBar;
import android.widget.TextView;
import com.mercandalli.android.apps.files.R;
import com.mercandalli.android.apps.files.common.animation.ScaleAnimationAdapter;
import com.mercandalli.android.apps.files.common.fragment.BackFragment;
import com.mercandalli.android.apps.files.common.listener.ResultCallback;
import com.mercandalli.android.apps.files.file.FileManager;
import com.mercandalli.android.apps.files.file.FileModel;
import com.mercandalli.android.apps.files.file.FileModelAdapter;
import com.mercandalli.android.apps.files.file.FileModelListener;
import com.mercandalli.android.apps.files.file.local.fab.FileLocalFabManager;
import com.mercandalli.android.apps.files.intent.IntentManager;
import com.mercandalli.android.apps.files.main.Config;
import com.mercandalli.android.apps.files.settings.SettingsManager;
import com.mercandalli.android.library.base.dialog.DialogUtils;
import com.mercandalli.android.library.base.event.EventManager;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
import static com.mercandalli.android.apps.files.file.FileUtils.createFile;
/**
* A {@link BackFragment} used to buildDisplay the local {@link FileModel} provide by the
* {@link FileLocalApi}.
*/
public class FileLocalFragment extends BackFragment implements
FileLocalPagerFragment.ListController,
FileModelAdapter.OnFileClickListener,
FileModelAdapter.OnFileLongClickListener,
FileModelListener,
FileLocalPagerFragment.HomeIconVisible,
FileLocalOverflowActions.FileLocalActionCallback,
SwipeRefreshLayout.OnRefreshListener,
FileLocalPagerFragment.ScrollTop,
FileLocalFabManager.FabController {
/**
* A key for the view pager position.
*/
@NonNull
private static final String ARG_POSITION_IN_VIEW_PAGER = "FileLocalFragment.Args.ARG_POSITION_IN_VIEW_PAGER";
@NonNull
public static FileLocalFragment newInstance(final int positionInViewPager) {
final FileLocalFragment fileLocalFragment = new FileLocalFragment();
final Bundle args = new Bundle();
args.putInt(ARG_POSITION_IN_VIEW_PAGER, positionInViewPager);
fileLocalFragment.setArguments(args);
return fileLocalFragment;
}
private RecyclerView mRecyclerView;
@NonNull
private final List<FileModel> mFilesList = new ArrayList<>();
private ProgressBar mProgressBar;
private FileModel mCurrentDirectory = createInitialDirectory();
private TextView mMessageTextView;
private SwipeRefreshLayout mSwipeRefreshLayout;
@NonNull
private final List<FileModel> mFilesToCutList = new ArrayList<>();
@NonNull
private final List<FileModel> mFilesToCopyList = new ArrayList<>();
/* Managers */
private FileManager mFileManager;
private FileLocalFabManager mFileLocalFabManager;
private FileModelAdapter mFileModelAdapter;
private FileLocalOverflowActions mFileLocalOverflowActions;
private ScaleAnimationAdapter mScaleAnimationAdapter;
private boolean mIsFabAnimating = false;
@NonNull
private final Handler mHandler = new Handler();
@NonNull
private final Runnable mRunnable = new Runnable() {
@Override
public void run() {
mIsFabAnimating = false;
}
};
private boolean mIsRefreshing = false;
private int mPositionInViewPager;
private boolean mIsFabHidden;
/**
* Default Constructor.
* <p/>
* <p/>
* lint [ValidFragment]
* http://developer.android.com/reference/android/app/Fragment.html#Fragment()
* Every fragment must have an empty constructor, so it can be instantiated when restoring its activity's state.
*/
public FileLocalFragment() {
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mFileManager = FileManager.getInstance(getContext());
mFileLocalFabManager = FileLocalFabManager.getInstance();
final Bundle args = getArguments();
if (!args.containsKey(ARG_POSITION_IN_VIEW_PAGER)) {
throw new IllegalStateException("Missing args. Please use newInstance()");
}
mPositionInViewPager = args.getInt(ARG_POSITION_IN_VIEW_PAGER);
mFileLocalFabManager.addFabController(mPositionInViewPager, this);
}
@Override
public void onDestroy() {
mFileLocalFabManager.removeFabController(mPositionInViewPager);
super.onDestroy();
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
final View rootView = inflater.inflate(R.layout.fragment_file_files, container, false);
findViews(rootView);
initViews();
refreshCurrentList();
mFileLocalOverflowActions = new FileLocalOverflowActions(getContext(), this);
final Context context = getContext();
if (context instanceof AppCompatActivity) {
((AppCompatActivity) context).invalidateOptionsMenu();
}
mFileLocalFabManager.updateFabButtons();
return rootView;
}
@Override
public boolean back() {
if (hasItemSelected()) {
deselectAll();
return true;
} else if (!mCurrentDirectory.getFile().getPath().equals(initialPath())) {
if (mCurrentDirectory.getFile().getParent() != null) {
mCurrentDirectory = new FileModel.FileModelBuilder()
.file(new File(mCurrentDirectory.getFile().getParentFile().getPath()))
.build();
refreshCurrentList();
return true;
}
} else if (!mFilesToCopyList.isEmpty() || !mFilesToCutList.isEmpty()) {
if (!mFilesToCopyList.isEmpty()) {
mFilesToCopyList.clear();
}
if (!mFilesToCutList.isEmpty()) {
mFilesToCutList.clear();
}
refreshFab();
return true;
}
return false;
}
/**
* {@inheritDoc}
*/
@Override
public void onFabClick(
final @IntRange(from = 0, to = FileLocalFabManager.NUMBER_MAX_OF_FAB - 1) int fabPosition,
final @NonNull FloatingActionButton floatingActionButton) {
final Context context = getContext();
switch (fabPosition) {
case 0:
if ((mFilesToCopyList.size() != 0) || (mFilesToCutList.size() != 0)) {
for (final FileModel file : mFilesToCopyList) {
mFileManager.copyLocalFile((Activity) context, file, mCurrentDirectory.getFile().getAbsolutePath() + File.separator);
}
mFilesToCopyList.clear();
for (final FileModel file : mFilesToCutList) {
mFileManager.renameLocalByPath(file, mCurrentDirectory.getFile().getAbsolutePath() + File.separator + file.getFullName());
}
mFilesToCutList.clear();
refreshCurrentList();
} else {
final AlertDialog.Builder menuAlert = new AlertDialog.Builder(context);
final String[] menuList = {context.getString(R.string.file_model_local_new_folder_file)};
menuAlert.setTitle(context.getString(R.string.file_model_local_new_title));
menuAlert.setItems(menuList,
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int item) {
switch (item) {
case 0:
DialogUtils.prompt(
context,
context.getString(R.string.file_model_local_new_folder_file),
context.getString(R.string.file_model_local_new_folder_file_description),
getString(R.string.ok),
new DialogUtils.OnDialogUtilsStringListener() {
@Override
public void onDialogUtilsStringCalledBack(String text) {
createFile(mCurrentDirectory.getFile().getPath() + File.separator, text);
refreshCurrentList();
}
}, getString(android.R.string.cancel), null, null, context.getString(R.string.name));
break;
}
}
});
menuAlert.create().show();
}
refreshFab();
break;
case 1:
if (mCurrentDirectory.getFile() != null &&
mCurrentDirectory.getFile().getParent() != null) {
mCurrentDirectory = new FileModel.FileModelBuilder()
.file(new File(mCurrentDirectory.getFile().getParentFile().getPath()))
.build();
refreshCurrentList();
}
refreshFab();
break;
}
}
/**
* {@inheritDoc}
*/
@Override
public boolean isFabVisible(
final @IntRange(from = 0, to = FileLocalFabManager.NUMBER_MAX_OF_FAB - 1) int fabPosition) {
if (mIsFabHidden) {
return false;
}
switch (fabPosition) {
case 0:
return true;
case 1:
return mCurrentDirectory != null &&
mCurrentDirectory.getFile().getParent() != null;
}
return false;
}
@DrawableRes
@Override
public int getFabImageResource(
final @IntRange(from = 0, to = FileLocalFabManager.NUMBER_MAX_OF_FAB - 1) int fabPosition) {
switch (fabPosition) {
case 0:
if (!mFilesToCopyList.isEmpty()) {
return R.drawable.ic_menu_paste_holo_dark;
} else if (!mFilesToCutList.isEmpty()) {
return R.drawable.ic_menu_paste_holo_dark;
} else {
return R.drawable.add;
}
case 1:
return R.drawable.arrow_up;
}
return R.drawable.add;
}
@Override
public void onFileClick(View view, int position) {
/*if (hasItemSelected()) {
mFilesList.get(position).selected = !mFilesList.get(position).selected;
adapter.notifyItemChanged(position);
} else */
if (mFilesList.get(position).isDirectory()) {
EventManager.getInstance().sendBasicEvent(
"key_filespace_folder_local_clicked",
"test",
"click",
"folder",
"",
this,
"");
mCurrentDirectory = new FileModel.FileModelBuilder()
.file(new File(mFilesList.get(position).getUrl()))
.build();
showFab();
refreshCurrentList();
} else {
mFileManager.execute((Activity) getContext(), position, mFilesList, view);
}
}
@Override
public boolean onFileLongClick(View view, int position) {
/*
mFilesList.get(position).selected = !mFilesList.get(position).selected;
adapter.notifyItemChanged(position);
*/
return true;
}
@Override
public void executeFileModel(final FileModel fileModel, final View view) {
mFileLocalOverflowActions.show(fileModel, view, Config.isLogged());
}
@Override
public boolean isHomeVisible() {
return !mCurrentDirectory.getUrl().equals(initialPath());
}
@Override
public void refreshFab() {
mFileLocalFabManager.updateFabButtons();
}
@Override
public void refreshData() {
refreshCurrentList();
}
@Override
public void addCopyFile(FileModel fileModel) {
mFilesToCopyList.add(fileModel);
}
@Override
public void addCutFile(FileModel fileModel) {
mFilesToCutList.add(fileModel);
}
@Override
public boolean isFileToCut() {
return !mFilesToCutList.isEmpty();
}
@Override
public boolean isFileToCopy() {
return !mFilesToCopyList.isEmpty();
}
@Override
public void clearFileToCut() {
mFilesToCutList.clear();
}
@Override
public void clearFileToCopy() {
mFilesToCopyList.clear();
}
/**
* {@inheritDoc}
*/
@Override
public void refreshCurrentList() {
if (mIsRefreshing || mCurrentDirectory == null || !isAdded()) {
return;
}
mIsRefreshing = true;
final Context context = getContext();
if (context instanceof AppCompatActivity) {
((AppCompatActivity) context).invalidateOptionsMenu();
}
mFilesList.clear();
mProgressBar.setVisibility(View.VISIBLE);
mRecyclerView.setVisibility(View.GONE);
mMessageTextView.setVisibility(View.GONE);
mRecyclerView.scrollToPosition(0);
mFileManager.getFiles(
mCurrentDirectory,
SettingsManager.getInstance(context).isSuperUser(),
new ResultCallback<List<FileModel>>() {
@Override
public void success(final List<FileModel> result) {
mFilesList.clear();
mFilesList.addAll(result);
if (mFilesList.size() == 0) {
mMessageTextView.setText(getString(R.string.no_file_local_folder, "" + mCurrentDirectory.getName()));
mMessageTextView.setVisibility(View.VISIBLE);
} else {
mMessageTextView.setVisibility(View.GONE);
}
mProgressBar.setVisibility(View.GONE);
mRecyclerView.setVisibility(View.VISIBLE);
mIsRefreshing = false;
updateAdapter();
}
@Override
public void failure() {
mFilesList.clear();
mMessageTextView.setText("Failed to load");
mMessageTextView.setVisibility(View.VISIBLE);
mProgressBar.setVisibility(View.GONE);
mRecyclerView.setVisibility(View.VISIBLE);
mIsRefreshing = false;
updateAdapter();
}
});
}
/**
* {@inheritDoc}
*/
@Override
public void updateAdapter() {
if (mRecyclerView != null && isAdded()) {
refreshFab();
final Parcelable recyclerViewState = mRecyclerView.getLayoutManager().onSaveInstanceState();
mFileModelAdapter.setList(mFilesList);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
mScaleAnimationAdapter = new ScaleAnimationAdapter(mRecyclerView, mFileModelAdapter);
mScaleAnimationAdapter.setDuration(220);
mScaleAnimationAdapter.setOffsetDuration(32);
mRecyclerView.setAdapter(mScaleAnimationAdapter);
}
mRecyclerView.getLayoutManager().onRestoreInstanceState(recyclerViewState);
mSwipeRefreshLayout.setRefreshing(false);
}
}
/**
* {@inheritDoc}
*/
@Override
public void onRefresh() {
refreshCurrentList();
}
/**
* {@inheritDoc}
*/
@Override
public void scrollTop() {
mRecyclerView.smoothScrollToPosition(0);
}
public void goHome() {
mCurrentDirectory = createInitialDirectory();
this.refreshCurrentList();
}
public boolean hasItemSelected() {
/*
for (FileModel file : mFilesList)
if (file.selected)
return true;
*/
return false;
}
public void deselectAll() {
/*
for (FileModel file : mFilesList)
file.selected = false;
*/
updateAdapter();
}
private void findViews(final View rootView) {
mProgressBar = (ProgressBar) rootView.findViewById(R.id.circularProgressBar);
mMessageTextView = (TextView) rootView.findViewById(R.id.message);
mRecyclerView = (RecyclerView) rootView.findViewById(R.id.fragment_file_files_recycler_view);
mSwipeRefreshLayout = (SwipeRefreshLayout) rootView.findViewById(R.id.fragment_file_files_swipe_refresh_layout);
}
private void initViews() {
mProgressBar.setVisibility(View.INVISIBLE);
mRecyclerView.setHasFixedSize(true);
mSwipeRefreshLayout.setEnabled(true);
mSwipeRefreshLayout.setOnRefreshListener(this);
mSwipeRefreshLayout.setColorSchemeResources(
android.R.color.holo_blue_bright,
android.R.color.holo_green_light,
android.R.color.holo_orange_light,
android.R.color.holo_red_light);
final Activity activity = getActivity();
final int nbColumn = activity.getResources().getInteger(R.integer.column_number_card);
if (nbColumn <= 1) {
mRecyclerView.setLayoutManager(new LinearLayoutManager(activity));
} else {
mRecyclerView.setLayoutManager(new GridLayoutManager(activity, nbColumn));
}
mFileModelAdapter = new FileModelAdapter(getContext(), mFilesList, this, this, this);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
mScaleAnimationAdapter = new ScaleAnimationAdapter(mRecyclerView, mFileModelAdapter);
mScaleAnimationAdapter.setDuration(220);
mScaleAnimationAdapter.setOffsetDuration(32);
mRecyclerView.setAdapter(mScaleAnimationAdapter);
} else {
mRecyclerView.setAdapter(mFileModelAdapter);
}
mRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
@Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
if (dy <= 0) {
showFab();
} else {
hideFab();
}
}
});
}
@NonNull
protected FileModel createInitialDirectory() {
final String initialFolderPath = IntentManager.getInstance().getInitialFolderPath();
final File file = new File(initialFolderPath != null ?
initialFolderPath :
Environment.getExternalStorageDirectory().getAbsolutePath() +
File.separator + Config.getLocalFolderName());
if (!file.exists()) {
file.mkdir();
}
return new FileModel.FileModelBuilder().file(file).build();
}
protected String initialPath() {
return Environment.getExternalStorageDirectory().getAbsolutePath();
}
private void hideFab() {
if (!mIsFabAnimating) {
mIsFabAnimating = true;
mIsFabHidden = true;
mFileLocalFabManager.updateFabButtons();
mHandler.removeCallbacks(mRunnable);
mHandler.postDelayed(mRunnable, 250);
}
}
private void showFab() {
if (!mIsFabAnimating) {
mIsFabAnimating = true;
mIsFabHidden = false;
mFileLocalFabManager.updateFabButtons();
mHandler.removeCallbacks(mRunnable);
mHandler.postDelayed(mRunnable, 250);
}
}
}