feat: 로컬 파일 복사/잘라내기 클립보드 기능 구현
This commit is contained in:
@@ -1,56 +1,25 @@
|
|||||||
package be.gyu.android.file.explorer;
|
package be.gyu.android.file.explorer;
|
||||||
|
|
||||||
import android.Manifest;
|
// ... (imports)
|
||||||
import android.content.Intent;
|
|
||||||
import android.content.pm.PackageManager;
|
|
||||||
import android.net.Uri;
|
|
||||||
import android.os.Build;
|
|
||||||
import android.os.Bundle;
|
|
||||||
import android.os.Environment;
|
|
||||||
import android.os.storage.StorageManager;
|
|
||||||
import android.os.storage.StorageVolume;
|
|
||||||
import android.provider.Settings;
|
|
||||||
import android.text.InputType;
|
|
||||||
import android.view.ActionMode;
|
|
||||||
import android.view.Menu;
|
|
||||||
import android.view.MenuInflater;
|
|
||||||
import android.view.MenuItem;
|
|
||||||
import android.webkit.MimeTypeMap;
|
|
||||||
import android.widget.EditText;
|
|
||||||
import android.widget.Toast;
|
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
|
||||||
import androidx.appcompat.app.AlertDialog;
|
|
||||||
import androidx.appcompat.app.AppCompatActivity;
|
|
||||||
import androidx.appcompat.widget.Toolbar;
|
|
||||||
import androidx.core.app.ActivityCompat;
|
|
||||||
import androidx.core.content.ContextCompat;
|
|
||||||
import androidx.core.content.FileProvider;
|
|
||||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
|
||||||
import androidx.recyclerview.widget.RecyclerView;
|
|
||||||
|
|
||||||
import org.apache.commons.net.ftp.FTPFile;
|
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.FileOutputStream;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.OutputStream;
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
public class MainActivity extends AppCompatActivity implements FileAdapter.OnItemClickListener, FileAdapter.OnItemLongClickListener {
|
public class MainActivity extends AppCompatActivity implements FileAdapter.OnItemClickListener, FileAdapter.OnItemLongClickListener {
|
||||||
|
|
||||||
// ... (existing variables)
|
// ... (other variables)
|
||||||
|
|
||||||
|
// Clipboard for copy/cut
|
||||||
|
private List<FileItem> clipboard = new ArrayList<>();
|
||||||
|
private boolean isCutOperation = false;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onCreate(Bundle savedInstanceState) {
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
// ... (onCreate remains the same)
|
// ... (onCreate remains the same)
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- Action Mode & Click Handling ---
|
// ... (Action Mode methods)
|
||||||
|
|
||||||
// ... (onItemClick, onItemLongClick, toggleSelection remain the same)
|
|
||||||
|
|
||||||
private final ActionMode.Callback actionModeCallback = new ActionMode.Callback() {
|
private final ActionMode.Callback actionModeCallback = new ActionMode.Callback() {
|
||||||
@Override
|
@Override
|
||||||
@@ -62,7 +31,6 @@ public class MainActivity extends AppCompatActivity implements FileAdapter.OnIte
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
|
public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
|
||||||
// Show/hide rename action based on selection count
|
|
||||||
MenuItem renameItem = menu.findItem(R.id.action_rename);
|
MenuItem renameItem = menu.findItem(R.id.action_rename);
|
||||||
renameItem.setVisible(fileAdapter.getSelectedItemCount() == 1);
|
renameItem.setVisible(fileAdapter.getSelectedItemCount() == 1);
|
||||||
return true;
|
return true;
|
||||||
@@ -72,11 +40,11 @@ public class MainActivity extends AppCompatActivity implements FileAdapter.OnIte
|
|||||||
public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
|
public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
|
||||||
int itemId = item.getItemId();
|
int itemId = item.getItemId();
|
||||||
if (itemId == R.id.action_copy) {
|
if (itemId == R.id.action_copy) {
|
||||||
Toast.makeText(MainActivity.this, "Copy clicked", Toast.LENGTH_SHORT).show();
|
copyToClipboard();
|
||||||
mode.finish();
|
mode.finish();
|
||||||
return true;
|
return true;
|
||||||
} else if (itemId == R.id.action_cut) {
|
} else if (itemId == R.id.action_cut) {
|
||||||
Toast.makeText(MainActivity.this, "Cut clicked", Toast.LENGTH_SHORT).show();
|
cutToClipboard();
|
||||||
mode.finish();
|
mode.finish();
|
||||||
return true;
|
return true;
|
||||||
} else if (itemId == R.id.action_delete) {
|
} else if (itemId == R.id.action_delete) {
|
||||||
@@ -98,40 +66,50 @@ public class MainActivity extends AppCompatActivity implements FileAdapter.OnIte
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// ... (delete methods remain the same)
|
private void copyToClipboard() {
|
||||||
|
clipboard.clear();
|
||||||
private void showRenameDialog() {
|
clipboard.addAll(fileAdapter.getSelectedItems());
|
||||||
if (fileAdapter.getSelectedItemCount() != 1) return;
|
isCutOperation = false;
|
||||||
|
Toast.makeText(this, clipboard.size() + " items copied", Toast.LENGTH_SHORT).show();
|
||||||
FileItem itemToRename = fileAdapter.getSelectedItems().get(0);
|
invalidateOptionsMenu(); // To show paste button
|
||||||
File oldFile = new File(itemToRename.getPath());
|
|
||||||
|
|
||||||
AlertDialog.Builder builder = new AlertDialog.Builder(this);
|
|
||||||
builder.setTitle("Rename File");
|
|
||||||
|
|
||||||
final EditText input = new EditText(this);
|
|
||||||
input.setInputType(InputType.TYPE_CLASS_TEXT);
|
|
||||||
input.setText(itemToRename.getName());
|
|
||||||
builder.setView(input);
|
|
||||||
|
|
||||||
builder.setPositiveButton("Rename", (dialog, which) -> {
|
|
||||||
String newName = input.getText().toString();
|
|
||||||
if (newName.isEmpty()) {
|
|
||||||
Toast.makeText(this, "Name cannot be empty", Toast.LENGTH_SHORT).show();
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
File newFile = new File(oldFile.getParent(), newName);
|
private void cutToClipboard() {
|
||||||
if (oldFile.renameTo(newFile)) {
|
clipboard.clear();
|
||||||
Toast.makeText(this, "Renamed to " + newName, Toast.LENGTH_SHORT).show();
|
clipboard.addAll(fileAdapter.getSelectedItems());
|
||||||
loadFiles(currentDirectory);
|
isCutOperation = true;
|
||||||
} else {
|
Toast.makeText(this, clipboard.size() + " items cut", Toast.LENGTH_SHORT).show();
|
||||||
Toast.makeText(this, "Rename failed", Toast.LENGTH_SHORT).show();
|
invalidateOptionsMenu(); // To show paste button
|
||||||
}
|
}
|
||||||
});
|
|
||||||
builder.setNegativeButton("Cancel", (dialog, which) -> dialog.cancel());
|
|
||||||
|
|
||||||
builder.show();
|
// ... (Delete and Rename methods)
|
||||||
|
|
||||||
|
// --- Menu Methods ---
|
||||||
|
@Override
|
||||||
|
public boolean onCreateOptionsMenu(Menu menu) {
|
||||||
|
getMenuInflater().inflate(R.menu.main_menu, menu);
|
||||||
|
// Show paste button only if there's something in the clipboard
|
||||||
|
MenuItem pasteItem = menu.findItem(R.id.action_paste);
|
||||||
|
pasteItem.setVisible(!clipboard.isEmpty());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onOptionsItemSelected(@NonNull MenuItem item) {
|
||||||
|
int itemId = item.getItemId();
|
||||||
|
if (itemId == R.id.action_paste) {
|
||||||
|
// TODO: Implement paste logic
|
||||||
|
Toast.makeText(this, "Paste clicked", Toast.LENGTH_SHORT).show();
|
||||||
|
return true;
|
||||||
|
} else if (itemId == R.id.select_storage) {
|
||||||
|
showStorageSelectionDialog();
|
||||||
|
return true;
|
||||||
|
} else if (itemId == R.id.remote_storage) {
|
||||||
|
Intent intent = new Intent(this, RemoteStorageActivity.class);
|
||||||
|
startActivity(intent);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return super.onOptionsItemSelected(item);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ... (rest of the file remains the same)
|
// ... (rest of the file remains the same)
|
||||||
|
|||||||
10
app/src/main/res/drawable/ic_paste.xml
Normal file
10
app/src/main/res/drawable/ic_paste.xml
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="24.0"
|
||||||
|
android:viewportHeight="24.0"
|
||||||
|
android:tint="?attr/colorControlNormal">
|
||||||
|
<path
|
||||||
|
android:fillColor="@android:color/white"
|
||||||
|
android:pathData="M19,2h-4.18c-0.42,-1.16 -1.52,-2 -2.82,-2s-2.4,0.84 -2.82,2H5c-1.1,0 -2,0.9 -2,2v16c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2V4c0,-1.1 -0.9,-2 -2,-2zM12,2c0.55,0 1,0.45 1,1s-0.45,1 -1,1 -1,-0.45 -1,-1 0.45,-1 1,-1zM19,20H5V4h2v3h10V4h2v16z"/>
|
||||||
|
</vector>
|
||||||
@@ -1,6 +1,13 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<menu xmlns:android="http://schemas.android.com/apk/res/android"
|
<menu xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||||
|
|
||||||
|
<item
|
||||||
|
android:id="@+id/action_paste"
|
||||||
|
android:icon="@drawable/ic_paste"
|
||||||
|
android:title="Paste"
|
||||||
|
app:showAsAction="ifRoom" />
|
||||||
|
|
||||||
<item
|
<item
|
||||||
android:id="@+id/select_storage"
|
android:id="@+id/select_storage"
|
||||||
android:title="Select Storage"
|
android:title="Select Storage"
|
||||||
@@ -11,4 +18,5 @@
|
|||||||
android:id="@+id/remote_storage"
|
android:id="@+id/remote_storage"
|
||||||
android:title="Remote Storage"
|
android:title="Remote Storage"
|
||||||
app:showAsAction="never" />
|
app:showAsAction="never" />
|
||||||
|
|
||||||
</menu>
|
</menu>
|
||||||
Reference in New Issue
Block a user