|
|
|
|
@ -3,14 +3,31 @@ package net.micode.notes.ui;
|
|
|
|
|
import android.app.AlertDialog;
|
|
|
|
|
import android.content.BroadcastReceiver;
|
|
|
|
|
import android.content.Context;
|
|
|
|
|
import android.content.DialogInterface;
|
|
|
|
|
import android.content.Intent;
|
|
|
|
|
import android.content.IntentFilter;
|
|
|
|
|
import android.graphics.Bitmap;
|
|
|
|
|
import android.graphics.Canvas;
|
|
|
|
|
import android.graphics.Color;
|
|
|
|
|
import android.net.Uri;
|
|
|
|
|
import android.os.Bundle;
|
|
|
|
|
import android.text.TextUtils;
|
|
|
|
|
import android.util.Log;
|
|
|
|
|
import android.view.View;
|
|
|
|
|
import android.widget.CheckBox;
|
|
|
|
|
import android.widget.EditText;
|
|
|
|
|
import android.widget.LinearLayout;
|
|
|
|
|
import android.widget.RadioButton;
|
|
|
|
|
import android.widget.RadioGroup;
|
|
|
|
|
import android.widget.Toast;
|
|
|
|
|
|
|
|
|
|
import java.io.File;
|
|
|
|
|
import java.util.ArrayList;
|
|
|
|
|
import java.util.List;
|
|
|
|
|
|
|
|
|
|
import net.micode.notes.tool.ImageExportHelper;
|
|
|
|
|
import net.micode.notes.tool.PdfExportHelper;
|
|
|
|
|
|
|
|
|
|
import androidx.annotation.NonNull;
|
|
|
|
|
import androidx.appcompat.app.AppCompatActivity;
|
|
|
|
|
import androidx.core.view.GravityCompat;
|
|
|
|
|
@ -24,6 +41,7 @@ import net.micode.notes.data.Notes;
|
|
|
|
|
import net.micode.notes.data.NotesRepository;
|
|
|
|
|
import net.micode.notes.databinding.ActivityHomeBinding;
|
|
|
|
|
import net.micode.notes.sync.SyncManager;
|
|
|
|
|
import net.micode.notes.tool.BackupUtils;
|
|
|
|
|
import net.micode.notes.tool.SecurityManager;
|
|
|
|
|
import net.micode.notes.viewmodel.NotesListViewModel;
|
|
|
|
|
|
|
|
|
|
@ -138,6 +156,11 @@ public class NotesListActivity extends BaseActivity implements SidebarFragment.O
|
|
|
|
|
viewModel.setIsSelectionMode(false);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
binding.btnActionExport.setOnClickListener(v -> {
|
|
|
|
|
exportSelectedNotes();
|
|
|
|
|
viewModel.setIsSelectionMode(false);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
binding.btnActionLock.setOnClickListener(v -> {
|
|
|
|
|
SecurityManager securityManager = SecurityManager.getInstance(this);
|
|
|
|
|
if (!securityManager.isPasswordSet()) {
|
|
|
|
|
@ -375,7 +398,11 @@ public class NotesListActivity extends BaseActivity implements SidebarFragment.O
|
|
|
|
|
@Override public void onSyncSelected() { binding.drawerLayout.closeDrawer(GravityCompat.START); SyncManager.getInstance().syncNotes(null); }
|
|
|
|
|
@Override public void onLoginSelected() { binding.drawerLayout.closeDrawer(GravityCompat.START); startActivity(new Intent(this, LoginActivity.class)); }
|
|
|
|
|
@Override public void onLogoutSelected() { binding.drawerLayout.closeDrawer(GravityCompat.START); viewModel.refreshNotes(); }
|
|
|
|
|
@Override public void onExportSelected() { binding.drawerLayout.closeDrawer(GravityCompat.START); Toast.makeText(this, "导出功能待实现", Toast.LENGTH_SHORT).show(); }
|
|
|
|
|
@Override public void onExportSelected() {
|
|
|
|
|
binding.drawerLayout.closeDrawer(GravityCompat.START);
|
|
|
|
|
viewModel.setIsSelectionMode(true);
|
|
|
|
|
Toast.makeText(this, "请选择要导出的便签", Toast.LENGTH_SHORT).show();
|
|
|
|
|
}
|
|
|
|
|
@Override public void onTemplateSelected() { binding.drawerLayout.closeDrawer(GravityCompat.START); viewModel.enterFolder(Notes.ID_TEMPLATE_FOLDER); }
|
|
|
|
|
@Override public void onSettingsSelected() { binding.drawerLayout.closeDrawer(GravityCompat.START); startActivity(new Intent(this, SettingsActivity.class)); }
|
|
|
|
|
@Override public void onCapsuleSelected() {
|
|
|
|
|
@ -410,6 +437,176 @@ public class NotesListActivity extends BaseActivity implements SidebarFragment.O
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void exportSelectedNotes() {
|
|
|
|
|
java.util.List<Long> selectedIds = viewModel.getSelectedNoteIds();
|
|
|
|
|
if (selectedIds.isEmpty()) {
|
|
|
|
|
Toast.makeText(this, "请先选择要导出的便签", Toast.LENGTH_SHORT).show();
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
AlertDialog.Builder builder = new AlertDialog.Builder(this);
|
|
|
|
|
builder.setTitle("批量导出便签");
|
|
|
|
|
|
|
|
|
|
LinearLayout layout = new LinearLayout(this);
|
|
|
|
|
layout.setOrientation(LinearLayout.VERTICAL);
|
|
|
|
|
layout.setPadding(50, 20, 50, 20);
|
|
|
|
|
|
|
|
|
|
final RadioGroup group = new RadioGroup(this);
|
|
|
|
|
RadioButton rbText = new RadioButton(this);
|
|
|
|
|
rbText.setText("导出为文本 (.txt)");
|
|
|
|
|
rbText.setId(View.generateViewId());
|
|
|
|
|
group.addView(rbText);
|
|
|
|
|
|
|
|
|
|
RadioButton rbImage = new RadioButton(this);
|
|
|
|
|
rbImage.setText("导出为图片 (.png)");
|
|
|
|
|
rbImage.setId(View.generateViewId());
|
|
|
|
|
group.addView(rbImage);
|
|
|
|
|
|
|
|
|
|
RadioButton rbPdf = new RadioButton(this);
|
|
|
|
|
rbPdf.setText("导出为 PDF (.pdf)");
|
|
|
|
|
rbPdf.setId(View.generateViewId());
|
|
|
|
|
group.addView(rbPdf);
|
|
|
|
|
|
|
|
|
|
group.check(rbText.getId());
|
|
|
|
|
layout.addView(group);
|
|
|
|
|
|
|
|
|
|
final CheckBox cbShare = new CheckBox(this);
|
|
|
|
|
cbShare.setText("导出后立即分享");
|
|
|
|
|
layout.addView(cbShare);
|
|
|
|
|
|
|
|
|
|
builder.setView(layout);
|
|
|
|
|
|
|
|
|
|
builder.setPositiveButton("开始导出", (dialog, which) -> {
|
|
|
|
|
int checkedId = group.getCheckedRadioButtonId();
|
|
|
|
|
boolean share = cbShare.isChecked();
|
|
|
|
|
|
|
|
|
|
if (checkedId == rbText.getId()) {
|
|
|
|
|
performBatchExport(0, selectedIds, share);
|
|
|
|
|
} else if (checkedId == rbImage.getId()) {
|
|
|
|
|
performBatchExport(1, selectedIds, share);
|
|
|
|
|
} else if (checkedId == rbPdf.getId()) {
|
|
|
|
|
performBatchExport(2, selectedIds, share);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
builder.setNegativeButton("取消", null);
|
|
|
|
|
builder.show();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void performBatchExport(int format, java.util.List<Long> selectedIds, boolean share) {
|
|
|
|
|
if (format == 0) { // Text (Combined)
|
|
|
|
|
BackupUtils backupUtils = BackupUtils.getInstance(this);
|
|
|
|
|
int state = backupUtils.exportNotesToText(selectedIds);
|
|
|
|
|
if (state == BackupUtils.STATE_SUCCESS) {
|
|
|
|
|
File file = new File(backupUtils.getExportedTextFileDir(), backupUtils.getExportedTextFileName());
|
|
|
|
|
Toast.makeText(this, "已导出至下载目录: " + file.getName(), Toast.LENGTH_SHORT).show();
|
|
|
|
|
if (share) shareFile(file, "text/plain");
|
|
|
|
|
} else {
|
|
|
|
|
Toast.makeText(this, "导出失败", Toast.LENGTH_SHORT).show();
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
final ArrayList<Uri> uris = new ArrayList<>();
|
|
|
|
|
final int total = selectedIds.size();
|
|
|
|
|
final int[] successCount = {0};
|
|
|
|
|
final String mimeType = (format == 1) ? "image/png" : "application/pdf";
|
|
|
|
|
|
|
|
|
|
for (Long id : selectedIds) {
|
|
|
|
|
viewModel.getNoteContent(id, new NotesRepository.Callback<String>() {
|
|
|
|
|
@Override
|
|
|
|
|
public void onSuccess(String content) {
|
|
|
|
|
if (!TextUtils.isEmpty(content)) {
|
|
|
|
|
String title = getTitleFromContent(content);
|
|
|
|
|
File exportedFile = null;
|
|
|
|
|
if (format == 1) { // Image
|
|
|
|
|
Bitmap bitmap = renderTextToBitmap(content);
|
|
|
|
|
Uri uri = ImageExportHelper.saveBitmapToExternal(NotesListActivity.this, bitmap, title);
|
|
|
|
|
if (uri != null) {
|
|
|
|
|
successCount[0]++;
|
|
|
|
|
uris.add(uri);
|
|
|
|
|
}
|
|
|
|
|
} else { // PDF
|
|
|
|
|
exportedFile = PdfExportHelper.exportToPdf(NotesListActivity.this, title, content);
|
|
|
|
|
if (exportedFile != null) {
|
|
|
|
|
successCount[0]++;
|
|
|
|
|
uris.add(androidx.core.content.FileProvider.getUriForFile(NotesListActivity.this, getPackageName() + ".fileprovider", exportedFile));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
checkBatchFinished(successCount[0], total, uris, mimeType, share);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public void onError(Exception error) {
|
|
|
|
|
checkBatchFinished(successCount[0], total, uris, mimeType, share);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private Bitmap renderTextToBitmap(String text) {
|
|
|
|
|
android.widget.TextView textView = new android.widget.TextView(this);
|
|
|
|
|
textView.setText(text);
|
|
|
|
|
textView.setTextColor(Color.BLACK);
|
|
|
|
|
textView.setBackgroundColor(Color.WHITE);
|
|
|
|
|
textView.setTextSize(16);
|
|
|
|
|
textView.setPadding(40, 40, 40, 40);
|
|
|
|
|
textView.setWidth(800);
|
|
|
|
|
|
|
|
|
|
int widthMeasureSpec = View.MeasureSpec.makeMeasureSpec(800, View.MeasureSpec.EXACTLY);
|
|
|
|
|
int heightMeasureSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
|
|
|
|
|
textView.measure(widthMeasureSpec, heightMeasureSpec);
|
|
|
|
|
textView.layout(0, 0, textView.getMeasuredWidth(), textView.getMeasuredHeight());
|
|
|
|
|
|
|
|
|
|
Bitmap bitmap = Bitmap.createBitmap(textView.getMeasuredWidth(), textView.getMeasuredHeight(), Bitmap.Config.ARGB_8888);
|
|
|
|
|
Canvas canvas = new Canvas(bitmap);
|
|
|
|
|
textView.draw(canvas);
|
|
|
|
|
return bitmap;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private synchronized void checkBatchFinished(int success, int total, ArrayList<Uri> uris, String mimeType, boolean share) {
|
|
|
|
|
// Since we are doing this sequentially or with callbacks, we need to track progress
|
|
|
|
|
// For simplicity in this implementation, I'll just check if we have reached total count
|
|
|
|
|
if (uris.size() + (total - success) >= total) {
|
|
|
|
|
if (success > 0) {
|
|
|
|
|
Toast.makeText(this, "成功导出 " + success + " 个文件至下载目录", Toast.LENGTH_SHORT).show();
|
|
|
|
|
if (share) shareUris(uris, mimeType);
|
|
|
|
|
} else {
|
|
|
|
|
Toast.makeText(this, "导出失败", Toast.LENGTH_SHORT).show();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private String getTitleFromContent(String content) {
|
|
|
|
|
if (TextUtils.isEmpty(content)) return "untitled";
|
|
|
|
|
String title = content.trim();
|
|
|
|
|
int firstNewLine = title.indexOf('\n');
|
|
|
|
|
if (firstNewLine > 0) {
|
|
|
|
|
title = title.substring(0, firstNewLine);
|
|
|
|
|
}
|
|
|
|
|
if (title.length() > 30) {
|
|
|
|
|
title = title.substring(0, 30);
|
|
|
|
|
}
|
|
|
|
|
return title;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void shareFile(File file, String mimeType) {
|
|
|
|
|
Uri uri = androidx.core.content.FileProvider.getUriForFile(this, getPackageName() + ".fileprovider", file);
|
|
|
|
|
Intent intent = new Intent(Intent.ACTION_SEND);
|
|
|
|
|
intent.setType(mimeType);
|
|
|
|
|
intent.putExtra(Intent.EXTRA_STREAM, uri);
|
|
|
|
|
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
|
|
|
|
|
startActivity(Intent.createChooser(intent, "分享便签"));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void shareUris(ArrayList<Uri> uris, String mimeType) {
|
|
|
|
|
Intent intent = new Intent(Intent.ACTION_SEND_MULTIPLE);
|
|
|
|
|
intent.setType(mimeType);
|
|
|
|
|
intent.putParcelableArrayListExtra(Intent.EXTRA_STREAM, uris);
|
|
|
|
|
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
|
|
|
|
|
startActivity(Intent.createChooser(intent, "分享便签"));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void showRenameFolderDialog(long folderId, String currentName) {
|
|
|
|
|
final EditText input = new EditText(this);
|
|
|
|
|
input.setText(currentName);
|
|
|
|
|
|