diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 1680ef09..8c8b218f 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -17,10 +17,10 @@ android:allowBackup="true" android:dataExtractionRules="@xml/data_extraction_rules" android:fullBackupContent="@xml/backup_rules" - android:icon="@mipmap/ic_launcher_temp_round" + android:icon="@mipmap/ic_launcher_final" android:name=".LacertaApplication" android:label="@string/app_name" - android:roundIcon="@mipmap/ic_launcher_round" + android:roundIcon="@mipmap/ic_launcher_final_round" android:supportsRtl="true" android:theme="@style/Theme.Lacerta" tools:targetApi="31"> diff --git a/app/src/main/ic_launcher_final-playstore.png b/app/src/main/ic_launcher_final-playstore.png new file mode 100644 index 00000000..5156b4c2 Binary files /dev/null and b/app/src/main/ic_launcher_final-playstore.png differ diff --git a/app/src/main/res/drawable/ic_launcher_final_foreground.xml b/app/src/main/res/drawable/ic_launcher_final_foreground.xml new file mode 100644 index 00000000..93b08672 --- /dev/null +++ b/app/src/main/res/drawable/ic_launcher_final_foreground.xml @@ -0,0 +1,15 @@ + + + + + diff --git a/app/src/main/res/mipmap-anydpi-v26/ic_launcher_final.xml b/app/src/main/res/mipmap-anydpi-v26/ic_launcher_final.xml new file mode 100644 index 00000000..714984ea --- /dev/null +++ b/app/src/main/res/mipmap-anydpi-v26/ic_launcher_final.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/mipmap-anydpi-v26/ic_launcher_final_round.xml b/app/src/main/res/mipmap-anydpi-v26/ic_launcher_final_round.xml new file mode 100644 index 00000000..714984ea --- /dev/null +++ b/app/src/main/res/mipmap-anydpi-v26/ic_launcher_final_round.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher_final.webp b/app/src/main/res/mipmap-hdpi/ic_launcher_final.webp new file mode 100644 index 00000000..9892389a Binary files /dev/null and b/app/src/main/res/mipmap-hdpi/ic_launcher_final.webp differ diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher_final_round.webp b/app/src/main/res/mipmap-hdpi/ic_launcher_final_round.webp new file mode 100644 index 00000000..260cfa75 Binary files /dev/null and b/app/src/main/res/mipmap-hdpi/ic_launcher_final_round.webp differ diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher_final.webp b/app/src/main/res/mipmap-mdpi/ic_launcher_final.webp new file mode 100644 index 00000000..e1380336 Binary files /dev/null and b/app/src/main/res/mipmap-mdpi/ic_launcher_final.webp differ diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher_final_round.webp b/app/src/main/res/mipmap-mdpi/ic_launcher_final_round.webp new file mode 100644 index 00000000..8d5ddfcf Binary files /dev/null and b/app/src/main/res/mipmap-mdpi/ic_launcher_final_round.webp differ diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher_final.webp b/app/src/main/res/mipmap-xhdpi/ic_launcher_final.webp new file mode 100644 index 00000000..eff542cf Binary files /dev/null and b/app/src/main/res/mipmap-xhdpi/ic_launcher_final.webp differ diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher_final_round.webp b/app/src/main/res/mipmap-xhdpi/ic_launcher_final_round.webp new file mode 100644 index 00000000..a326e6f7 Binary files /dev/null and b/app/src/main/res/mipmap-xhdpi/ic_launcher_final_round.webp differ diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher_final.webp b/app/src/main/res/mipmap-xxhdpi/ic_launcher_final.webp new file mode 100644 index 00000000..355b7e54 Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/ic_launcher_final.webp differ diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher_final_round.webp b/app/src/main/res/mipmap-xxhdpi/ic_launcher_final_round.webp new file mode 100644 index 00000000..56f72a24 Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/ic_launcher_final_round.webp differ diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher_final.webp b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_final.webp new file mode 100644 index 00000000..c3e96b0b Binary files /dev/null and b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_final.webp differ diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher_final_round.webp b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_final_round.webp new file mode 100644 index 00000000..1349a293 Binary files /dev/null and b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_final_round.webp differ diff --git a/app/src/main/res/values/ic_launcher_final_background.xml b/app/src/main/res/values/ic_launcher_final_background.xml new file mode 100644 index 00000000..579d66a9 --- /dev/null +++ b/app/src/main/res/values/ic_launcher_final_background.xml @@ -0,0 +1,4 @@ + + + #B8D3C3 + \ No newline at end of file diff --git a/component/common/src/main/java/one/nem/lacerta/component/common/picker/LacertaFilePickerAdapter.java b/component/common/src/main/java/one/nem/lacerta/component/common/picker/LacertaFilePickerAdapter.java index fa0163d2..29fd3eec 100644 --- a/component/common/src/main/java/one/nem/lacerta/component/common/picker/LacertaFilePickerAdapter.java +++ b/component/common/src/main/java/one/nem/lacerta/component/common/picker/LacertaFilePickerAdapter.java @@ -1,5 +1,7 @@ package one.nem.lacerta.component.common.picker; +import com.google.android.material.dialog.MaterialAlertDialogBuilder; + import one.nem.lacerta.component.common.picker.base.LacertaFilePickerAdapterBase; import one.nem.lacerta.model.ListItem; import one.nem.lacerta.model.ListItemType; @@ -9,6 +11,7 @@ public class LacertaFilePickerAdapter extends LacertaFilePickerAdapterBase { // Listener public interface LacertaFilePickerAdapterListener extends LacertaFilePickerAdapterBase.LacertaFilePickerAdapterListener { void onDocumentSelected(String documentId); + void onCombinedDocumentSelected(String documentId); } // Variables @@ -24,10 +27,16 @@ public class LacertaFilePickerAdapter extends LacertaFilePickerAdapterBase { public void onBindViewHolder(LacertaFilePickerViewHolder holder, int position) { super.onBindViewHolder(holder, position); if (libraryItemPage.getListItems().get(position).getItemType() == ListItemType.ITEM_TYPE_DOCUMENT) { - holder.itemView.setOnClickListener(v -> { - ListItem listItem = libraryItemPage.getListItems().get(position); - listener.onDocumentSelected(listItem.getItemId()); - }); + if (libraryItemPage.getListItems().get(position).getHasCombined()) { + holder.itemView.setOnClickListener(v -> { + listener.onCombinedDocumentSelected(libraryItemPage.getListItems().get(position).getItemId()); + }); + } else { + holder.itemView.setOnClickListener(v -> { + ListItem listItem = libraryItemPage.getListItems().get(position); + listener.onDocumentSelected(listItem.getItemId()); + }); + } } } } diff --git a/component/common/src/main/java/one/nem/lacerta/component/common/picker/LacertaFilePickerDialog.java b/component/common/src/main/java/one/nem/lacerta/component/common/picker/LacertaFilePickerDialog.java index 5a082b74..09c497ce 100644 --- a/component/common/src/main/java/one/nem/lacerta/component/common/picker/LacertaFilePickerDialog.java +++ b/component/common/src/main/java/one/nem/lacerta/component/common/picker/LacertaFilePickerDialog.java @@ -76,6 +76,19 @@ public class LacertaFilePickerDialog extends LacertaFilePickerDialogBase { listener.onFileSelected(documentId, documentId); } } + + @Override + public void onCombinedDocumentSelected(String documentId) { + if (listener != null) { + LacertaFilePickerSelectDocumentDialog dialog = new LacertaFilePickerSelectDocumentDialog(); + dialog.setDocumentId(documentId); + dialog.setListener(documentId1 -> { + dismiss(); + listener.onFileSelected(documentId1, documentId1); + }); + dialog.show(getParentFragmentManager(), "select_document_dialog"); + } + } }); recyclerView.setAdapter(adapter); diff --git a/component/common/src/main/java/one/nem/lacerta/component/common/picker/LacertaFilePickerSelectDocumentAdapter.java b/component/common/src/main/java/one/nem/lacerta/component/common/picker/LacertaFilePickerSelectDocumentAdapter.java new file mode 100644 index 00000000..489d07a9 --- /dev/null +++ b/component/common/src/main/java/one/nem/lacerta/component/common/picker/LacertaFilePickerSelectDocumentAdapter.java @@ -0,0 +1,75 @@ +package one.nem.lacerta.component.common.picker; + +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ImageView; +import android.widget.TextView; + +import androidx.annotation.NonNull; +import androidx.recyclerview.widget.RecyclerView; + +import java.util.ArrayList; + +import one.nem.lacerta.component.common.R; +import one.nem.lacerta.model.ListItem; +import one.nem.lacerta.model.document.DocumentMeta; + +public class LacertaFilePickerSelectDocumentAdapter extends RecyclerView.Adapter{ + + // Listener + public interface LacertaFilePickerSelectDocumentAdapterListener { + void onDocumentSelected(String documentId); + } + + // Variables + LacertaFilePickerSelectDocumentAdapterListener listener; + + ArrayList listItems; + + // Setter + public LacertaFilePickerSelectDocumentAdapter setListener(LacertaFilePickerSelectDocumentAdapterListener listener) { + this.listener = listener; + return this; + } + + public void setListItems(ArrayList listItems) { + this.listItems = listItems; + } + + + @NonNull + @Override + public LacertaFilePickerSelectDocumentAdapter.LacertaFilePickerSelectDocumentViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { + View view = LayoutInflater.from(parent.getContext()).inflate(one.nem.lacerta.shared.ui.R.layout.common_list_item, parent, false); + return new LacertaFilePickerSelectDocumentViewHolder(view); + } + + @Override + public void onBindViewHolder(@NonNull LacertaFilePickerSelectDocumentAdapter.LacertaFilePickerSelectDocumentViewHolder holder, int position) { + ListItem listItem = listItems.get(position); + holder.title.setText(listItem.getTitle()); + holder.description.setVisibility(View.GONE); + holder.icon.setImageResource(listItem.getItemType().getIconId()); + holder.itemView.setOnClickListener(v -> listener.onDocumentSelected(listItem.getItemId())); + } + + @Override + public int getItemCount() { + return listItems == null ? 0 : listItems.size(); + } + + public class LacertaFilePickerSelectDocumentViewHolder extends RecyclerView.ViewHolder { + + ImageView icon; + TextView title; + TextView description; + + public LacertaFilePickerSelectDocumentViewHolder(@NonNull View itemView) { + super(itemView); + icon = itemView.findViewById(one.nem.lacerta.shared.ui.R.id.item_icon); + title = itemView.findViewById(one.nem.lacerta.shared.ui.R.id.item_title); + description = itemView.findViewById(one.nem.lacerta.shared.ui.R.id.item_description); + } + } +} diff --git a/component/common/src/main/java/one/nem/lacerta/component/common/picker/LacertaFilePickerSelectDocumentDialog.java b/component/common/src/main/java/one/nem/lacerta/component/common/picker/LacertaFilePickerSelectDocumentDialog.java new file mode 100644 index 00000000..9d34d457 --- /dev/null +++ b/component/common/src/main/java/one/nem/lacerta/component/common/picker/LacertaFilePickerSelectDocumentDialog.java @@ -0,0 +1,88 @@ +package one.nem.lacerta.component.common.picker; + +import android.app.Dialog; +import android.os.Bundle; +import android.view.LayoutInflater; +import android.view.View; + +import androidx.fragment.app.DialogFragment; +import androidx.recyclerview.widget.LinearLayoutManager; +import androidx.recyclerview.widget.RecyclerView; + +import com.google.android.material.dialog.MaterialAlertDialogBuilder; + +import java.util.ArrayList; + +import javax.inject.Inject; + +import dagger.hilt.android.AndroidEntryPoint; +import one.nem.lacerta.component.common.R; +import one.nem.lacerta.data.LacertaLibrary; +import one.nem.lacerta.model.ListItem; +import one.nem.lacerta.model.ListItemType; +import one.nem.lacerta.model.pref.ToxiDocumentModel; + +@AndroidEntryPoint +public class LacertaFilePickerSelectDocumentDialog extends DialogFragment { + + @Inject + LacertaLibrary lacertaLibrary; + + // Listener + public interface LacertaFilePickerSelectDocumentDialogListener { + void onDocumentSelected(String documentId); + } + + // Variables + LacertaFilePickerSelectDocumentDialogListener listener; + + String documentId; + + // Setter + public LacertaFilePickerSelectDocumentDialog setListener(LacertaFilePickerSelectDocumentDialogListener listener) { + this.listener = listener; + return this; + } + + public LacertaFilePickerSelectDocumentDialog setDocumentId(String documentId) { + this.documentId = documentId; + return this; + } + + @Override + public Dialog onCreateDialog(Bundle savedInstanceState) { + super.onCreateDialog(savedInstanceState); + + MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(getActivity()); + View view = LayoutInflater.from(getActivity()).inflate(R.layout.lacerta_dialog_select_doc, null); + + // 高さを画面の40%にする + int height = (int) (getResources().getDisplayMetrics().heightPixels * 0.4); + view.setMinimumHeight(height); + + RecyclerView recyclerView = view.findViewById(R.id.document_list_recycler_view); + + LacertaFilePickerSelectDocumentAdapter adapter = new LacertaFilePickerSelectDocumentAdapter(); + + adapter.setListener(documentId -> listener.onDocumentSelected(documentId)); + + recyclerView.setAdapter(adapter); + recyclerView.setLayoutManager(new LinearLayoutManager(getActivity())); + builder.setTitle("追加先を選択"); + + builder.setView(view); + + lacertaLibrary.getCombinedDocumentToxiList(this.documentId).thenAccept(toxiDocumentModels -> { + ArrayList listItems = new ArrayList<>(); + for (ToxiDocumentModel toxiDocumentModel : toxiDocumentModels) { + listItems.add(new ListItem(toxiDocumentModel.titleCache, null, ListItemType.ITEM_TYPE_DOCUMENT, toxiDocumentModel.childDocumentId)); + } + adapter.setListItems(listItems); + adapter.notifyDataSetChanged(); + }); + + return builder.create(); + } + + +} diff --git a/component/common/src/main/res/layout/lacerta_dialog_select_doc.xml b/component/common/src/main/res/layout/lacerta_dialog_select_doc.xml new file mode 100644 index 00000000..afd94234 --- /dev/null +++ b/component/common/src/main/res/layout/lacerta_dialog_select_doc.xml @@ -0,0 +1,12 @@ + + + + + \ No newline at end of file diff --git a/component/scanner/src/main/java/one/nem/lacerta/component/scanner/ScannerManagerActivity.java b/component/scanner/src/main/java/one/nem/lacerta/component/scanner/ScannerManagerActivity.java index c2c8ed69..50713ced 100644 --- a/component/scanner/src/main/java/one/nem/lacerta/component/scanner/ScannerManagerActivity.java +++ b/component/scanner/src/main/java/one/nem/lacerta/component/scanner/ScannerManagerActivity.java @@ -35,6 +35,8 @@ import one.nem.lacerta.data.Document; import one.nem.lacerta.data.LacertaLibrary; import one.nem.lacerta.model.document.DocumentDetail; import one.nem.lacerta.model.document.DocumentMeta; +import one.nem.lacerta.model.document.page.Page; +import one.nem.lacerta.processor.DocumentProcessor; import one.nem.lacerta.processor.factory.DocumentProcessorFactory; import one.nem.lacerta.utils.LacertaLogger; import one.nem.lacerta.vcs.factory.LacertaVcsFactory; @@ -73,7 +75,9 @@ public class ScannerManagerActivity extends AppCompatActivity { // Variables private ArrayList croppedImages = new ArrayList<>(); - private boolean single = false; + private boolean update = false; + private String documentId; + private int index = 0; View view; @@ -104,7 +108,7 @@ public class ScannerManagerActivity extends AppCompatActivity { null ); - DocumentScanner documentScannerSingle = new DocumentScanner( // TODO-rca: ひどすぎるのでなんとかする + DocumentScanner documentScannerUpdate = new DocumentScanner( // TODO-rca: ひどすぎるのでなんとかする this, (croppedImageResults) -> { logger.debug(TAG, "croppedImage size: " + croppedImageResults.size()); @@ -113,6 +117,7 @@ public class ScannerManagerActivity extends AppCompatActivity { croppedImages.add(BitmapFactory.decodeFile(result)); } processResult(croppedImages); + updatePage(); return null; }, (errorMessage) -> { @@ -160,11 +165,12 @@ public class ScannerManagerActivity extends AppCompatActivity { Intent intent = getIntent(); Bundle bundle = intent.getExtras(); if (bundle != null) { - this.single = bundle.getBoolean("single", false); + update = bundle.getBoolean("update", false); + documentId = bundle.getString("documentId"); + index = bundle.getInt("index", 0); } - - if (this.single) { - documentScanner = documentScannerSingle; + if (this.update) { + documentScanner = documentScannerUpdate; } documentScanner.startScan(); // Init @@ -231,7 +237,7 @@ public class ScannerManagerActivity extends AppCompatActivity { Bitmap[] bitmaps = new Bitmap[croppedImages.size()]; croppedImages.toArray(bitmaps); logger.debug(TAG, "bitmaps.length: " + bitmaps.length); - addPagesToDocumentDetail(documentDetail, bitmaps, null).join(); + addPagesToDocumentDetail(documentDetail, bitmaps, "Initial Commit").join(); document.updateDocument(documentDetail).join(); dialog.dismiss(); finish(); @@ -243,7 +249,7 @@ public class ScannerManagerActivity extends AppCompatActivity { return CompletableFuture.runAsync(() -> { try { document.updateDocument(documentProcessorFactory.create(documentDetail).addNewPagesToLast(bitmaps).getDocumentDetail()).join(); - lacertaVcsFactory.create(documentDetail.getMeta().getId()).generateRevisionAtCurrent(commitMessage == null ? "Update" : commitMessage); + lacertaVcsFactory.create(documentDetail.getMeta().getId()).generateRevisionAtCurrent(commitMessage == null ? "NONE" : commitMessage); } catch (Exception e) { logger.error(TAG, "Error: " + e.getMessage()); logger.e_code("9dff2a28-20e8-4ccd-9d04-f0c7646faa6a"); @@ -251,6 +257,33 @@ public class ScannerManagerActivity extends AppCompatActivity { }); } + private void updatePage() { + logger.debug(TAG, "updatePage"); + // Deprecatedだが、中断機能が存在しないので操作をブロックする目的で(意図的に)使用 + ProgressDialog dialog = new ProgressDialog(this); + dialog.setMessage("保存中..."); // TODO-rca: テキストをリソースに移動 + dialog.setProgressStyle(ProgressDialog.STYLE_SPINNER); + dialog.setCancelable(false); + dialog.show(); + document.getDocument(documentId).thenAccept((documentDetail) -> { + DocumentProcessor documentProcessor = documentProcessorFactory.create(documentDetail); + if (croppedImages.size() != 1) { + logger.error(TAG, "croppedImages.size() != 1"); + logger.e_code("d8e2b8c9-9b7e-4b7e-9e1e-9e3b8b8b8b8b"); + return; + } + if (croppedImages.get(0) == null) { + logger.error(TAG, "croppedImages.get(0) == null"); + logger.e_code("d8e2b8c9-9b7e-4b7e-9e1e-9e3b8b8b8b8b"); + return; + } + documentProcessor.updatePageAtIndex(croppedImages.get(0), index); + document.updateDocument(documentProcessor.getDocumentDetail()).join(); + lacertaVcsFactory.create(documentDetail.getMeta().getId()).generateRevisionAtCurrent(index + "ページ目を更新"); // TODO-rca: メッセージを動的にする, 指定できるようにする + dialog.dismiss(); + }); + } + private void insertToExistDocument() { logger.debug(TAG, "insertToExistDocument"); LacertaFilePickerDialog dialog = new LacertaFilePickerDialog(); @@ -293,6 +326,8 @@ public class ScannerManagerActivity extends AppCompatActivity { resultView.addView(resultImageView); } + + selectedImage.setImageBitmap(resultImages.get(0)); } } \ No newline at end of file diff --git a/component/viewer/build.gradle b/component/viewer/build.gradle index e1d552bb..e7d7a9eb 100644 --- a/component/viewer/build.gradle +++ b/component/viewer/build.gradle @@ -63,4 +63,6 @@ dependencies { // ViewPager2 implementation "androidx.viewpager2:viewpager2:1.0.0" + + implementation project(':component:scanner') // めちゃくちゃよくないけどもう正しくやってる時間がないので } \ No newline at end of file diff --git a/component/viewer/src/main/java/one/nem/lacerta/component/viewer/ItemClickListener.java b/component/viewer/src/main/java/one/nem/lacerta/component/viewer/ItemClickListener.java index 91026a39..02cbae6e 100644 --- a/component/viewer/src/main/java/one/nem/lacerta/component/viewer/ItemClickListener.java +++ b/component/viewer/src/main/java/one/nem/lacerta/component/viewer/ItemClickListener.java @@ -2,4 +2,6 @@ package one.nem.lacerta.component.viewer; public interface ItemClickListener { void onItemClick(String fileName); // DEBUG + + void onItemLongClick(String fileName, int position); } diff --git a/component/viewer/src/main/java/one/nem/lacerta/component/viewer/ViewerBodyAdapter.java b/component/viewer/src/main/java/one/nem/lacerta/component/viewer/ViewerBodyAdapter.java index 8a430f96..398aaee7 100644 --- a/component/viewer/src/main/java/one/nem/lacerta/component/viewer/ViewerBodyAdapter.java +++ b/component/viewer/src/main/java/one/nem/lacerta/component/viewer/ViewerBodyAdapter.java @@ -44,8 +44,9 @@ public class ViewerBodyAdapter extends RecyclerView.Adapter { - + holder.itemView.setOnLongClickListener(v -> { + listener.onItemLongClick(pages.get(position).getFileName(), position); + return true; }); } diff --git a/component/viewer/src/main/java/one/nem/lacerta/component/viewer/ViewerBodyFragment.java b/component/viewer/src/main/java/one/nem/lacerta/component/viewer/ViewerBodyFragment.java index 3fe30549..db302a17 100644 --- a/component/viewer/src/main/java/one/nem/lacerta/component/viewer/ViewerBodyFragment.java +++ b/component/viewer/src/main/java/one/nem/lacerta/component/viewer/ViewerBodyFragment.java @@ -1,5 +1,6 @@ package one.nem.lacerta.component.viewer; +import android.content.Intent; import android.os.Bundle; import androidx.fragment.app.Fragment; @@ -11,9 +12,14 @@ import android.view.View; import android.view.ViewGroup; import android.widget.Toast; +import com.google.android.material.dialog.MaterialAlertDialogBuilder; + +import java.util.ArrayList; + import javax.inject.Inject; import dagger.hilt.android.AndroidEntryPoint; +import one.nem.lacerta.component.scanner.ScannerManagerActivity; import one.nem.lacerta.data.Document; import one.nem.lacerta.data.LacertaLibrary; import one.nem.lacerta.utils.LacertaLogger; @@ -93,30 +99,68 @@ public class ViewerBodyFragment extends Fragment { RecyclerView recyclerView = view.findViewById(R.id.recycler_view); recyclerView.setLayoutManager(new LinearLayoutManager(getContext())); - ViewerBodyAdapter viewerBodyAdapter = new ViewerBodyAdapter(fileName -> { - Toast.makeText(getContext(), fileName, Toast.LENGTH_SHORT).show(); - // TODO-rca: なにか処理をもたせる + ViewerBodyAdapter viewerBodyAdapter = new ViewerBodyAdapter(new ItemClickListener() { + @Override + public void onItemClick(String fileName) { + + } + + @Override + public void onItemLongClick(String fileName, int position) { + MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(getActivity()); + builder.setTitle("ページを更新しますか?"); + + builder.setPositiveButton("更新", (dialog, which) -> { + // ScannerをIntent + Intent intent = new Intent(getActivity(), ScannerManagerActivity.class); + intent.putExtra("update", true); + intent.putExtra("documentId", documentId); + intent.putExtra("index", position); + startActivity(intent); + }); + builder.setNegativeButton("キャンセル", (dialog, which) -> { + // cancel + dialog.dismiss(); + }); + + builder.show(); + } + }); + + getActivity().runOnUiThread(() -> { + recyclerView.setAdapter(viewerBodyAdapter); + viewerBodyAdapter.notifyDataSetChanged(); }); - recyclerView.setAdapter(viewerBodyAdapter); loadDocument(viewerBodyAdapter, documentId, revisionId); } private void loadDocument(ViewerBodyAdapter adapter, String documentId, String revisionId) { if (revisionId == null) { // load latest revision - document.getDocument(documentId).thenAccept(document -> { + document.getDocument(documentId).thenApply(document -> { getActivity().runOnUiThread(() -> { adapter.setPages(document.getPages()); adapter.notifyDataSetChanged(); }); + return null; }); } else { // load specified revision LacertaVcs vcs = lacertaVcsFactory.create(documentId); - document.getDocumentPageListByFileNameList(documentId, vcs.getDocumentPagePathListRev(revisionId).join()).thenAccept(documentPageList -> { +// document.getDocumentPageListByFileNameList(documentId, vcs.getDocumentPagePathListRev(revisionId).join()).thenApply(documentPageList -> { +// getActivity().runOnUiThread(() -> { +// adapter.setPages(documentPageList); +// adapter.notifyDataSetChanged(); +// }); +// return null; +// }); + + ArrayList fileNameList = vcs.getDocumentPagePathListRev(revisionId).join(); + document.getDocumentPageListByFileNameList(documentId, fileNameList).thenApply(documentPageList -> { getActivity().runOnUiThread(() -> { adapter.setPages(documentPageList); adapter.notifyDataSetChanged(); }); + return null; }); } } diff --git a/component/viewer/src/main/java/one/nem/lacerta/component/viewer/ViewerContainerFragment.java b/component/viewer/src/main/java/one/nem/lacerta/component/viewer/ViewerContainerFragment.java index 6152d199..5e16662b 100644 --- a/component/viewer/src/main/java/one/nem/lacerta/component/viewer/ViewerContainerFragment.java +++ b/component/viewer/src/main/java/one/nem/lacerta/component/viewer/ViewerContainerFragment.java @@ -10,6 +10,7 @@ import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.ImageButton; +import android.widget.PopupMenu; import android.widget.TextView; import android.widget.Toast; @@ -26,13 +27,19 @@ import javax.inject.Inject; import dagger.hilt.android.AndroidEntryPoint; import one.nem.lacerta.component.common.LacertaApplyTagDialog; +import one.nem.lacerta.component.common.LacertaSelectRevDialog; +import one.nem.lacerta.component.common.LacertaSelectRevDialogListener; +import one.nem.lacerta.component.common.picker.LacertaDirPickerDialog; import one.nem.lacerta.component.common.picker.LacertaFilePickerDialog; import one.nem.lacerta.data.Document; import one.nem.lacerta.data.LacertaLibrary; +import one.nem.lacerta.model.ListItemType; import one.nem.lacerta.model.document.page.Page; import one.nem.lacerta.model.document.tag.DocumentTag; import one.nem.lacerta.model.pref.ToxiDocumentModel; import one.nem.lacerta.utils.LacertaLogger; +import one.nem.lacerta.vcs.LacertaVcs; +import one.nem.lacerta.vcs.factory.LacertaVcsFactory; /** * A simple {@link Fragment} subclass. @@ -53,10 +60,14 @@ public class ViewerContainerFragment extends Fragment { @Inject Document document; + @Inject + LacertaVcsFactory lacertaVcsFactory; + // Variables private String documentId; private String documentName; private boolean hasCombined = false; + private String revId; private ViewerViewPagerAdapter viewerViewPagerAdapter; public ViewerContainerFragment() { @@ -70,6 +81,7 @@ public class ViewerContainerFragment extends Fragment { args.putString("documentId", documentId); args.putString("documentName", documentName); args.putBoolean("hasCombined", hasCombined); + args.putString("revId", null); return fragment; } @@ -80,6 +92,18 @@ public class ViewerContainerFragment extends Fragment { args.putString("documentId", documentId); args.putString("documentName", documentName); args.putBoolean("hasCombined", false); + args.putString("revId", null); + return fragment; + } + + public static ViewerContainerFragment newInstance(String documentId, String documentName, String revId) { + ViewerContainerFragment fragment = new ViewerContainerFragment(); + Bundle args = new Bundle(); + fragment.setArguments(args); + args.putString("documentId", documentId); + args.putString("documentName", documentName); + args.putBoolean("hasCombined", false); + args.putString("revId", revId); return fragment; } @@ -90,6 +114,7 @@ public class ViewerContainerFragment extends Fragment { documentId = getArguments().getString("documentId"); documentName = getArguments().getString("documentName"); hasCombined = getArguments().getBoolean("hasCombined"); + revId = getArguments().getString("revId"); } } @@ -118,27 +143,46 @@ public class ViewerContainerFragment extends Fragment { Toolbar toolbar = view.findViewById(R.id.toolbar); initToolbar(toolbar, true, documentName); - // Get document page - if (this.hasCombined) { // 結合親の場合 - logger.debug("ViewerContainerFragment", "hasCombined: " + hasCombined); - lacertaLibrary.getCombinedDocumentToxiList(documentId).thenAccept(combinedDocumentToxiList -> { - logger.debug("ViewerContainerFragment", "combinedDocumentToxiList: " + combinedDocumentToxiList.size()); - - viewerViewPagerAdapter.setFragmentTargetIdList( - combinedDocumentToxiList.stream().map(ToxiDocumentModel::getChildDocumentId).collect(Collectors.toCollection(ArrayList::new))); - viewerViewPagerAdapter.setFragmentTitleList( - combinedDocumentToxiList.stream().map(ToxiDocumentModel::getTitleCache).collect(Collectors.toCollection(ArrayList::new))); - - viewerViewPagerAdapter.notifyItemRangeChanged(0, combinedDocumentToxiList.size()); - }); - } else { // それ以外の場合 - logger.debug("ViewerContainerFragment", "hasCombined: " + hasCombined); - tabLayout.setVisibility(View.GONE); + if (this.revId != null) { // Revが指定されている場合 + LacertaVcs lacertaVcs = lacertaVcsFactory.create(documentId); viewerViewPagerAdapter.setFragmentTargetIdList(new ArrayList(){{add(documentId);}}); // TODO-rca: 読みにくいので直接追加できるようにする - viewerViewPagerAdapter.setFragmentTitleList(new ArrayList(){{add(documentName);}}); + viewerViewPagerAdapter.setFragmentTitleList(new ArrayList(){{add(documentName);}}); // TODO-rca: 読みにくいので直接追加できるようにする + viewerViewPagerAdapter.setFragmentRevisionList(new ArrayList(){{add(revId);}}); // TODO-rca: 読みにくいので直接追加できるようにする viewerViewPagerAdapter.notifyItemRangeChanged(0, 1); - } + tabLayout.setVisibility(View.GONE); + toolbar.setSubtitle("リビジョン: " + revId); + } else { + // Get document page + if (this.hasCombined) { // 結合親の場合 + // バージョンを遡る操作を非表示 + toolbar.getMenu().findItem(R.id.action_open_vcs_rev_list).setVisible(false); + logger.debug("ViewerContainerFragment", "hasCombined: " + hasCombined); + lacertaLibrary.getCombinedDocumentToxiList(documentId).thenAccept(combinedDocumentToxiList -> { + logger.debug("ViewerContainerFragment", "combinedDocumentToxiList: " + combinedDocumentToxiList.size()); + viewerViewPagerAdapter.setFragmentTargetIdList( + combinedDocumentToxiList.stream().map(ToxiDocumentModel::getChildDocumentId).collect(Collectors.toCollection(ArrayList::new))); + viewerViewPagerAdapter.setFragmentTitleList( + combinedDocumentToxiList.stream().map(ToxiDocumentModel::getTitleCache).collect(Collectors.toCollection(ArrayList::new))); + + viewerViewPagerAdapter.notifyItemRangeChanged(0, combinedDocumentToxiList.size()); + toolbar.setSubtitle("結合ドキュメント"); + }); + } else { // それ以外の場合 + logger.debug("ViewerContainerFragment", "hasCombined: " + hasCombined); + tabLayout.setVisibility(View.GONE); + viewerViewPagerAdapter.setFragmentTargetIdList(new ArrayList(){{add(documentId);}}); // TODO-rca: 読みにくいので直接追加できるようにする + viewerViewPagerAdapter.setFragmentTitleList(new ArrayList(){{add(documentName);}}); + viewerViewPagerAdapter.notifyItemRangeChanged(0, 1); + } + + // サブタイトルとしてパスを表示(暫定) + lacertaLibrary.getPublicPath(documentId, ListItemType.ITEM_TYPE_DOCUMENT).thenAccept(publicPath -> { + logger.debug("ViewerContainerFragment", "publicPath: " + publicPath); + toolbar.setSubtitle("/" + publicPath.parent().getStringPath()); + }); + + } // Attach tab layout to view pager new TabLayoutMediator(tabLayout, viewPager, (tab, position) -> { View customView = LayoutInflater.from(getContext()).inflate(R.layout.viewer_custom_tab, null); @@ -148,11 +192,27 @@ public class ViewerContainerFragment extends Fragment { ImageButton imageButton = customView.findViewById(R.id.tab_modify); imageButton.setOnClickListener(v -> { - renameCombinedDocument( - this.documentId, - viewerViewPagerAdapter.getFragmentTargetId(position), - viewerViewPagerAdapter.getFragmentTitle(position), - position); + PopupMenu popupMenu = new PopupMenu(getContext(), v); + popupMenu.inflate(R.menu.viewer_tab_menu); + popupMenu.setOnMenuItemClickListener(item -> { + if (item.getItemId() == R.id.action_open_vcs_rev_list) { + showRevList(viewerViewPagerAdapter.getFragmentTargetId(position), viewerViewPagerAdapter.getFragmentTitle(position)); + return true; + } else if (item.getItemId() == R.id.action_rename) { + renameCombinedDocument( + documentId, + viewerViewPagerAdapter.getFragmentTargetIdList().get(position), + viewerViewPagerAdapter.getFragmentTitle(position), + position); + return true; + } else if (item.getItemId() == R.id.action_delete) { + Toast.makeText(getContext(), "Work in progress", Toast.LENGTH_SHORT).show(); + return true; + } else { + return false; + } + }); + popupMenu.show(); }); tab.setCustomView(customView); @@ -222,16 +282,16 @@ public class ViewerContainerFragment extends Fragment { toolbar.inflateMenu(R.menu.viewer_menu); toolbar.setOnMenuItemClickListener(item -> { if (item.getItemId() == R.id.action_open_vcs_rev_list) { - Toast.makeText(getContext(), "Work in progress", Toast.LENGTH_SHORT).show(); + showRevList(this.documentId, this.documentName); return true; } else if (item.getItemId() == R.id.action_rename) { renameDocument(); return true; } else if (item.getItemId() == R.id.action_delete) { - Toast.makeText(getContext(), "Work in progress", Toast.LENGTH_SHORT).show(); + deleteDocument(); return true; } else if (item.getItemId() == R.id.action_move) { - Toast.makeText(getContext(), "Work in progress", Toast.LENGTH_SHORT).show(); + moveDocument(); return true; } else if (item.getItemId() == R.id.action_combine) { combineDocument(); @@ -246,11 +306,68 @@ public class ViewerContainerFragment extends Fragment { }); } + private void showRevList(String targetId, String targetName) { + LacertaSelectRevDialog lacertaSelectRevDialog = new LacertaSelectRevDialog(); + lacertaSelectRevDialog.setDocumentId(targetId).setTitle("リビジョンの選択").setMessage("リビジョンを選択してください。").setNegativeButtonText("キャンセル"); + lacertaSelectRevDialog.setListener(new LacertaSelectRevDialogListener() { + @Override + public void onItemSelected(String revId) { + logger.debug("ViewerContainerFragment", "Dialog Result: revId: " + revId); + getParentFragmentManager().beginTransaction() + .replace(R.id.nav_host_fragment, ViewerContainerFragment.newInstance(targetId, targetName, revId)) + .commit(); + } + + @Override + public void onDialogCanceled() { + } + }); + lacertaSelectRevDialog.show(getParentFragmentManager(), "select_rev_dialog"); + } + + private void moveDocument() { + LacertaDirPickerDialog lacertaDirPickerDialog = new LacertaDirPickerDialog(); + lacertaDirPickerDialog.setListener((name, dirId) -> { + logger.debug("MoveDocument", "Selected dir: " + name + ", " + dirId); + document.moveDocument(documentId, dirId).thenAccept(aVoid -> { + getActivity().runOnUiThread(() -> { + // Stop Activity + getActivity().finish(); // TODO-rca: ファイル移動後に終了するべきかは検討 + }); + }); + }); + lacertaDirPickerDialog.setTitle("ファイルの移動") + .setMessage("ファイルを移動するフォルダを選択してください。") + .setPositiveButtonText("移動") + .setNegativeButtonText("キャンセル"); + lacertaDirPickerDialog.show(getParentFragmentManager(), "select_dir_dialog"); + } + + private void deleteDocument() { + MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(getContext()); + builder.setTitle("ファイルの削除"); + builder.setMessage("ファイルを削除しますか?"); + + builder.setPositiveButton("削除", (dialog, which) -> { + document.deleteDocument(documentId).thenAccept(aVoid -> { + getActivity().runOnUiThread(() -> { + Toast.makeText(getContext(), "削除しました", Toast.LENGTH_SHORT).show(); + getActivity().finish(); // TODO-rca: 終了させずにUIを更新したい + }); + }); + }); + builder.setNegativeButton("キャンセル", (dialog, which) -> { + dialog.cancel(); + }); + + builder.show(); + } + private void applyTag() { LacertaApplyTagDialog lacertaApplyTagDialog = new LacertaApplyTagDialog(); lacertaApplyTagDialog .setTitle("タグの適用") - .setMessage("タグを適用するファイルを選択してください") + .setMessage("適用するタグを選択してください") .setNegativeButtonText("キャンセル") .setDocumentId(documentId) .setListener(new LacertaApplyTagDialog.LacertaApplyTagDialogListener() { diff --git a/component/viewer/src/main/java/one/nem/lacerta/component/viewer/ViewerListFragment.java b/component/viewer/src/main/java/one/nem/lacerta/component/viewer/ViewerListFragment.java index c4ba8a1c..a13620cc 100644 --- a/component/viewer/src/main/java/one/nem/lacerta/component/viewer/ViewerListFragment.java +++ b/component/viewer/src/main/java/one/nem/lacerta/component/viewer/ViewerListFragment.java @@ -106,8 +106,16 @@ public class ViewerListFragment extends Fragment { RecyclerView recyclerView = view.findViewById(R.id.body_recycler_view); recyclerView.setLayoutManager(new LinearLayoutManager(getContext())); - ViewerBodyAdapter viewerBodyAdapter = new ViewerBodyAdapter(fileName -> { - Toast.makeText(getContext(), fileName, Toast.LENGTH_SHORT).show(); + ViewerBodyAdapter viewerBodyAdapter = new ViewerBodyAdapter(new ItemClickListener() { + @Override + public void onItemClick(String fileName) { + + } + + @Override + public void onItemLongClick(String fileName, int position) { + + } }); recyclerView.setAdapter(viewerBodyAdapter); diff --git a/component/viewer/src/main/java/one/nem/lacerta/component/viewer/ViewerViewPagerAdapter.java b/component/viewer/src/main/java/one/nem/lacerta/component/viewer/ViewerViewPagerAdapter.java index 66a6d8a9..e4646237 100644 --- a/component/viewer/src/main/java/one/nem/lacerta/component/viewer/ViewerViewPagerAdapter.java +++ b/component/viewer/src/main/java/one/nem/lacerta/component/viewer/ViewerViewPagerAdapter.java @@ -15,6 +15,7 @@ public class ViewerViewPagerAdapter extends FragmentStateAdapter { // Variables private ArrayList fragmentTargetIdList = new ArrayList<>(); private ArrayList fragmentTitleList = new ArrayList<>(); + private ArrayList fragmentRevisionList = new ArrayList<>(); // Setter @@ -26,6 +27,10 @@ public class ViewerViewPagerAdapter extends FragmentStateAdapter { this.fragmentTitleList = fragmentTitleList; } + public void setFragmentRevisionList(ArrayList fragmentRevisionList) { + this.fragmentRevisionList = fragmentRevisionList; + } + public ViewerViewPagerAdapter(@NonNull FragmentActivity fragmentActivity) { super(fragmentActivity); } @@ -33,7 +38,11 @@ public class ViewerViewPagerAdapter extends FragmentStateAdapter { @NonNull @Override public Fragment createFragment(int position) { - return ViewerBodyFragment.newInstance(fragmentTargetIdList.get(position), fragmentTitleList.get(position)); + if (fragmentRevisionList != null && fragmentRevisionList.size() > position) { + return ViewerBodyFragment.newInstance(fragmentTargetIdList.get(position), fragmentTitleList.get(position), fragmentRevisionList.get(position)); + } else { + return ViewerBodyFragment.newInstance(fragmentTargetIdList.get(position), fragmentTitleList.get(position)); + } } @Override diff --git a/component/viewer/src/main/res/layout/fragment_viewer_body.xml b/component/viewer/src/main/res/layout/fragment_viewer_body.xml index 0309ef0a..db2ae9e5 100644 --- a/component/viewer/src/main/res/layout/fragment_viewer_body.xml +++ b/component/viewer/src/main/res/layout/fragment_viewer_body.xml @@ -10,6 +10,7 @@ android:id="@+id/recycler_view" android:layout_width="match_parent" android:layout_height="match_parent" + android:layout_margin="8dp" app:layout_behavior="@string/appbar_scrolling_view_behavior" /> \ No newline at end of file diff --git a/component/viewer/src/main/res/menu/viewer_menu.xml b/component/viewer/src/main/res/menu/viewer_menu.xml index 92dab962..ce7c27be 100644 --- a/component/viewer/src/main/res/menu/viewer_menu.xml +++ b/component/viewer/src/main/res/menu/viewer_menu.xml @@ -1,8 +1,9 @@ - + + android:title="バージョン履歴" /> + android:icon="@drawable/delete_24px" + android:title="削除" + app:showAsAction="ifRoom"/> - - + android:icon="@drawable/merge_type_24px" + android:title="結合" + app:showAsAction="ifRoom"/> + + + + + + + \ No newline at end of file diff --git a/data/src/main/java/one/nem/lacerta/data/LacertaLibrary.java b/data/src/main/java/one/nem/lacerta/data/LacertaLibrary.java index e1ce346e..46728e2d 100644 --- a/data/src/main/java/one/nem/lacerta/data/LacertaLibrary.java +++ b/data/src/main/java/one/nem/lacerta/data/LacertaLibrary.java @@ -28,6 +28,9 @@ public interface LacertaLibrary { // Create Folder CompletableFuture createFolder(String parentId, String name); + // delete Folder + CompletableFuture deleteFolder(String folderId); + // Get Public Path CompletableFuture getPublicPath(String itemId, ListItemType itemType); diff --git a/data/src/main/java/one/nem/lacerta/data/impl/LacertaLibraryImpl.java b/data/src/main/java/one/nem/lacerta/data/impl/LacertaLibraryImpl.java index f35dee8c..94ff9a1a 100644 --- a/data/src/main/java/one/nem/lacerta/data/impl/LacertaLibraryImpl.java +++ b/data/src/main/java/one/nem/lacerta/data/impl/LacertaLibraryImpl.java @@ -50,12 +50,13 @@ public class LacertaLibraryImpl implements LacertaLibrary { return CompletableFuture.supplyAsync(() -> { List documentEntities = database.documentDao().getRecentDocument(limit); + SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss"); ArrayList listItems = new ArrayList<>(); for (DocumentEntity documentEntity : documentEntities) { ListItem listItem = new ListItem(); listItem.setItemType(ListItemType.ITEM_TYPE_DOCUMENT); listItem.setTitle(documentEntity.title); - listItem.setDescription(DateFormat.getDateInstance().format(documentEntity.updatedAt)); + listItem.setDescription(simpleDateFormat.format(documentEntity.updatedAt)); listItem.setItemId(documentEntity.id); listItem.setHasCombined(documentEntity.isCombineParent); listItems.add(listItem); @@ -111,7 +112,7 @@ public class LacertaLibraryImpl implements LacertaLibrary { listItems.add(listItem); } - SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy/MM/dd HH:mm:"); + SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss"); for (DocumentEntity documentEntity : documentEntities) { logger.debug("LacertaLibraryImpl", "documentEntity.title: " + documentEntity.title); @@ -206,6 +207,14 @@ public class LacertaLibraryImpl implements LacertaLibrary { }); } + @Override + public CompletableFuture deleteFolder(String folderId) { + return CompletableFuture.supplyAsync(() -> { + database.folderDao().deleteById(folderId); + return null; + }); + } + @Override public CompletableFuture getPublicPath(String itemId, ListItemType itemType) { return CompletableFuture.supplyAsync(() -> { diff --git a/feature/library/build.gradle b/feature/library/build.gradle index 6a661620..a7ca4dde 100644 --- a/feature/library/build.gradle +++ b/feature/library/build.gradle @@ -55,6 +55,10 @@ dependencies { implementation project(':component:viewer') + implementation project(':processor') // TODO-rca: あんまり依存したくないけど時間がないので一旦 + + implementation project(':vcs') // TODO-rca: あんまり依存したくないけど時間がないので一旦 + // TODO-rca: バージョンカタログに切り出す implementation "com.hendraanggrian.material:collapsingtoolbarlayout-subtitle:1.5.0" } \ No newline at end of file diff --git a/feature/library/src/main/java/one/nem/lacerta/feature/library/LibraryPageFragment.java b/feature/library/src/main/java/one/nem/lacerta/feature/library/LibraryPageFragment.java index 158e61e1..3ba134c0 100644 --- a/feature/library/src/main/java/one/nem/lacerta/feature/library/LibraryPageFragment.java +++ b/feature/library/src/main/java/one/nem/lacerta/feature/library/LibraryPageFragment.java @@ -1,8 +1,13 @@ package one.nem.lacerta.feature.library; import android.content.Intent; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.net.Uri; import android.os.Bundle; +import androidx.activity.result.ActivityResultLauncher; +import androidx.activity.result.contract.ActivityResultContracts; import androidx.appcompat.widget.Toolbar; import androidx.core.content.ContextCompat; import androidx.fragment.app.Fragment; @@ -17,6 +22,8 @@ import android.view.ViewGroup; import com.google.android.material.appbar.AppBarLayout; import com.google.android.material.dialog.MaterialAlertDialogBuilder; +import java.util.concurrent.CompletableFuture; + import javax.inject.Inject; import dagger.hilt.android.AndroidEntryPoint; @@ -25,9 +32,12 @@ import one.nem.lacerta.data.Document; import one.nem.lacerta.data.LacertaLibrary; import one.nem.lacerta.model.LibraryItemPage; import one.nem.lacerta.model.ListItemType; +import one.nem.lacerta.model.document.DocumentDetail; import one.nem.lacerta.model.document.tag.DocumentTag; +import one.nem.lacerta.processor.factory.DocumentProcessorFactory; import one.nem.lacerta.utils.FeatureSwitch; import one.nem.lacerta.utils.LacertaLogger; +import one.nem.lacerta.vcs.factory.LacertaVcsFactory; /** @@ -48,6 +58,8 @@ public class LibraryPageFragment extends Fragment { String parentId; Toolbar toolbar; + // ActivityResultContracts + ActivityResultLauncher getContent; @Inject LacertaLibrary lacertaLibrary; @@ -58,6 +70,12 @@ public class LibraryPageFragment extends Fragment { @Inject Document document; + @Inject + DocumentProcessorFactory documentProcessorFactory; + + @Inject + LacertaVcsFactory lacertaVcsFactory; + ListItemAdapter listItemAdapter; public LibraryPageFragment() { @@ -97,6 +115,37 @@ public class LibraryPageFragment extends Fragment { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); + + getContent = registerForActivityResult(new ActivityResultContracts.GetMultipleContents(), uris -> { + for (int i = 0; i < uris.size(); i++) { + logger.debug("LibraryTopFragment", "uris.get(" + i + "): " + uris.get(i).getPath()); + } + // ここで取得したURIリストを使用して画像を操作します。 + if (uris != null) { + Bitmap[] bitmaps = new Bitmap[uris.size()]; + for (int i = 0; i < uris.size(); i++) { + Uri uri = uris.get(i); + try { + bitmaps[i] = BitmapFactory.decodeStream(requireContext().getContentResolver().openInputStream(uri)); + } catch (Exception e) { + logger.error("LibraryTopFragment", "Error: " + e.getMessage()); + logger.e_code("826da745-7fc9-43e6-9935-9daa17a3932f"); + } + } + + logger.debug("LibraryTopFragment", "bitmaps.length: " + bitmaps.length); + // ドキュメントを作成 + document.createDocument().thenApply(documentDetail -> { + logger.debug("LibraryTopFragment", "create document (may) success!"); + // ドキュメントにページを追加 + addPagesToDocumentDetail(documentDetail, bitmaps, "Initial commit(IMPORT)").join(); + document.updateDocument(documentDetail).join(); + return null; + }); + } else { + logger.debug("LibraryTopFragment", "uris is null"); + } + }); } @@ -153,10 +202,6 @@ public class LibraryPageFragment extends Fragment { @Override public void onFolderSelected(String folderId, String folderName) { logger.debug("LibraryTopFragment", "Folder selected! folderId: " + folderId + ", folderName: " + folderName); -// // 画面遷移 -// FragmentNavigation fragmentNavigation = (FragmentNavigation) getActivity(); -// // folderId: 推移先で表示するフォルダのID, folderName: 推移先で表示するフォルダの名前, parentId: このフラグメントで表示しているフォルダのID(推移先の親) -// fragmentNavigation.navigateToFragment(LibraryPageFragment.newInstance(folderId, folderName, libraryItemPage != null ? libraryItemPage.getParentId() : null), false); Bundle bundle = new Bundle(); bundle.putString("folderId", folderId); bundle.putString("title", folderName); @@ -238,7 +283,11 @@ public class LibraryPageFragment extends Fragment { }); } - private void getTag(String documentId) { //debug + @Override + public void onResume() { + super.onResume(); + + updateItem(this.folderId); // 暫定, Pull-to-refreshを実装するまで } /** @@ -270,6 +319,7 @@ public class LibraryPageFragment extends Fragment { } else { toolbar.setNavigationIcon(null); } + toolbar.setTitle(title); toolbar.getMenu().clear(); toolbar.inflateMenu(R.menu.dir_menu); @@ -277,10 +327,70 @@ public class LibraryPageFragment extends Fragment { if (item.getItemId() == R.id.menu_item_create_new_folder) { createFolder(this.folderId); return true; + } else if (item.getItemId() == R.id.menu_item_add_by_media) { + createDocByMediaPicker(); + return true; + } else if (item.getItemId() == R.id.menu_item_delete_folder) { + MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(requireContext()); + builder.setTitle("フォルダの削除"); + builder.setMessage("フォルダを削除しますか?"); + + builder.setPositiveButton("削除", (dialog, which) -> { + deleteMe(); + }); + builder.setNegativeButton("キャンセル", (dialog, which) -> { + dialog.cancel(); + }); + + builder.show(); + return true; } else { return false; } }); + + if (this.folderId == null) toolbar.getMenu().findItem(R.id.menu_item_delete_folder).setVisible(false); // ルートフォルダの場合は削除ボタンを非表示にする + }); + } + + private void deleteMe() { + lacertaLibrary.deleteFolder(this.folderId).thenAccept(aVoid -> { + // Move to root + getActivity().runOnUiThread(() -> { + Navigation.findNavController(requireView()).popBackStack(R.id.feature_library_top_fragment, false); + + // Refresh + updateItem(this.folderId); + + // Update toolbar + toolbarSetup(this.toolbar, false, "ライブラリ"); + }); + }); + } + + /** + * メディアピッカーを使用してドキュメントを作成する(呼び出し部分) + */ + private void createDocByMediaPicker() { + getContent.launch("image/*"); + } + + /** + * ページを追加する + * @param documentDetail ドキュメント詳細 + * @param bitmaps ビットマップ + * @param commitMessage コミットメッセージ + * @return CompletableFuture + */ + private CompletableFuture addPagesToDocumentDetail(DocumentDetail documentDetail, Bitmap[] bitmaps, String commitMessage) { + return CompletableFuture.runAsync(() -> { + try { + document.updateDocument(documentProcessorFactory.create(documentDetail).addNewPagesToLast(bitmaps).getDocumentDetail()).join(); + lacertaVcsFactory.create(documentDetail.getMeta().getId()).generateRevisionAtCurrent(commitMessage == null ? "NONE" : commitMessage); + } catch (Exception e) { + logger.error("LibraryAddSupport", "Error: " + e.getMessage()); + logger.e_code("9dff2a28-20e8-4ccd-9d04-f0c7646faa6a"); + } }); } } diff --git a/feature/library/src/main/java/one/nem/lacerta/feature/library/ListItemAdapter.java b/feature/library/src/main/java/one/nem/lacerta/feature/library/ListItemAdapter.java index ee310413..f16cb2cd 100644 --- a/feature/library/src/main/java/one/nem/lacerta/feature/library/ListItemAdapter.java +++ b/feature/library/src/main/java/one/nem/lacerta/feature/library/ListItemAdapter.java @@ -1,5 +1,6 @@ package one.nem.lacerta.feature.library; +import android.graphics.Color; import android.util.Log; import android.view.LayoutInflater; import android.view.View; @@ -49,11 +50,14 @@ public class ListItemAdapter extends RecyclerView.Adapter 0) { // ごまかし + holder.tagGroup.removeAllViews(); + } for (int i = 0; i < listItem.getTagList().size(); i++) { - Toast.makeText(holder.tagGroup.getContext(), listItem.getTagList().get(i).getName(), Toast.LENGTH_SHORT).show(); ChipGroup chipGroup = holder.tagGroup; Chip chip = new Chip(chipGroup.getContext()); chip.setText(listItem.getTagList().get(i).getName()); + chip.setTextColor(Color.parseColor(listItem.getTagList().get(i).getColor())); chipGroup.addView(chip); } holder.tagGroup.setVisibility(View.VISIBLE); diff --git a/feature/library/src/main/res/menu/dir_menu.xml b/feature/library/src/main/res/menu/dir_menu.xml index e5cea78a..872e777b 100644 --- a/feature/library/src/main/res/menu/dir_menu.xml +++ b/feature/library/src/main/res/menu/dir_menu.xml @@ -8,4 +8,15 @@ android:title="@string/create_new_folder" app:showAsAction="never"/> + + + + \ No newline at end of file diff --git a/feature/setting/src/main/res/menu/setting_tag_manage_menu.xml b/feature/setting/src/main/res/menu/setting_tag_manage_menu.xml index f012a68b..9cbabb03 100644 --- a/feature/setting/src/main/res/menu/setting_tag_manage_menu.xml +++ b/feature/setting/src/main/res/menu/setting_tag_manage_menu.xml @@ -4,7 +4,7 @@ \ No newline at end of file diff --git a/feature/setting/src/main/res/navigation/feature_setting_navigation.xml b/feature/setting/src/main/res/navigation/feature_setting_navigation.xml index 4d382d4d..781b43fc 100644 --- a/feature/setting/src/main/res/navigation/feature_setting_navigation.xml +++ b/feature/setting/src/main/res/navigation/feature_setting_navigation.xml @@ -12,34 +12,38 @@ + app:enterAnim="@anim/nav_twitter_enter_anim" + app:exitAnim="@anim/nav_twitter_exit_anim" + app:popEnterAnim="@anim/nav_twitter_pop_enter_anim" + app:popExitAnim="@anim/nav_twitter_pop_exit_anim" /> + app:enterAnim="@anim/nav_twitter_enter_anim" + app:exitAnim="@anim/nav_twitter_exit_anim" + app:popEnterAnim="@anim/nav_twitter_pop_enter_anim" + app:popExitAnim="@anim/nav_twitter_pop_exit_anim" /> + app:enterAnim="@anim/nav_twitter_enter_anim" + app:exitAnim="@anim/nav_twitter_exit_anim" + app:popEnterAnim="@anim/nav_twitter_pop_enter_anim" + app:popExitAnim="@anim/nav_twitter_pop_exit_anim" /> + app:enterAnim="@anim/nav_twitter_enter_anim" + app:exitAnim="@anim/nav_twitter_exit_anim" + app:popEnterAnim="@anim/nav_twitter_pop_enter_anim" + app:popExitAnim="@anim/nav_twitter_pop_exit_anim"/> + app:destination="@id/settingTagManageFragment" + app:enterAnim="@anim/nav_twitter_enter_anim" + app:exitAnim="@anim/nav_twitter_exit_anim" + app:popEnterAnim="@anim/nav_twitter_pop_enter_anim" + app:popExitAnim="@anim/nav_twitter_pop_exit_anim" /> + + diff --git a/shared/ui/src/main/res/drawable/delete_24px.xml b/shared/ui/src/main/res/drawable/delete_24px.xml new file mode 100644 index 00000000..43b217b7 --- /dev/null +++ b/shared/ui/src/main/res/drawable/delete_24px.xml @@ -0,0 +1,10 @@ + + + diff --git a/shared/ui/src/main/res/drawable/merge_type_24px.xml b/shared/ui/src/main/res/drawable/merge_type_24px.xml new file mode 100644 index 00000000..25a3cd79 --- /dev/null +++ b/shared/ui/src/main/res/drawable/merge_type_24px.xml @@ -0,0 +1,11 @@ + + + diff --git a/source/src/main/java/one/nem/lacerta/source/database/dao/FolderDao.java b/source/src/main/java/one/nem/lacerta/source/database/dao/FolderDao.java index 404b53f8..d5084849 100644 --- a/source/src/main/java/one/nem/lacerta/source/database/dao/FolderDao.java +++ b/source/src/main/java/one/nem/lacerta/source/database/dao/FolderDao.java @@ -26,4 +26,7 @@ public interface FolderDao { @Insert void insertAll(FolderEntity... folderEntities); + + @Query("DELETE FROM Folder WHERE id = :id") + void deleteById(String id); } diff --git a/vcs/src/main/java/one/nem/lacerta/vcs/impl/LacertaVcsImpl.java b/vcs/src/main/java/one/nem/lacerta/vcs/impl/LacertaVcsImpl.java index 5e193bcd..5a89d6e4 100644 --- a/vcs/src/main/java/one/nem/lacerta/vcs/impl/LacertaVcsImpl.java +++ b/vcs/src/main/java/one/nem/lacerta/vcs/impl/LacertaVcsImpl.java @@ -1,6 +1,7 @@ package one.nem.lacerta.vcs.impl; import java.util.ArrayList; +import java.util.Comparator; import java.util.List; import java.util.UUID; import java.util.concurrent.CompletableFuture; @@ -41,7 +42,20 @@ public class LacertaVcsImpl implements LacertaVcs { @Override public void updatePage(int index, String fileName) { + logger.debug(TAG, "updatePage"); + // UpdatePage + UpdatePage updatePage = new UpdatePage(index, fileName); + updatePage.setActionType(ActionType.UPDATE_PAGE); + + VcsLogEntity vcsLogEntity = new VcsLogEntity(); + vcsLogEntity.id = UUID.randomUUID().toString(); + vcsLogEntity.documentId = documentId; + vcsLogEntity.branchName = "master"; + vcsLogEntity.createdAt = new java.util.Date(); + vcsLogEntity.actionType = ActionType.UPDATE_PAGE.getValue(); + vcsLogEntity.action = JsonUtils.toJson(updatePage); + database.vcsLogDao().insert(vcsLogEntity); } @Override @@ -182,16 +196,20 @@ public class LacertaVcsImpl implements LacertaVcs { } private CompletableFuture> getRevBeforeTargetIdAsync(String revId){ + logger.debug(TAG, "getRevBeforeTargetIdAsync called: " + revId); return CompletableFuture.supplyAsync(() -> { ArrayList vcsRevEntities = new ArrayList<>(database.vcsRevDao().findByDocumentId(this.documentId)); + // 古い順に並び替え + vcsRevEntities.sort(Comparator.comparing(a -> a.createdAt)); ArrayList vcsRevEntitiesBeforeTarget = new ArrayList<>(); - vcsRevEntities.forEach(vcsRevEntity -> { + for (VcsRevEntity vcsRevEntity : vcsRevEntities) { if(vcsRevEntity.id.equals(revId)){ + logger.debug(TAG, "getRevBeforeTargetIdAsync: Target found"); vcsRevEntitiesBeforeTarget.add(vcsRevEntity); - return; + break; } vcsRevEntitiesBeforeTarget.add(vcsRevEntity); - }); + } logger.debug(TAG, "getRevBeforeTargetIdAsync finished\nResult size: " + vcsRevEntitiesBeforeTarget.size()); return vcsRevEntitiesBeforeTarget; }); @@ -203,8 +221,8 @@ public class LacertaVcsImpl implements LacertaVcs { vcsRevEntities.forEach(vcsRevEntity -> { logIds.addAll(vcsRevEntity.logIds); }); - // TODO-rca: ソートしないといけないかも(順番が保証されているわけではない + 順番が変わるとほぼ確実に壊れる) ArrayList vcsLogEntities = new ArrayList<>(database.vcsLogDao().findByIds(logIds)); + vcsLogEntities.sort(Comparator.comparing(a -> a.createdAt)); logger.debug(TAG, "getLogInRevsAsync finished\nResult size: " + vcsLogEntities.size()); return vcsLogEntities; }); @@ -222,7 +240,13 @@ public class LacertaVcsImpl implements LacertaVcs { public CompletableFuture> getDocumentPagePathListRev(String revId) { return CompletableFuture.supplyAsync(() -> { logger.debug(TAG, "getDocumentPagePathListRev"); - ArrayList vcsLogEntities = getRevBeforeTargetIdAsync(revId).thenCompose(this::getLogInRevsAsync).join(); + ArrayList vcsRevEntities = getRevBeforeTargetIdAsync(revId).join(); + + logger.debug(TAG, "getDocumentPagePathListRev: vcsRevEntities size: " + vcsRevEntities.size()); + + ArrayList vcsLogEntities = getLogInRevsAsync(vcsRevEntities).join(); + + logger.debug(TAG, "getDocumentPagePathListRev: vcsLogEntities size: " + vcsLogEntities.size()); ArrayList fileNameList = new ArrayList<>(); for(VcsLogEntity vcsLogEntity : vcsLogEntities){