Adds card and binder export and import.
This commit is contained in:
@@ -187,10 +187,28 @@ public class CollectionDetailFragment extends MCFragment {
|
||||
if (item.getItemId() == R.id.menu_action_add_collection_to_dashboard) {
|
||||
addCollectionToDashboard();
|
||||
return true;
|
||||
} else if (item.getItemId() == R.id.menu_action_export_collection) {
|
||||
exportCollection();
|
||||
return true;
|
||||
}
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
|
||||
private void exportCollection() {
|
||||
Collection collection = mViewModel.getCollection().getValue();
|
||||
List<Monster> monsters = mViewModel.getMonsters().getValue();
|
||||
if (collection != null) {
|
||||
String json = new com.majinnaibu.monstercards.exporters.BinderExporter().exportBinder(
|
||||
collection.name,
|
||||
monsters != null ? monsters : new java.util.ArrayList<>()
|
||||
);
|
||||
String collectionName = (collection.name != null && !collection.name.trim().isEmpty())
|
||||
? collection.name.trim()
|
||||
: "collection";
|
||||
exportToFile(collectionName + ".binder", json);
|
||||
}
|
||||
}
|
||||
|
||||
private void addCollectionToDashboard() {
|
||||
MonsterRepository repository = getMonsterRepository();
|
||||
mDisposables.add(repository.addCollectionToDashboard(mCollectionId)
|
||||
|
||||
@@ -274,10 +274,25 @@ public class DashboardFragment extends MCFragment {
|
||||
} else if (item.getItemId() == R.id.menu_action_clear_dashboard) {
|
||||
clearDashboard();
|
||||
return true;
|
||||
} else if (item.getItemId() == R.id.menu_action_export_dashboard) {
|
||||
exportDashboard();
|
||||
return true;
|
||||
}
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
|
||||
private void exportDashboard() {
|
||||
mDisposables.add(getMonsterRepository().getDashboardMonsters()
|
||||
.firstOrError()
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(monsters -> {
|
||||
String json = new com.majinnaibu.monstercards.exporters.BinderExporter().exportBinder("", monsters);
|
||||
String fileName = getString(R.string.default_filename_dashboard) + ".binder";
|
||||
exportToFile(fileName, json);
|
||||
}, Logger::logError));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroyView() {
|
||||
super.onDestroyView();
|
||||
|
||||
@@ -70,10 +70,25 @@ public class LibraryFragment extends MCFragment {
|
||||
if (item.getItemId() == R.id.menu_action_import_from_url) {
|
||||
showImportUrlDialog();
|
||||
return true;
|
||||
} else if (item.getItemId() == R.id.menu_action_export_library) {
|
||||
exportLibrary();
|
||||
return true;
|
||||
}
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
|
||||
private void exportLibrary() {
|
||||
getMonsterRepository().getMonsters()
|
||||
.firstOrError()
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(monsters -> {
|
||||
String json = new com.majinnaibu.monstercards.exporters.BinderExporter().exportBinder("", monsters);
|
||||
String fileName = getString(R.string.default_filename_library) + ".binder";
|
||||
exportToFile(fileName, json);
|
||||
}, Logger::logError);
|
||||
}
|
||||
|
||||
private void showImportUrlDialog() {
|
||||
Context context = requireContext();
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(context);
|
||||
|
||||
@@ -195,10 +195,25 @@ public class MonsterDetailFragment extends MCFragment {
|
||||
} else if (item.getItemId() == R.id.menu_action_share_monster) {
|
||||
shareCurrentMonster();
|
||||
return true;
|
||||
} else if (item.getItemId() == R.id.menu_action_export_card) {
|
||||
exportCurrentMonsterCard();
|
||||
return true;
|
||||
}
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
|
||||
private void exportCurrentMonsterCard() {
|
||||
Monster monster = mViewModel.getMonster();
|
||||
if (monster == null) {
|
||||
return;
|
||||
}
|
||||
String jsonCard = new com.majinnaibu.monstercards.exporters.MonsterCardExporter().exportCard(monster);
|
||||
String safeName = (monster.name != null && !monster.name.trim().isEmpty())
|
||||
? monster.name.trim()
|
||||
: "monster";
|
||||
exportToFile(safeName + ".card", jsonCard);
|
||||
}
|
||||
|
||||
private void shareCurrentMonster() {
|
||||
Monster monster = mViewModel.getMonster();
|
||||
if (monster == null) {
|
||||
|
||||
@@ -1,15 +1,53 @@
|
||||
package com.majinnaibu.monstercards.ui.shared;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.database.Cursor;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.provider.DocumentsContract;
|
||||
import android.provider.OpenableColumns;
|
||||
import android.view.View;
|
||||
|
||||
import androidx.activity.result.ActivityResultLauncher;
|
||||
import androidx.activity.result.contract.ActivityResultContracts;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.appcompat.app.ActionBar;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.fragment.app.Fragment;
|
||||
|
||||
import com.google.android.material.snackbar.Snackbar;
|
||||
import com.majinnaibu.monstercards.MonsterCardsApplication;
|
||||
import com.majinnaibu.monstercards.R;
|
||||
import com.majinnaibu.monstercards.data.MonsterRepository;
|
||||
import com.majinnaibu.monstercards.utils.Logger;
|
||||
|
||||
import java.io.OutputStream;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
public class MCFragment extends Fragment {
|
||||
private String mPendingExportContent;
|
||||
private ActivityResultLauncher<Intent> mCreateDocumentLauncher;
|
||||
|
||||
@Override
|
||||
public void onCreate(@Nullable Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
mCreateDocumentLauncher = registerForActivityResult(
|
||||
new ActivityResultContracts.StartActivityForResult(),
|
||||
result -> {
|
||||
if (result.getResultCode() == Activity.RESULT_OK && result.getData() != null) {
|
||||
Uri uri = result.getData().getData();
|
||||
if (uri != null && mPendingExportContent != null) {
|
||||
writeContentToUri(uri, mPendingExportContent);
|
||||
}
|
||||
}
|
||||
mPendingExportContent = null;
|
||||
});
|
||||
}
|
||||
|
||||
public MonsterCardsApplication getApplication() {
|
||||
return (MonsterCardsApplication) requireActivity().getApplication();
|
||||
}
|
||||
@@ -32,4 +70,76 @@ public class MCFragment extends Fragment {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void exportToFile(@NonNull String defaultFileName, @NonNull String content) {
|
||||
mPendingExportContent = content;
|
||||
Intent intent = new Intent(Intent.ACTION_CREATE_DOCUMENT);
|
||||
intent.addCategory(Intent.CATEGORY_OPENABLE);
|
||||
intent.setType("*/*");
|
||||
intent.putExtra(Intent.EXTRA_TITLE, defaultFileName);
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
Uri downloadsUri = Uri.parse("content://com.android.externalstorage.documents/document/primary%3ADownload");
|
||||
intent.putExtra(DocumentsContract.EXTRA_INITIAL_URI, downloadsUri);
|
||||
}
|
||||
try {
|
||||
mCreateDocumentLauncher.launch(intent);
|
||||
} catch (Exception e) {
|
||||
Logger.logError("Failed to launch document creation picker", e);
|
||||
View view = getView();
|
||||
if (view != null) {
|
||||
Snackbar.make(view, R.string.snackbar_export_failed, Snackbar.LENGTH_LONG).show();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void writeContentToUri(@NonNull Uri uri, @NonNull String content) {
|
||||
Context context = getContext();
|
||||
if (context == null) return;
|
||||
try (OutputStream out = context.getContentResolver().openOutputStream(uri)) {
|
||||
if (out != null) {
|
||||
out.write(content.getBytes(StandardCharsets.UTF_8));
|
||||
out.flush();
|
||||
View view = getView();
|
||||
if (view != null) {
|
||||
String fileName = getFileNameFromUri(context, uri);
|
||||
String message = getString(R.string.snackbar_export_success, fileName);
|
||||
Snackbar.make(view, message, Snackbar.LENGTH_LONG).show();
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Logger.logError("Failed to write export content to URI", e);
|
||||
View view = getView();
|
||||
if (view != null) {
|
||||
Snackbar.make(view, R.string.snackbar_export_failed, Snackbar.LENGTH_LONG).show();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private String getFileNameFromUri(@NonNull Context context, @NonNull Uri uri) {
|
||||
String displayName = null;
|
||||
if ("content".equals(uri.getScheme())) {
|
||||
try (Cursor cursor = context.getContentResolver().query(uri, new String[]{OpenableColumns.DISPLAY_NAME}, null, null, null)) {
|
||||
if (cursor != null && cursor.moveToFirst()) {
|
||||
int nameIndex = cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME);
|
||||
if (nameIndex != -1) {
|
||||
displayName = cursor.getString(nameIndex);
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Logger.logError("Error querying display name from content URI", e);
|
||||
}
|
||||
}
|
||||
return displayName != null ? displayName : defaultFileNameFromUri(uri);
|
||||
}
|
||||
|
||||
private String defaultFileNameFromUri(@NonNull Uri uri) {
|
||||
String path = uri.getPath();
|
||||
if (path != null) {
|
||||
int lastSlash = path.lastIndexOf('/');
|
||||
if (lastSlash >= 0 && lastSlash < path.length() - 1) {
|
||||
return path.substring(lastSlash + 1);
|
||||
}
|
||||
}
|
||||
return uri.toString();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user