convert ItemsFragment to kotlin
This commit is contained in:
parent
f5257a0c8a
commit
5a76dd5db2
|
@ -1,498 +0,0 @@
|
||||||
package com.simplemobiletools.filemanager.fragments;
|
|
||||||
|
|
||||||
import android.content.ActivityNotFoundException;
|
|
||||||
import android.content.Intent;
|
|
||||||
import android.content.res.Resources;
|
|
||||||
import android.graphics.Color;
|
|
||||||
import android.net.Uri;
|
|
||||||
import android.os.Bundle;
|
|
||||||
import android.os.Parcelable;
|
|
||||||
import android.support.annotation.Nullable;
|
|
||||||
import android.support.design.widget.CoordinatorLayout;
|
|
||||||
import android.support.design.widget.Snackbar;
|
|
||||||
import android.support.v4.provider.DocumentFile;
|
|
||||||
import android.support.v4.widget.SwipeRefreshLayout;
|
|
||||||
import android.util.SparseBooleanArray;
|
|
||||||
import android.view.ActionMode;
|
|
||||||
import android.view.LayoutInflater;
|
|
||||||
import android.view.Menu;
|
|
||||||
import android.view.MenuItem;
|
|
||||||
import android.view.MotionEvent;
|
|
||||||
import android.view.View;
|
|
||||||
import android.view.ViewGroup;
|
|
||||||
import android.webkit.MimeTypeMap;
|
|
||||||
import android.widget.AdapterView;
|
|
||||||
import android.widget.ListView;
|
|
||||||
|
|
||||||
import com.simplemobiletools.filemanager.Config;
|
|
||||||
import com.simplemobiletools.filemanager.Constants;
|
|
||||||
import com.simplemobiletools.filemanager.R;
|
|
||||||
import com.simplemobiletools.filemanager.Utils;
|
|
||||||
import com.simplemobiletools.filemanager.activities.SimpleActivity;
|
|
||||||
import com.simplemobiletools.filemanager.adapters.ItemsAdapter;
|
|
||||||
import com.simplemobiletools.filemanager.dialogs.CopyDialog;
|
|
||||||
import com.simplemobiletools.filemanager.dialogs.CreateNewItemDialog;
|
|
||||||
import com.simplemobiletools.filemanager.dialogs.RenameItemDialog;
|
|
||||||
import com.simplemobiletools.filepicker.asynctasks.CopyMoveTask;
|
|
||||||
import com.simplemobiletools.filepicker.models.FileDirItem;
|
|
||||||
import com.simplemobiletools.fileproperties.dialogs.PropertiesDialog;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.FileFilter;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
import butterknife.BindView;
|
|
||||||
import butterknife.ButterKnife;
|
|
||||||
import butterknife.OnClick;
|
|
||||||
|
|
||||||
public class ItemsFragment extends android.support.v4.app.Fragment
|
|
||||||
implements AdapterView.OnItemClickListener, SwipeRefreshLayout.OnRefreshListener, ListView.MultiChoiceModeListener,
|
|
||||||
ListView.OnTouchListener {
|
|
||||||
@BindView(R.id.items_list) ListView mListView;
|
|
||||||
@BindView(R.id.items_swipe_refresh) SwipeRefreshLayout mSwipeRefreshLayout;
|
|
||||||
@BindView(R.id.items_holder) CoordinatorLayout mCoordinatorLayout;
|
|
||||||
|
|
||||||
private static Map<String, Parcelable> mStates;
|
|
||||||
|
|
||||||
private List<FileDirItem> mItems;
|
|
||||||
private ItemInteractionListener mListener;
|
|
||||||
private List<String> mToBeDeleted;
|
|
||||||
private String mPath;
|
|
||||||
private Snackbar mSnackbar;
|
|
||||||
private Config mConfig;
|
|
||||||
|
|
||||||
private boolean mShowHidden;
|
|
||||||
private int mSelectedItemsCnt;
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
@Override
|
|
||||||
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
|
||||||
final View view = inflater.inflate(R.layout.items_fragment, container, false);
|
|
||||||
ButterKnife.bind(this, view);
|
|
||||||
return view;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
|
|
||||||
super.onViewCreated(view, savedInstanceState);
|
|
||||||
if (mStates == null)
|
|
||||||
mStates = new HashMap<>();
|
|
||||||
mConfig = Config.Companion.newInstance(getContext());
|
|
||||||
mShowHidden = mConfig.getShowHidden();
|
|
||||||
mItems = new ArrayList<>();
|
|
||||||
mToBeDeleted = new ArrayList<>();
|
|
||||||
fillItems();
|
|
||||||
mSwipeRefreshLayout.setOnRefreshListener(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onResume() {
|
|
||||||
super.onResume();
|
|
||||||
if (mShowHidden != mConfig.getShowHidden()) {
|
|
||||||
mShowHidden = !mShowHidden;
|
|
||||||
mStates.remove(mPath);
|
|
||||||
fillItems();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onPause() {
|
|
||||||
super.onPause();
|
|
||||||
deleteItems();
|
|
||||||
mStates.put(mPath, mListView.onSaveInstanceState());
|
|
||||||
}
|
|
||||||
|
|
||||||
private void fillItems() {
|
|
||||||
mPath = getArguments().getString(Constants.INSTANCE.getPATH());
|
|
||||||
final List<FileDirItem> newItems = getItems(mPath);
|
|
||||||
Collections.sort(newItems);
|
|
||||||
if (mItems != null && newItems.toString().equals(mItems.toString())) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
mItems = newItems;
|
|
||||||
|
|
||||||
final ItemsAdapter adapter = new ItemsAdapter(getContext(), mItems);
|
|
||||||
mListView.setAdapter(adapter);
|
|
||||||
mListView.setOnItemClickListener(this);
|
|
||||||
mListView.setMultiChoiceModeListener(this);
|
|
||||||
mListView.setOnTouchListener(this);
|
|
||||||
|
|
||||||
if (mStates != null && mStates.get(mPath) != null) {
|
|
||||||
mListView.onRestoreInstanceState(mStates.get(mPath));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setListener(ItemInteractionListener listener) {
|
|
||||||
mListener = listener;
|
|
||||||
}
|
|
||||||
|
|
||||||
private List<FileDirItem> getItems(String path) {
|
|
||||||
final List<FileDirItem> items = new ArrayList<>();
|
|
||||||
final File base = new File(path);
|
|
||||||
File[] files = base.listFiles();
|
|
||||||
if (files != null) {
|
|
||||||
for (File file : files) {
|
|
||||||
final String curPath = file.getAbsolutePath();
|
|
||||||
final String curName = Utils.Companion.getFilename(curPath);
|
|
||||||
if (!mShowHidden && curName.startsWith("."))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (mToBeDeleted.contains(curPath))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
int children = getChildren(file);
|
|
||||||
long size = file.length();
|
|
||||||
|
|
||||||
items.add(new FileDirItem(curPath, curName, file.isDirectory(), children, size));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return items;
|
|
||||||
}
|
|
||||||
|
|
||||||
private int getChildren(File file) {
|
|
||||||
if (file.listFiles() == null)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
if (file.isDirectory()) {
|
|
||||||
if (mShowHidden) {
|
|
||||||
return file.listFiles().length;
|
|
||||||
} else {
|
|
||||||
return file.listFiles(new FileFilter() {
|
|
||||||
@Override
|
|
||||||
public boolean accept(File file) {
|
|
||||||
return !file.isHidden();
|
|
||||||
}
|
|
||||||
}).length;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
|
|
||||||
final FileDirItem item = mItems.get(position);
|
|
||||||
if (item.isDirectory()) {
|
|
||||||
if (mListener != null)
|
|
||||||
mListener.itemClicked(item);
|
|
||||||
} else {
|
|
||||||
final String path = item.getPath();
|
|
||||||
final File file = new File(path);
|
|
||||||
String mimeType = MimeTypeMap.getSingleton().getMimeTypeFromExtension(Utils.Companion.getFileExtension(path));
|
|
||||||
if (mimeType == null)
|
|
||||||
mimeType = "text/plain";
|
|
||||||
|
|
||||||
final Intent intent = new Intent(Intent.ACTION_VIEW);
|
|
||||||
intent.setDataAndType(Uri.fromFile(file), mimeType);
|
|
||||||
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
|
||||||
try {
|
|
||||||
startActivity(intent);
|
|
||||||
} catch (ActivityNotFoundException e) {
|
|
||||||
if (!tryGenericMimeType(intent, mimeType, file)) {
|
|
||||||
Utils.Companion.showToast(getContext(), R.string.no_app_found);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean tryGenericMimeType(Intent intent, String mimeType, File file) {
|
|
||||||
final String genericMimeType = getGenericMimeType(mimeType);
|
|
||||||
intent.setDataAndType(Uri.fromFile(file), genericMimeType);
|
|
||||||
try {
|
|
||||||
startActivity(intent);
|
|
||||||
return true;
|
|
||||||
} catch (ActivityNotFoundException e) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@OnClick(R.id.items_fab)
|
|
||||||
public void fabClicked(View view) {
|
|
||||||
new CreateNewItemDialog(getContext(), mPath, new CreateNewItemDialog.OnCreateNewItemListener() {
|
|
||||||
@Override
|
|
||||||
public void onSuccess() {
|
|
||||||
fillItems();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onRefresh() {
|
|
||||||
fillItems();
|
|
||||||
mSwipeRefreshLayout.setRefreshing(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
private String getGenericMimeType(String mimeType) {
|
|
||||||
if (!mimeType.contains("/"))
|
|
||||||
return mimeType;
|
|
||||||
|
|
||||||
final String type = mimeType.substring(0, mimeType.indexOf("/"));
|
|
||||||
return type + "/*";
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onItemCheckedStateChanged(ActionMode mode, int position, long id, boolean checked) {
|
|
||||||
if (checked) {
|
|
||||||
mSelectedItemsCnt++;
|
|
||||||
} else {
|
|
||||||
mSelectedItemsCnt--;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mSelectedItemsCnt > 0) {
|
|
||||||
mode.setTitle(String.valueOf(mSelectedItemsCnt));
|
|
||||||
}
|
|
||||||
|
|
||||||
mode.invalidate();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean onCreateActionMode(ActionMode mode, Menu menu) {
|
|
||||||
mode.getMenuInflater().inflate(R.menu.cab, menu);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
|
|
||||||
MenuItem menuItem = menu.findItem(R.id.cab_rename);
|
|
||||||
menuItem.setVisible(mSelectedItemsCnt == 1);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
|
|
||||||
switch (item.getItemId()) {
|
|
||||||
case R.id.cab_rename:
|
|
||||||
displayRenameDialog();
|
|
||||||
mode.finish();
|
|
||||||
break;
|
|
||||||
case R.id.cab_properties:
|
|
||||||
displayPropertiesDialog();
|
|
||||||
break;
|
|
||||||
case R.id.cab_share:
|
|
||||||
shareFiles();
|
|
||||||
break;
|
|
||||||
case R.id.cab_copy:
|
|
||||||
displayCopyDialog();
|
|
||||||
mode.finish();
|
|
||||||
break;
|
|
||||||
case R.id.cab_delete:
|
|
||||||
prepareForDeleting();
|
|
||||||
mode.finish();
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void shareFiles() {
|
|
||||||
final List<Integer> itemIndexes = getSelectedItemIndexes();
|
|
||||||
if (itemIndexes.isEmpty())
|
|
||||||
return;
|
|
||||||
|
|
||||||
final ArrayList<Uri> uris = new ArrayList<>(itemIndexes.size());
|
|
||||||
for (int i : itemIndexes) {
|
|
||||||
final File file = new File(mItems.get(i).getPath());
|
|
||||||
if (!file.isDirectory())
|
|
||||||
uris.add(Uri.fromFile(file));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (uris.isEmpty()) {
|
|
||||||
Utils.Companion.showToast(getContext(), R.string.no_files_selected);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
final String shareTitle = getResources().getString(R.string.share_via);
|
|
||||||
final Intent sendIntent = new Intent();
|
|
||||||
sendIntent.setAction(Intent.ACTION_SEND_MULTIPLE);
|
|
||||||
sendIntent.putExtra(Intent.EXTRA_SUBJECT, getResources().getString(R.string.shared_files));
|
|
||||||
sendIntent.putParcelableArrayListExtra(Intent.EXTRA_STREAM, uris);
|
|
||||||
sendIntent.setType("*/*");
|
|
||||||
startActivity(Intent.createChooser(sendIntent, shareTitle));
|
|
||||||
}
|
|
||||||
|
|
||||||
private void displayPropertiesDialog() {
|
|
||||||
final List<Integer> itemIndexes = getSelectedItemIndexes();
|
|
||||||
if (itemIndexes.isEmpty())
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (itemIndexes.size() == 1) {
|
|
||||||
showOneItemProperties();
|
|
||||||
} else {
|
|
||||||
showMultipleItemProperties(itemIndexes);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void showOneItemProperties() {
|
|
||||||
final FileDirItem item = getSelectedItem();
|
|
||||||
if (item == null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
new PropertiesDialog(getActivity(), item.getPath(), mConfig.getShowHidden());
|
|
||||||
}
|
|
||||||
|
|
||||||
private void showMultipleItemProperties(List<Integer> itemIndexes) {
|
|
||||||
final List<String> paths = new ArrayList<>(itemIndexes.size());
|
|
||||||
for (int i : itemIndexes) {
|
|
||||||
paths.add(mItems.get(i).getPath());
|
|
||||||
}
|
|
||||||
new PropertiesDialog(getActivity(), paths, mConfig.getShowHidden());
|
|
||||||
}
|
|
||||||
|
|
||||||
private void displayRenameDialog() {
|
|
||||||
final FileDirItem item = getSelectedItem();
|
|
||||||
if (item == null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
new RenameItemDialog(getContext(), mPath, item, new RenameItemDialog.OnRenameItemListener() {
|
|
||||||
@Override
|
|
||||||
public void onSuccess() {
|
|
||||||
fillItems();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private void displayCopyDialog() {
|
|
||||||
final List<Integer> fileIndexes = getSelectedItemIndexes();
|
|
||||||
if (fileIndexes.isEmpty())
|
|
||||||
return;
|
|
||||||
|
|
||||||
final ArrayList<File> files = new ArrayList<>(fileIndexes.size());
|
|
||||||
for (Integer i : fileIndexes) {
|
|
||||||
FileDirItem item = mItems.get(i);
|
|
||||||
files.add(new File(item.getPath()));
|
|
||||||
}
|
|
||||||
|
|
||||||
new CopyDialog((SimpleActivity) getActivity(), files, new CopyMoveTask.CopyMoveListener() {
|
|
||||||
@Override
|
|
||||||
public void copySucceeded(boolean deleted, boolean copiedAll) {
|
|
||||||
int msgId;
|
|
||||||
if (deleted) {
|
|
||||||
msgId = copiedAll ? R.string.moving_success : R.string.moving_success_partial;
|
|
||||||
} else {
|
|
||||||
msgId = copiedAll? R.string.copying_success : R.string.copying_success_partial;
|
|
||||||
}
|
|
||||||
fillItems();
|
|
||||||
Utils.Companion.showToast(getContext(), msgId);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void copyFailed() {
|
|
||||||
Utils.Companion.showToast(getContext(), R.string.copy_move_failed);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private FileDirItem getSelectedItem() {
|
|
||||||
final List<Integer> itemIndexes = getSelectedItemIndexes();
|
|
||||||
if (itemIndexes.isEmpty())
|
|
||||||
return null;
|
|
||||||
|
|
||||||
final int itemIndex = itemIndexes.get(0);
|
|
||||||
return mItems.get(itemIndex);
|
|
||||||
}
|
|
||||||
|
|
||||||
private List<Integer> getSelectedItemIndexes() {
|
|
||||||
final List<Integer> selectedItems = new ArrayList<>();
|
|
||||||
final SparseBooleanArray items = mListView.getCheckedItemPositions();
|
|
||||||
int cnt = items.size();
|
|
||||||
for (int i = 0; i < cnt; i++) {
|
|
||||||
if (items.valueAt(i)) {
|
|
||||||
selectedItems.add(items.keyAt(i));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return selectedItems;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void prepareForDeleting() {
|
|
||||||
mToBeDeleted.clear();
|
|
||||||
final SparseBooleanArray items = mListView.getCheckedItemPositions();
|
|
||||||
final int cnt = items.size();
|
|
||||||
int deletedCnt = 0;
|
|
||||||
for (int i = 0; i < cnt; i++) {
|
|
||||||
if (items.valueAt(i)) {
|
|
||||||
final int id = items.keyAt(i);
|
|
||||||
final String path = mItems.get(id).getPath();
|
|
||||||
mToBeDeleted.add(path);
|
|
||||||
deletedCnt++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
notifyDeletion(deletedCnt);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void notifyDeletion(int cnt) {
|
|
||||||
final Resources res = getResources();
|
|
||||||
final String msg = res.getQuantityString(R.plurals.items_deleted, cnt, cnt);
|
|
||||||
mSnackbar = Snackbar.make(mCoordinatorLayout, msg, Snackbar.LENGTH_INDEFINITE);
|
|
||||||
mSnackbar.setAction(res.getString(R.string.undo), undoDeletion);
|
|
||||||
mSnackbar.setActionTextColor(Color.WHITE);
|
|
||||||
mSnackbar.show();
|
|
||||||
fillItems();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean onTouch(View v, MotionEvent event) {
|
|
||||||
if (mSnackbar != null && mSnackbar.isShown()) {
|
|
||||||
deleteItems();
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void deleteItems() {
|
|
||||||
if (mToBeDeleted == null || mToBeDeleted.isEmpty())
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (mSnackbar != null) {
|
|
||||||
mSnackbar.dismiss();
|
|
||||||
}
|
|
||||||
|
|
||||||
for (String delPath : mToBeDeleted) {
|
|
||||||
final File file = new File(delPath);
|
|
||||||
if (file.exists()) {
|
|
||||||
deleteItem(file);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
mToBeDeleted.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void deleteItem(File item) {
|
|
||||||
if (item.isDirectory()) {
|
|
||||||
for (File child : item.listFiles()) {
|
|
||||||
deleteItem(child);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Utils.Companion.needsStupidWritePermissions(getContext(), item.getAbsolutePath())) {
|
|
||||||
final DocumentFile document = Utils.Companion.getFileDocument(getContext(), item.getAbsolutePath(), mConfig.getTreeUri());
|
|
||||||
document.delete();
|
|
||||||
} else {
|
|
||||||
item.delete();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private View.OnClickListener undoDeletion = new View.OnClickListener() {
|
|
||||||
@Override
|
|
||||||
public void onClick(View v) {
|
|
||||||
mToBeDeleted.clear();
|
|
||||||
mSnackbar.dismiss();
|
|
||||||
fillItems();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onDestroyActionMode(ActionMode mode) {
|
|
||||||
mSelectedItemsCnt = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
public interface ItemInteractionListener {
|
|
||||||
void itemClicked(FileDirItem item);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,5 +1,7 @@
|
||||||
package com.simplemobiletools.filemanager
|
package com.simplemobiletools.filemanager
|
||||||
|
|
||||||
|
val PATH = "path"
|
||||||
|
|
||||||
// shared preferences
|
// shared preferences
|
||||||
val PREFS_KEY = "File Manager"
|
val PREFS_KEY = "File Manager"
|
||||||
val IS_FIRST_RUN = "is_first_run"
|
val IS_FIRST_RUN = "is_first_run"
|
||||||
|
@ -9,7 +11,3 @@ val TREE_URI = "tree_uri"
|
||||||
|
|
||||||
// global intents
|
// global intents
|
||||||
val OPEN_DOCUMENT_TREE = 1000
|
val OPEN_DOCUMENT_TREE = 1000
|
||||||
|
|
||||||
object Constants {
|
|
||||||
val PATH = "path"
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,24 +0,0 @@
|
||||||
package com.simplemobiletools.filemanager
|
|
||||||
|
|
||||||
import android.content.Context
|
|
||||||
import com.simplemobiletools.filepicker.extensions.getFileDocument
|
|
||||||
import com.simplemobiletools.filepicker.extensions.needsStupidWritePermissions
|
|
||||||
import com.simplemobiletools.filepicker.extensions.scanFile
|
|
||||||
import com.simplemobiletools.filepicker.extensions.toast
|
|
||||||
import java.io.File
|
|
||||||
|
|
||||||
class Utils {
|
|
||||||
companion object {
|
|
||||||
fun getFilename(path: String) = path.substring(path.lastIndexOf("/") + 1)
|
|
||||||
|
|
||||||
fun getFileExtension(fileName: String) = fileName.substring(fileName.lastIndexOf(".") + 1, fileName.length).toLowerCase()
|
|
||||||
|
|
||||||
fun showToast(context: Context, resId: Int) = context.toast(resId)
|
|
||||||
|
|
||||||
fun needsStupidWritePermissions(context: Context, path: String) = context.needsStupidWritePermissions(path)
|
|
||||||
|
|
||||||
fun getFileDocument(context: Context, path: String, treeUri: String) = context.getFileDocument(path, treeUri)
|
|
||||||
|
|
||||||
fun scanFile(context: Context, file: File) = context.scanFile(file) {}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -8,7 +8,7 @@ import android.os.Handler
|
||||||
import android.support.v4.app.ActivityCompat
|
import android.support.v4.app.ActivityCompat
|
||||||
import android.view.Menu
|
import android.view.Menu
|
||||||
import android.view.MenuItem
|
import android.view.MenuItem
|
||||||
import com.simplemobiletools.filemanager.Constants
|
import com.simplemobiletools.filemanager.PATH
|
||||||
import com.simplemobiletools.filemanager.R
|
import com.simplemobiletools.filemanager.R
|
||||||
import com.simplemobiletools.filemanager.fragments.ItemsFragment
|
import com.simplemobiletools.filemanager.fragments.ItemsFragment
|
||||||
import com.simplemobiletools.filepicker.dialogs.StoragePickerDialog
|
import com.simplemobiletools.filepicker.dialogs.StoragePickerDialog
|
||||||
|
@ -50,7 +50,7 @@ class MainActivity : SimpleActivity(), ItemsFragment.ItemInteractionListener, Br
|
||||||
private fun openPath(path: String) {
|
private fun openPath(path: String) {
|
||||||
breadcrumbs.setBreadcrumb(path)
|
breadcrumbs.setBreadcrumb(path)
|
||||||
val bundle = Bundle()
|
val bundle = Bundle()
|
||||||
bundle.putString(Constants.PATH, path)
|
bundle.putString(PATH, path)
|
||||||
|
|
||||||
val fragment = ItemsFragment()
|
val fragment = ItemsFragment()
|
||||||
fragment.arguments = bundle
|
fragment.arguments = bundle
|
||||||
|
|
|
@ -0,0 +1,400 @@
|
||||||
|
package com.simplemobiletools.filemanager.fragments
|
||||||
|
|
||||||
|
import android.content.ActivityNotFoundException
|
||||||
|
import android.content.Intent
|
||||||
|
import android.graphics.Color
|
||||||
|
import android.net.Uri
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.support.design.widget.Snackbar
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.view.MotionEvent
|
||||||
|
import android.view.View
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import android.webkit.MimeTypeMap
|
||||||
|
import android.widget.AdapterView
|
||||||
|
import com.simplemobiletools.filemanager.Config
|
||||||
|
import com.simplemobiletools.filemanager.PATH
|
||||||
|
import com.simplemobiletools.filemanager.R
|
||||||
|
import com.simplemobiletools.filemanager.activities.SimpleActivity
|
||||||
|
import com.simplemobiletools.filemanager.adapters.ItemsAdapter
|
||||||
|
import com.simplemobiletools.filemanager.dialogs.CopyDialog
|
||||||
|
import com.simplemobiletools.filemanager.dialogs.CreateNewItemDialog
|
||||||
|
import com.simplemobiletools.filemanager.dialogs.RenameItemDialog
|
||||||
|
import com.simplemobiletools.filepicker.asynctasks.CopyMoveTask
|
||||||
|
import com.simplemobiletools.filepicker.extensions.*
|
||||||
|
import com.simplemobiletools.filepicker.models.FileDirItem
|
||||||
|
import com.simplemobiletools.fileproperties.dialogs.PropertiesDialog
|
||||||
|
import kotlinx.android.synthetic.main.items_fragment.*
|
||||||
|
import java.io.File
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
|
class ItemsFragment : android.support.v4.app.Fragment(), AdapterView.OnItemClickListener, /*ListView.MultiChoiceModeListener, */View.OnTouchListener {
|
||||||
|
private var mListener: ItemInteractionListener? = null
|
||||||
|
private var mSnackbar: Snackbar? = null
|
||||||
|
|
||||||
|
lateinit var mItems: List<FileDirItem>
|
||||||
|
lateinit var mConfig: Config
|
||||||
|
lateinit var mToBeDeleted: MutableList<String>
|
||||||
|
|
||||||
|
private var mPath = ""
|
||||||
|
private var mShowHidden = false
|
||||||
|
|
||||||
|
override fun onCreateView(inflater: LayoutInflater?, container: ViewGroup?, savedInstanceState: Bundle?) =
|
||||||
|
inflater!!.inflate(R.layout.items_fragment, container, false)
|
||||||
|
|
||||||
|
override fun onViewCreated(view: View?, savedInstanceState: Bundle?) {
|
||||||
|
super.onViewCreated(view, savedInstanceState)
|
||||||
|
mConfig = Config.newInstance(context)
|
||||||
|
mShowHidden = mConfig.showHidden
|
||||||
|
mItems = ArrayList<FileDirItem>()
|
||||||
|
mToBeDeleted = ArrayList<String>()
|
||||||
|
fillItems()
|
||||||
|
|
||||||
|
items_swipe_refresh.setOnRefreshListener({ fillItems() })
|
||||||
|
items_fab.setOnClickListener { createNewItem() }
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onResume() {
|
||||||
|
super.onResume()
|
||||||
|
if (mShowHidden != mConfig.showHidden) {
|
||||||
|
mShowHidden = !mShowHidden
|
||||||
|
fillItems()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onPause() {
|
||||||
|
super.onPause()
|
||||||
|
deleteItems()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun fillItems() {
|
||||||
|
mPath = arguments.getString(PATH)
|
||||||
|
val newItems = getItems(mPath)
|
||||||
|
Collections.sort(newItems)
|
||||||
|
items_swipe_refresh.isRefreshing = false
|
||||||
|
if (newItems.toString() == mItems.toString()) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
mItems = newItems
|
||||||
|
|
||||||
|
val adapter = ItemsAdapter(context, mItems)
|
||||||
|
items_list.adapter = adapter
|
||||||
|
items_list.onItemClickListener = this
|
||||||
|
items_list.setOnTouchListener(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setListener(listener: ItemInteractionListener) {
|
||||||
|
mListener = listener
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getItems(path: String): List<FileDirItem> {
|
||||||
|
val items = ArrayList<FileDirItem>()
|
||||||
|
val files = File(path).listFiles()
|
||||||
|
if (files != null) {
|
||||||
|
for (file in files) {
|
||||||
|
val curPath = file.absolutePath
|
||||||
|
val curName = curPath.getFilenameFromPath()
|
||||||
|
if (!mShowHidden && curName.startsWith("."))
|
||||||
|
continue
|
||||||
|
|
||||||
|
if (mToBeDeleted.contains(curPath))
|
||||||
|
continue
|
||||||
|
|
||||||
|
val children = getChildren(file)
|
||||||
|
val size = file.length()
|
||||||
|
|
||||||
|
items.add(FileDirItem(curPath, curName, file.isDirectory, children, size))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return items
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getChildren(file: File): Int {
|
||||||
|
if (file.listFiles() == null)
|
||||||
|
return 0
|
||||||
|
|
||||||
|
if (file.isDirectory) {
|
||||||
|
return if (mShowHidden) {
|
||||||
|
file.listFiles().size
|
||||||
|
} else {
|
||||||
|
file.listFiles { file -> !file.isHidden }.size
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onItemClick(parent: AdapterView<*>, view: View, position: Int, id: Long) {
|
||||||
|
val item = mItems[position]
|
||||||
|
if (item.isDirectory) {
|
||||||
|
mListener?.itemClicked(item)
|
||||||
|
} else {
|
||||||
|
val path = item.path
|
||||||
|
val file = File(path)
|
||||||
|
var mimeType: String? = MimeTypeMap.getSingleton().getMimeTypeFromExtension(path.getFilenameExtension().toLowerCase())
|
||||||
|
if (mimeType == null)
|
||||||
|
mimeType = "text/plain"
|
||||||
|
|
||||||
|
Intent(Intent.ACTION_VIEW).apply {
|
||||||
|
setDataAndType(Uri.fromFile(file), mimeType)
|
||||||
|
flags = Intent.FLAG_ACTIVITY_NEW_TASK
|
||||||
|
try {
|
||||||
|
startActivity(this)
|
||||||
|
} catch (e: ActivityNotFoundException) {
|
||||||
|
if (!tryGenericMimeType(this, mimeType!!, file)) {
|
||||||
|
context.toast(R.string.no_app_found)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun tryGenericMimeType(intent: Intent, mimeType: String, file: File): Boolean {
|
||||||
|
val genericMimeType = getGenericMimeType(mimeType)
|
||||||
|
intent.setDataAndType(Uri.fromFile(file), genericMimeType)
|
||||||
|
return try {
|
||||||
|
startActivity(intent)
|
||||||
|
true
|
||||||
|
} catch (e: ActivityNotFoundException) {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun createNewItem() {
|
||||||
|
CreateNewItemDialog(context, mPath, object : CreateNewItemDialog.OnCreateNewItemListener {
|
||||||
|
override fun onSuccess() {
|
||||||
|
fillItems()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getGenericMimeType(mimeType: String): String {
|
||||||
|
if (!mimeType.contains("/"))
|
||||||
|
return mimeType
|
||||||
|
|
||||||
|
val type = mimeType.substring(0, mimeType.indexOf("/"))
|
||||||
|
return type + "/*"
|
||||||
|
}
|
||||||
|
|
||||||
|
/*override fun onItemCheckedStateChanged(mode: ActionMode, position: Int, id: Long, checked: Boolean) {
|
||||||
|
if (checked) {
|
||||||
|
mSelectedItemsCnt++
|
||||||
|
} else {
|
||||||
|
mSelectedItemsCnt--
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mSelectedItemsCnt > 0) {
|
||||||
|
mode.title = mSelectedItemsCnt.toString()
|
||||||
|
}
|
||||||
|
|
||||||
|
mode.invalidate()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCreateActionMode(mode: ActionMode, menu: Menu): Boolean {
|
||||||
|
mode.menuInflater.inflate(R.menu.cab, menu)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onPrepareActionMode(mode: ActionMode, menu: Menu): Boolean {
|
||||||
|
val menuItem = menu.findItem(R.id.cab_rename)
|
||||||
|
menuItem.isVisible = mSelectedItemsCnt == 1
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onActionItemClicked(mode: ActionMode, item: MenuItem): Boolean {
|
||||||
|
when (item.itemId) {
|
||||||
|
R.id.cab_rename -> {
|
||||||
|
displayRenameDialog()
|
||||||
|
mode.finish()
|
||||||
|
}
|
||||||
|
R.id.cab_properties -> displayPropertiesDialog()
|
||||||
|
R.id.cab_share -> shareFiles()
|
||||||
|
R.id.cab_copy -> {
|
||||||
|
displayCopyDialog()
|
||||||
|
mode.finish()
|
||||||
|
}
|
||||||
|
R.id.cab_delete -> {
|
||||||
|
prepareForDeleting()
|
||||||
|
mode.finish()
|
||||||
|
}
|
||||||
|
else -> return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}*/
|
||||||
|
|
||||||
|
private fun shareFiles() {
|
||||||
|
val itemIndexes = getSelectedItemIndexes()
|
||||||
|
if (itemIndexes.isEmpty())
|
||||||
|
return
|
||||||
|
|
||||||
|
val uris = ArrayList<Uri>(itemIndexes.size)
|
||||||
|
itemIndexes.map { File(mItems[it].path) }
|
||||||
|
.filterNot { it.isDirectory }
|
||||||
|
.mapTo(uris) { Uri.fromFile(it) }
|
||||||
|
|
||||||
|
if (uris.isEmpty()) {
|
||||||
|
context.toast(R.string.no_files_selected)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
val shareTitle = resources.getString(R.string.share_via)
|
||||||
|
Intent().apply {
|
||||||
|
action = Intent.ACTION_SEND_MULTIPLE
|
||||||
|
putExtra(Intent.EXTRA_SUBJECT, resources.getString(R.string.shared_files))
|
||||||
|
putParcelableArrayListExtra(Intent.EXTRA_STREAM, uris)
|
||||||
|
type = "*/*"
|
||||||
|
startActivity(Intent.createChooser(this, shareTitle))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun displayPropertiesDialog() {
|
||||||
|
val itemIndexes = getSelectedItemIndexes()
|
||||||
|
if (itemIndexes.isEmpty())
|
||||||
|
return
|
||||||
|
|
||||||
|
if (itemIndexes.size == 1) {
|
||||||
|
showOneItemProperties()
|
||||||
|
} else {
|
||||||
|
showMultipleItemProperties(itemIndexes)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun showOneItemProperties() {
|
||||||
|
val item = getSelectedItem() ?: return
|
||||||
|
PropertiesDialog(activity, item.path, mConfig.showHidden)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun showMultipleItemProperties(itemIndexes: List<Int>) {
|
||||||
|
val paths = ArrayList<String>(itemIndexes.size)
|
||||||
|
itemIndexes.mapTo(paths) { mItems[it].path }
|
||||||
|
PropertiesDialog(activity, paths, mConfig.showHidden)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun displayRenameDialog() {
|
||||||
|
val item = getSelectedItem() ?: return
|
||||||
|
|
||||||
|
RenameItemDialog(context, mPath, item, object : RenameItemDialog.OnRenameItemListener {
|
||||||
|
override fun onSuccess() {
|
||||||
|
fillItems()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun displayCopyDialog() {
|
||||||
|
val fileIndexes = getSelectedItemIndexes()
|
||||||
|
if (fileIndexes.isEmpty())
|
||||||
|
return
|
||||||
|
|
||||||
|
val files = ArrayList<File>(fileIndexes.size)
|
||||||
|
fileIndexes.mapTo(files) { File(mItems[it].path) }
|
||||||
|
|
||||||
|
CopyDialog(activity as SimpleActivity, files, object : CopyMoveTask.CopyMoveListener {
|
||||||
|
override fun copySucceeded(deleted: Boolean, copiedAll: Boolean) {
|
||||||
|
if (deleted) {
|
||||||
|
context.toast(if (copiedAll) R.string.moving_success else R.string.moving_success_partial)
|
||||||
|
} else {
|
||||||
|
context.toast(if (copiedAll) R.string.copying_success else R.string.copying_success_partial)
|
||||||
|
}
|
||||||
|
fillItems()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun copyFailed() {
|
||||||
|
context.toast(R.string.copy_move_failed)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getSelectedItem(): FileDirItem? {
|
||||||
|
val itemIndexes = getSelectedItemIndexes()
|
||||||
|
if (itemIndexes.isEmpty())
|
||||||
|
return null
|
||||||
|
|
||||||
|
val itemIndex = itemIndexes[0]
|
||||||
|
return mItems[itemIndex]
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getSelectedItemIndexes(): List<Int> {
|
||||||
|
val items = items_list.checkedItemPositions
|
||||||
|
val cnt = items.size()
|
||||||
|
val selectedItems = (0..cnt - 1)
|
||||||
|
.filter { items.valueAt(it) }
|
||||||
|
.map { items.keyAt(it) }
|
||||||
|
return selectedItems
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun prepareForDeleting() {
|
||||||
|
mToBeDeleted.clear()
|
||||||
|
val items = items_list.checkedItemPositions
|
||||||
|
val cnt = items.size()
|
||||||
|
var deletedCnt = 0
|
||||||
|
for (i in 0..cnt - 1) {
|
||||||
|
if (items.valueAt(i)) {
|
||||||
|
val id = items.keyAt(i)
|
||||||
|
val path = mItems[id].path
|
||||||
|
mToBeDeleted.add(path)
|
||||||
|
deletedCnt++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
notifyDeletion(deletedCnt)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun notifyDeletion(cnt: Int) {
|
||||||
|
val res = resources
|
||||||
|
val msg = res.getQuantityString(R.plurals.items_deleted, cnt, cnt)
|
||||||
|
mSnackbar = Snackbar.make(items_holder, msg, Snackbar.LENGTH_INDEFINITE)
|
||||||
|
mSnackbar!!.apply {
|
||||||
|
setAction(res.getString(R.string.undo), undoDeletion)
|
||||||
|
setActionTextColor(Color.WHITE)
|
||||||
|
show()
|
||||||
|
}
|
||||||
|
fillItems()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onTouch(v: View, event: MotionEvent): Boolean {
|
||||||
|
if (mSnackbar != null && mSnackbar!!.isShown) {
|
||||||
|
deleteItems()
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun deleteItems() {
|
||||||
|
if (mToBeDeleted.isEmpty())
|
||||||
|
return
|
||||||
|
|
||||||
|
mSnackbar?.dismiss()
|
||||||
|
mToBeDeleted
|
||||||
|
.map(::File)
|
||||||
|
.filter(File::exists)
|
||||||
|
.forEach { deleteItem(it) }
|
||||||
|
|
||||||
|
mToBeDeleted.clear()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun deleteItem(item: File) {
|
||||||
|
if (item.isDirectory) {
|
||||||
|
for (child in item.listFiles()) {
|
||||||
|
deleteItem(child)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (context.needsStupidWritePermissions(item.absolutePath)) {
|
||||||
|
val document = context.getFileDocument(item.absolutePath, mConfig.treeUri)
|
||||||
|
document.delete()
|
||||||
|
} else {
|
||||||
|
item.delete()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private val undoDeletion = View.OnClickListener {
|
||||||
|
mToBeDeleted.clear()
|
||||||
|
mSnackbar!!.dismiss()
|
||||||
|
fillItems()
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ItemInteractionListener {
|
||||||
|
fun itemClicked(item: FileDirItem)
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue