diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index a5ae62e6..1680ef09 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -17,7 +17,7 @@ android:allowBackup="true" android:dataExtractionRules="@xml/data_extraction_rules" android:fullBackupContent="@xml/backup_rules" - android:icon="@mipmap/ic_launcher" + android:icon="@mipmap/ic_launcher_temp_round" android:name=".LacertaApplication" android:label="@string/app_name" android:roundIcon="@mipmap/ic_launcher_round" diff --git a/app/src/main/ic_launcher_temp-playstore.png b/app/src/main/ic_launcher_temp-playstore.png new file mode 100644 index 00000000..f83a7d3a Binary files /dev/null and b/app/src/main/ic_launcher_temp-playstore.png differ diff --git a/app/src/main/java/one/nem/lacerta/LacertaApplication.java b/app/src/main/java/one/nem/lacerta/LacertaApplication.java index 448349b5..78dae9ae 100644 --- a/app/src/main/java/one/nem/lacerta/LacertaApplication.java +++ b/app/src/main/java/one/nem/lacerta/LacertaApplication.java @@ -6,6 +6,7 @@ import android.util.Log; import com.google.android.material.color.DynamicColors; import dagger.hilt.android.HiltAndroidApp; +import one.nem.lacerta.utils.FeatureSwitch; @HiltAndroidApp public class LacertaApplication extends Application { @@ -15,7 +16,11 @@ public class LacertaApplication extends Application { if (DynamicColors.isDynamicColorAvailable()) { Log.d("DynamicColors", "DynamicColors is available. Applying to activities..."); - DynamicColors.applyToActivitiesIfAvailable(this); + if (FeatureSwitch.Meta.disableDynamicColor) { + Log.d("DynamicColors", "DynamicColors is disabled by FeatureSwitch."); + } else { + DynamicColors.applyToActivitiesIfAvailable(this); + } } else { Log.d("DynamicColors", "DynamicColors is not available."); } diff --git a/app/src/main/java/one/nem/lacerta/MainActivity.java b/app/src/main/java/one/nem/lacerta/MainActivity.java index f74b20a7..d7fac694 100644 --- a/app/src/main/java/one/nem/lacerta/MainActivity.java +++ b/app/src/main/java/one/nem/lacerta/MainActivity.java @@ -1,5 +1,6 @@ package one.nem.lacerta; +import androidx.annotation.NonNull; import androidx.appcompat.app.AppCompatActivity; import androidx.appcompat.widget.Toolbar; import androidx.core.content.ContextCompat; @@ -11,6 +12,7 @@ import androidx.navigation.fragment.NavHostFragment; import androidx.navigation.ui.NavigationUI; import android.content.Intent; +import android.content.pm.PackageManager; import android.graphics.Color; import android.os.Bundle; import android.util.Log; @@ -85,6 +87,21 @@ public class MainActivity extends AppCompatActivity implements FragmentNavigatio }); } + + @Override + public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { + super.onRequestPermissionsResult(requestCode, permissions, grantResults); + if (requestCode == 1) { + if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { + Toast.makeText(this, "Permission granted", Toast.LENGTH_SHORT).show(); + } else { + Toast.makeText(this, "Permission denied", Toast.LENGTH_SHORT).show(); + Toast.makeText(this, "カメラの権限は必須です.", Toast.LENGTH_LONG).show(); + finish(); // Exit app + } + } + } + private void initializeApp() { Log.d("Init", "Initializing app"); // Set feature switch override to default value diff --git a/app/src/main/res/drawable/ic_launcher_temp_foreground.xml b/app/src/main/res/drawable/ic_launcher_temp_foreground.xml new file mode 100644 index 00000000..3cf3c7d5 --- /dev/null +++ b/app/src/main/res/drawable/ic_launcher_temp_foreground.xml @@ -0,0 +1,15 @@ + + + + + diff --git a/app/src/main/res/mipmap-anydpi-v26/ic_launcher_temp.xml b/app/src/main/res/mipmap-anydpi-v26/ic_launcher_temp.xml new file mode 100644 index 00000000..08dacb68 --- /dev/null +++ b/app/src/main/res/mipmap-anydpi-v26/ic_launcher_temp.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/mipmap-anydpi-v26/ic_launcher_temp_round.xml b/app/src/main/res/mipmap-anydpi-v26/ic_launcher_temp_round.xml new file mode 100644 index 00000000..08dacb68 --- /dev/null +++ b/app/src/main/res/mipmap-anydpi-v26/ic_launcher_temp_round.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher_temp.webp b/app/src/main/res/mipmap-hdpi/ic_launcher_temp.webp new file mode 100644 index 00000000..83854c1d Binary files /dev/null and b/app/src/main/res/mipmap-hdpi/ic_launcher_temp.webp differ diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher_temp_round.webp b/app/src/main/res/mipmap-hdpi/ic_launcher_temp_round.webp new file mode 100644 index 00000000..6dbf2e77 Binary files /dev/null and b/app/src/main/res/mipmap-hdpi/ic_launcher_temp_round.webp differ diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher_temp.webp b/app/src/main/res/mipmap-mdpi/ic_launcher_temp.webp new file mode 100644 index 00000000..9b879b55 Binary files /dev/null and b/app/src/main/res/mipmap-mdpi/ic_launcher_temp.webp differ diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher_temp_round.webp b/app/src/main/res/mipmap-mdpi/ic_launcher_temp_round.webp new file mode 100644 index 00000000..dfd24231 Binary files /dev/null and b/app/src/main/res/mipmap-mdpi/ic_launcher_temp_round.webp differ diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher_temp.webp b/app/src/main/res/mipmap-xhdpi/ic_launcher_temp.webp new file mode 100644 index 00000000..b4efa44e Binary files /dev/null and b/app/src/main/res/mipmap-xhdpi/ic_launcher_temp.webp differ diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher_temp_round.webp b/app/src/main/res/mipmap-xhdpi/ic_launcher_temp_round.webp new file mode 100644 index 00000000..bcb26698 Binary files /dev/null and b/app/src/main/res/mipmap-xhdpi/ic_launcher_temp_round.webp differ diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher_temp.webp b/app/src/main/res/mipmap-xxhdpi/ic_launcher_temp.webp new file mode 100644 index 00000000..ccd89e0a Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/ic_launcher_temp.webp differ diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher_temp_round.webp b/app/src/main/res/mipmap-xxhdpi/ic_launcher_temp_round.webp new file mode 100644 index 00000000..f4b04e3a Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/ic_launcher_temp_round.webp differ diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher_temp.webp b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_temp.webp new file mode 100644 index 00000000..91502f73 Binary files /dev/null and b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_temp.webp differ diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher_temp_round.webp b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_temp_round.webp new file mode 100644 index 00000000..9d824496 Binary files /dev/null and b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_temp_round.webp differ diff --git a/app/src/main/res/values/ic_launcher_temp_background.xml b/app/src/main/res/values/ic_launcher_temp_background.xml new file mode 100644 index 00000000..94a6cd1e --- /dev/null +++ b/app/src/main/res/values/ic_launcher_temp_background.xml @@ -0,0 +1,4 @@ + + + #A386EB + \ 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 dcfc89fa..80cf6ae5 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 @@ -1,6 +1,8 @@ package one.nem.lacerta.component.scanner; +import android.Manifest; import android.app.ProgressDialog; +import android.content.pm.PackageManager; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.os.Bundle; @@ -11,6 +13,8 @@ import android.widget.Toast; import androidx.activity.EdgeToEdge; import androidx.appcompat.app.AppCompatActivity; +import androidx.core.app.ActivityCompat; +import androidx.core.content.ContextCompat; import androidx.core.graphics.Insets; import androidx.core.view.ViewCompat; import androidx.core.view.WindowInsetsCompat; @@ -92,6 +96,14 @@ public class ScannerManagerActivity extends AppCompatActivity { return insets; }); + + + if (ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA) + != PackageManager.PERMISSION_GRANTED) { + ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.CAMERA}, 1); // TODO-rca: リクエストコードを定数にする + } + + MaterialToolbar toolbar = findViewById(R.id.top_toolbar); setSupportActionBar(toolbar); Objects.requireNonNull(getSupportActionBar()).setDisplayHomeAsUpEnabled(true); @@ -116,11 +128,12 @@ public class ScannerManagerActivity extends AppCompatActivity { Toast.makeText(this, "保存処理", Toast.LENGTH_SHORT).show(); saveNewDocument(); return true; - } else if (item.getItemId() == R.id.action_insert_exist) { - // 既存ドキュメントに挿入 - Toast.makeText(this, "Work in progress", Toast.LENGTH_SHORT).show(); - insertToExistDocument(); - return true; +// } +// else if (item.getItemId() == R.id.action_insert_exist) { +// // 既存ドキュメントに挿入 +// Toast.makeText(this, "Work in progress", Toast.LENGTH_SHORT).show(); +// insertToExistDocument(); +// return true; } else if (item.getItemId() == android.R.id.home) { finish(); return true; diff --git a/component/scanner/src/main/res/menu/scanner_result_toolbar.xml b/component/scanner/src/main/res/menu/scanner_result_toolbar.xml index 3321aadc..dc815d7b 100644 --- a/component/scanner/src/main/res/menu/scanner_result_toolbar.xml +++ b/component/scanner/src/main/res/menu/scanner_result_toolbar.xml @@ -6,9 +6,9 @@ android:icon="@drawable/save_24px" android:title="Save" app:showAsAction="ifRoom"/> - + + + + + \ No newline at end of file diff --git a/component/viewer/src/main/java/one/nem/lacerta/component/viewer/RevAdapter.java b/component/viewer/src/main/java/one/nem/lacerta/component/viewer/RevAdapter.java index eb5c634f..4ad968c1 100644 --- a/component/viewer/src/main/java/one/nem/lacerta/component/viewer/RevAdapter.java +++ b/component/viewer/src/main/java/one/nem/lacerta/component/viewer/RevAdapter.java @@ -12,6 +12,7 @@ import java.text.SimpleDateFormat; import java.time.format.DateTimeFormatter; import java.util.ArrayList; +import one.nem.lacerta.component.viewer.model.RevSelectListener; import one.nem.lacerta.model.VcsRevModel; import one.nem.lacerta.utils.FeatureSwitch; @@ -19,11 +20,14 @@ public class RevAdapter extends RecyclerView.Adapter{ private ArrayList revModels; + private RevSelectListener revSelectListener; + public RevAdapter(ArrayList revModels) { this.revModels = revModels; } - public RevAdapter() { + public RevAdapter(RevSelectListener revSelectListener) { + this.revSelectListener = revSelectListener; } public void setRevModels(ArrayList revModels) { @@ -52,6 +56,13 @@ public class RevAdapter extends RecyclerView.Adapter{ } holder.revId.setText("RevID: " + revModel.getId()); + holder.itemView.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + revSelectListener.onRevSelect(revModel.getId()); + } + }); + } @Override diff --git a/component/viewer/src/main/java/one/nem/lacerta/component/viewer/ComponentViewerTopFragment.java b/component/viewer/src/main/java/one/nem/lacerta/component/viewer/ViewerListFragment.java similarity index 68% rename from component/viewer/src/main/java/one/nem/lacerta/component/viewer/ComponentViewerTopFragment.java rename to component/viewer/src/main/java/one/nem/lacerta/component/viewer/ViewerListFragment.java index 6e456d16..4c306755 100644 --- a/component/viewer/src/main/java/one/nem/lacerta/component/viewer/ComponentViewerTopFragment.java +++ b/component/viewer/src/main/java/one/nem/lacerta/component/viewer/ViewerListFragment.java @@ -1,12 +1,10 @@ package one.nem.lacerta.component.viewer; import android.app.AlertDialog; -import android.graphics.Bitmap; import android.os.Bundle; import androidx.appcompat.widget.Toolbar; import androidx.fragment.app.Fragment; -import androidx.navigation.Navigation; import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.RecyclerView; @@ -16,24 +14,24 @@ import android.view.ViewGroup; import android.widget.Toast; import java.util.ArrayList; -import java.util.concurrent.CompletableFuture; import javax.inject.Inject; import dagger.hilt.android.AndroidEntryPoint; import one.nem.lacerta.data.Document; -import one.nem.lacerta.model.document.DocumentDetail; import one.nem.lacerta.model.document.page.Page; import one.nem.lacerta.utils.FeatureSwitch; import one.nem.lacerta.utils.LacertaLogger; +import one.nem.lacerta.vcs.LacertaVcs; +import one.nem.lacerta.vcs.factory.LacertaVcsFactory; /** * A simple {@link Fragment} subclass. - * Use the {@link ComponentViewerTopFragment#newInstance} factory method to + * Use the {@link ViewerListFragment#newInstance} factory method to * create an instance of this fragment. */ @AndroidEntryPoint -public class ComponentViewerTopFragment extends Fragment { +public class ViewerListFragment extends Fragment { @Inject Document document; @@ -41,17 +39,21 @@ public class ComponentViewerTopFragment extends Fragment { @Inject LacertaLogger logger; + @Inject + LacertaVcsFactory lacertaVcsFactory; + private static final String TAG = "ComponentViewerTopFragment"; private String documentId; private String documentName; + private String revisionId; - public ComponentViewerTopFragment() { + public ViewerListFragment() { // Required empty public constructor } - public static ComponentViewerTopFragment newInstance(String documentId, String documentName) { - ComponentViewerTopFragment fragment = new ComponentViewerTopFragment(); + public static ViewerListFragment newInstance(String documentId, String documentName) { + ViewerListFragment fragment = new ViewerListFragment(); Bundle args = new Bundle(); args.putString("documentId", documentId); args.putString("documentName", documentName); @@ -59,12 +61,23 @@ public class ComponentViewerTopFragment extends Fragment { return fragment; } + public static ViewerListFragment newInstance(String documentId, String documentName, String revisionId) { + ViewerListFragment fragment = new ViewerListFragment(); + Bundle args = new Bundle(); + args.putString("documentId", documentId); + args.putString("documentName", documentName); + args.putString("revisionId", revisionId); + fragment.setArguments(args); + return fragment; + } + @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); if (getArguments() != null) { documentId = getArguments().getString("documentId"); documentName = getArguments().getString("documentName"); + revisionId = getArguments().getString("revisionId"); } } @@ -85,16 +98,34 @@ public class ComponentViewerTopFragment extends Fragment { }); recyclerView.setAdapter(viewerBodyAdapter); - if (FeatureSwitch.Viewer.showProgressBarWhenLoading) view.findViewById(R.id.loading_progress_bar).setVisibility(View.VISIBLE); - document.getDocument(documentId).thenAccept(documentDetail -> { - ArrayList pages = documentDetail.getPages(); - logger.debug(TAG, "pages.size(): " + pages.size()); - viewerBodyAdapter.setPages(pages); - getActivity().runOnUiThread(() -> { - viewerBodyAdapter.notifyItemRangeChanged(0, pages.size()); - if (FeatureSwitch.Viewer.showProgressBarWhenLoading) view.findViewById(R.id.loading_progress_bar).setVisibility(View.GONE); + if (revisionId == null) { + logger.debug(TAG, "revisionId is empty, loading latest revision"); + if (FeatureSwitch.Viewer.showProgressBarWhenLoading) view.findViewById(R.id.loading_progress_bar).setVisibility(View.VISIBLE); + document.getDocument(documentId).thenAccept(documentDetail -> { + ArrayList pages = documentDetail.getPages(); + logger.debug(TAG, "pages.size(): " + pages.size()); + viewerBodyAdapter.setPages(pages); + getActivity().runOnUiThread(() -> { + viewerBodyAdapter.notifyItemRangeChanged(0, pages.size()); + if (FeatureSwitch.Viewer.showProgressBarWhenLoading) view.findViewById(R.id.loading_progress_bar).setVisibility(View.GONE); + }); }); - }); + } else { + logger.debug(TAG, "revisionId: " + revisionId); + if (FeatureSwitch.Viewer.showProgressBarWhenLoading) view.findViewById(R.id.loading_progress_bar).setVisibility(View.VISIBLE); + LacertaVcs lacertaVcs = lacertaVcsFactory.create(documentId); + lacertaVcs.getDocumentPagePathListRev(revisionId).thenAccept(documentPathList -> { + logger.debug(TAG, "documentPathList.size(): " + documentPathList.size()); + document.getDocumentPageListByFileNameList(documentId, documentPathList).thenAccept(pages -> { + logger.debug(TAG, "pages.size(): " + pages.size()); + viewerBodyAdapter.setPages(pages); + getActivity().runOnUiThread(() -> { + viewerBodyAdapter.notifyItemRangeChanged(0, pages.size()); + if (FeatureSwitch.Viewer.showProgressBarWhenLoading) view.findViewById(R.id.loading_progress_bar).setVisibility(View.GONE); + }); + }); + }); + } return view; } @@ -123,7 +154,7 @@ public class ComponentViewerTopFragment extends Fragment { if (item.getItemId() == R.id.action_open_vcs_rev_list) { // Open vcs rev list getParentFragmentManager().beginTransaction() - .replace(R.id.nav_host_fragment, ViewerVcsRevListFragment.newInstance(documentId)) + .replace(R.id.nav_host_fragment, ViewerVcsRevListFragment.newInstance(documentId, documentName)) .commit(); return true; } else if (item.getItemId() == R.id.action_rename) { diff --git a/component/viewer/src/main/java/one/nem/lacerta/component/viewer/ViewerMainActivity.java b/component/viewer/src/main/java/one/nem/lacerta/component/viewer/ViewerMainActivity.java index 435e472c..6c079c11 100644 --- a/component/viewer/src/main/java/one/nem/lacerta/component/viewer/ViewerMainActivity.java +++ b/component/viewer/src/main/java/one/nem/lacerta/component/viewer/ViewerMainActivity.java @@ -2,7 +2,6 @@ package one.nem.lacerta.component.viewer; import android.content.Intent; import android.os.Bundle; -import android.util.Log; import android.widget.Toast; import androidx.activity.EdgeToEdge; @@ -10,12 +9,6 @@ import androidx.appcompat.app.AppCompatActivity; import androidx.core.graphics.Insets; import androidx.core.view.ViewCompat; import androidx.core.view.WindowInsetsCompat; -import androidx.fragment.app.FragmentManager; -import androidx.navigation.NavController; -import androidx.navigation.fragment.NavHostFragment; -import androidx.navigation.ui.NavigationUI; - -import com.google.android.material.bottomnavigation.BottomNavigationView; import javax.inject.Inject; @@ -62,7 +55,7 @@ public class ViewerMainActivity extends AppCompatActivity { // Navigation getSupportFragmentManager().beginTransaction() - .replace(R.id.nav_host_fragment, ComponentViewerTopFragment.newInstance(documentId, documentName)) + .replace(R.id.nav_host_fragment, ViewerListFragment.newInstance(documentId, documentName)) .commit(); } } \ No newline at end of file diff --git a/component/viewer/src/main/java/one/nem/lacerta/component/viewer/ViewerVcsRevListFragment.java b/component/viewer/src/main/java/one/nem/lacerta/component/viewer/ViewerVcsRevListFragment.java index 237c1564..a86d7e88 100644 --- a/component/viewer/src/main/java/one/nem/lacerta/component/viewer/ViewerVcsRevListFragment.java +++ b/component/viewer/src/main/java/one/nem/lacerta/component/viewer/ViewerVcsRevListFragment.java @@ -34,15 +34,17 @@ public class ViewerVcsRevListFragment extends Fragment { LacertaVcs lacertaVcs; private String documentId; + private String documentName; public ViewerVcsRevListFragment() { // Required empty public constructor } - public static ViewerVcsRevListFragment newInstance(String documentId) { + public static ViewerVcsRevListFragment newInstance(String documentId, String documentName) { ViewerVcsRevListFragment fragment = new ViewerVcsRevListFragment(); Bundle args = new Bundle(); args.putString("documentId", documentId); + args.putString("documentName", documentName); fragment.setArguments(args); return fragment; } @@ -66,17 +68,22 @@ public class ViewerVcsRevListFragment extends Fragment { // Init arg if (getArguments() != null) { this.documentId = getArguments().getString("documentId"); - logger.debug("ViewerVcsRevListFragment", "documentId: " + documentId); + logger.debug("ViewerVcsRevListFragment", "documentId: " + this.documentId); } // Init vcs - lacertaVcs = lacertaVcsFactory.create(documentId); + lacertaVcs = lacertaVcsFactory.create(this.documentId); // Init view RecyclerView recyclerView = view.findViewById(R.id.rev_list); // Init adapter - RevAdapter revAdapter = new RevAdapter(); + RevAdapter revAdapter = new RevAdapter(revisionId -> { + logger.debug("ViewerVcsRevListFragment", "Selected revisionId: " + revisionId); + getParentFragmentManager().beginTransaction() + .replace(R.id.nav_host_fragment, ViewerListFragment.newInstance(this.documentId, this.documentName, revisionId)) + .commit(); + }); // Set adapter recyclerView.setAdapter(revAdapter); diff --git a/component/viewer/src/main/java/one/nem/lacerta/component/viewer/model/RevSelectListener.java b/component/viewer/src/main/java/one/nem/lacerta/component/viewer/model/RevSelectListener.java new file mode 100644 index 00000000..f1a69486 --- /dev/null +++ b/component/viewer/src/main/java/one/nem/lacerta/component/viewer/model/RevSelectListener.java @@ -0,0 +1,5 @@ +package one.nem.lacerta.component.viewer.model; + +public interface RevSelectListener { + void onRevSelect(String revisionId); +} diff --git a/component/viewer/src/main/res/layout/activity_viewer_main.xml b/component/viewer/src/main/res/layout/activity_viewer_main.xml index 73290603..f077ceec 100644 --- a/component/viewer/src/main/res/layout/activity_viewer_main.xml +++ b/component/viewer/src/main/res/layout/activity_viewer_main.xml @@ -9,7 +9,7 @@ + app:startDestination="@id/viewerListFragment"> updateDocument(DocumentDetail detail); CompletableFuture getDocument(String documentId); + + CompletableFuture> getDocumentPageListByFileNameList(String documentId, ArrayList fileNameList); } diff --git a/data/src/main/java/one/nem/lacerta/data/impl/DocumentImpl.java b/data/src/main/java/one/nem/lacerta/data/impl/DocumentImpl.java index 7a303ec5..523c7277 100644 --- a/data/src/main/java/one/nem/lacerta/data/impl/DocumentImpl.java +++ b/data/src/main/java/one/nem/lacerta/data/impl/DocumentImpl.java @@ -173,6 +173,34 @@ public class DocumentImpl implements Document { }); } + @Override + public CompletableFuture> getDocumentPageListByFileNameList(String documentId, ArrayList fileNameList) { + return CompletableFuture.supplyAsync(() -> { + ArrayList pages = new ArrayList<>(); + FileManager fileManager; + try { + fileManager = fileManagerFactory.create(deviceInfoUtils.getExternalStorageDirectory()).resolve(documentId).resolve("raw"); + } catch (IOException e) { + logger.error(TAG, "FileManager resolve error"); + logger.trace(TAG, e.getMessage()); + logger.e_code("1210ae5b-dd2f-42ef-bc15-40b9a9bbdb16"); + return null; + } + + fileNameList.forEach(fileName -> { + try { + pages.add(new Page(fileName, fileManager.loadBitmap(fileName))); + } catch (IOException e) { + logger.error(TAG, "Bitmap decode error"); + logger.trace(TAG, e.getMessage()); + logger.e_code("6f9ba0dc-ac63-401c-8f50-a2bd9ff5cb91"); + } + }); + + return pages; + }); + } + private CompletableFuture> getPagesByXmlMeta(String documentId) { return CompletableFuture.supplyAsync(() -> { FileManager fileManager = fileManagerFactory.create(deviceInfoUtils.getExternalStorageDirectory()); diff --git a/model/src/main/java/one/nem/lacerta/model/VcsLogModel.java b/model/src/main/java/one/nem/lacerta/model/VcsLogModel.java new file mode 100644 index 00000000..366f7a22 --- /dev/null +++ b/model/src/main/java/one/nem/lacerta/model/VcsLogModel.java @@ -0,0 +1,64 @@ +package one.nem.lacerta.model; + +import java.util.Date; + +public class VcsLogModel { + + String id; + String documentId; + String branchName; + String action; + Date createdAt; + + public VcsLogModel(String id, String documentId, String branchName, String action, Date createdAt) { + this.id = id; + this.documentId = documentId; + this.branchName = branchName; + this.action = action; + this.createdAt = createdAt; + } + + // Empty constructor + public VcsLogModel() { + } + + public String getId() { + return id; + } + + public String getDocumentId() { + return documentId; + } + + public String getBranchName() { + return branchName; + } + + public String getAction() { + return action; + } + + public Date getCreatedAt() { + return createdAt; + } + + public void setId(String id) { + this.id = id; + } + + public void setDocumentId(String documentId) { + this.documentId = documentId; + } + + public void setBranchName(String branchName) { + this.branchName = branchName; + } + + public void setAction(String action) { + this.action = action; + } + + public void setCreatedAt(Date createdAt) { + this.createdAt = createdAt; + } +} diff --git a/processor/src/main/java/one/nem/lacerta/processor/impl/DocumentProcessorImpl.java b/processor/src/main/java/one/nem/lacerta/processor/impl/DocumentProcessorImpl.java index d2312b91..fea8396f 100644 --- a/processor/src/main/java/one/nem/lacerta/processor/impl/DocumentProcessorImpl.java +++ b/processor/src/main/java/one/nem/lacerta/processor/impl/DocumentProcessorImpl.java @@ -74,18 +74,24 @@ public class DocumentProcessorImpl implements DocumentProcessor{ logger.debug("addNewPageToLast", "called"); String filename = UUID.randomUUID().toString() + ".png"; // TODO-rca: 拡張子を動的にする - Page page = new Page(); - page.setFileName(filename); - page.setBitmap(bitmap); - this.documentDetail.getPages().add(page); + lacertaVcs.insertPage(this.documentDetail.getPages().size(), filename); - lacertaVcs.insertPage(documentDetail.getPages().size(), filename); + try { + Page page = new Page(); + page.setFileName(filename); + page.setBitmap(bitmap); + this.documentDetail.getPages().add(page); - this.fileManager.getNewInstance().createDirectoryIfNotExist(DEFAULT_SAVE_DIR).resolve(DEFAULT_SAVE_DIR).saveBitmap(bitmap, filename); - - logger.info("addNewPageToLast", "finished"); - logger.info("addNewPageToLast", "filename: " + filename); + this.fileManager.getNewInstance().createDirectoryIfNotExist(DEFAULT_SAVE_DIR).resolve(DEFAULT_SAVE_DIR).saveBitmap(bitmap, filename); + logger.info("addNewPageToLast", "finished"); + logger.info("addNewPageToLast", "filename: " + filename); + } catch (Exception e) { + logger.error("addNewPageToLast", "failed: Unknown error"); + logger.e_code("d9191286-6092-40b3-80ed-9239106a8c65"); + // Recover (Undo latest action) + lacertaVcs.undo(); + } return this; } diff --git a/source/src/main/java/one/nem/lacerta/source/database/LacertaDatabase.java b/source/src/main/java/one/nem/lacerta/source/database/LacertaDatabase.java index e3d6a6b9..47e0e4bb 100644 --- a/source/src/main/java/one/nem/lacerta/source/database/LacertaDatabase.java +++ b/source/src/main/java/one/nem/lacerta/source/database/LacertaDatabase.java @@ -19,7 +19,7 @@ import one.nem.lacerta.source.database.dao.LibraryDao; import one.nem.lacerta.source.database.dao.VcsRevDao; import one.nem.lacerta.source.database.dao.VcsLogDao; -@Database(entities = {TagEntity.class, DocumentEntity.class, LibraryEntity.class, VcsRevEntity.class, VcsLogEntity.class, FolderEntity.class}, version = 5) +@Database(entities = {TagEntity.class, DocumentEntity.class, LibraryEntity.class, VcsRevEntity.class, VcsLogEntity.class, FolderEntity.class}, version = 4) public abstract class LacertaDatabase extends RoomDatabase { public abstract TagDao tagDao(); public abstract DocumentDao documentDao(); diff --git a/source/src/main/java/one/nem/lacerta/source/database/dao/VcsLogDao.java b/source/src/main/java/one/nem/lacerta/source/database/dao/VcsLogDao.java index 7721555f..b8923de9 100644 --- a/source/src/main/java/one/nem/lacerta/source/database/dao/VcsLogDao.java +++ b/source/src/main/java/one/nem/lacerta/source/database/dao/VcsLogDao.java @@ -52,4 +52,11 @@ public interface VcsLogDao { @Update void updateAll(List vcsLogs); + + // Delete + @Query("DELETE FROM vcs_log WHERE id = :id") + void deleteById(String id); + + @Query("DELETE FROM vcs_log WHERE id = (SELECT id FROM vcs_log WHERE document_id = :documentId ORDER BY created_at DESC LIMIT 1)") + void deleteLatestByDocumentId(String documentId); } diff --git a/source/src/main/java/one/nem/lacerta/source/database/dao/VcsRevDao.java b/source/src/main/java/one/nem/lacerta/source/database/dao/VcsRevDao.java index 3213ca28..2df991fe 100644 --- a/source/src/main/java/one/nem/lacerta/source/database/dao/VcsRevDao.java +++ b/source/src/main/java/one/nem/lacerta/source/database/dao/VcsRevDao.java @@ -21,7 +21,7 @@ public interface VcsRevDao { @Query("SELECT * FROM vcs_rev WHERE id IN (:ids)") List findByIds(List ids); - @Query("SELECT * FROM vcs_rev WHERE document_id = :documentId") + @Query("SELECT * FROM vcs_rev WHERE document_id = :documentId ORDER BY created_at ASC") List findByDocumentId(String documentId); @Query("SELECT * FROM vcs_rev WHERE document_id = :documentId ORDER BY created_at DESC LIMIT 1") diff --git a/source/src/main/java/one/nem/lacerta/source/database/entity/VcsLogEntity.java b/source/src/main/java/one/nem/lacerta/source/database/entity/VcsLogEntity.java index 17628e8e..152d5333 100644 --- a/source/src/main/java/one/nem/lacerta/source/database/entity/VcsLogEntity.java +++ b/source/src/main/java/one/nem/lacerta/source/database/entity/VcsLogEntity.java @@ -40,6 +40,12 @@ public class VcsLogEntity { @ColumnInfo(name = "created_at") public Date createdAt; + /** + * アクションタイプ + */ + @ColumnInfo(name = "action_type") + public String actionType; + /** * 発生アクション */ diff --git a/utils/src/main/java/one/nem/lacerta/utils/FeatureSwitch.java b/utils/src/main/java/one/nem/lacerta/utils/FeatureSwitch.java index 5c808841..aae02397 100644 --- a/utils/src/main/java/one/nem/lacerta/utils/FeatureSwitch.java +++ b/utils/src/main/java/one/nem/lacerta/utils/FeatureSwitch.java @@ -4,6 +4,8 @@ public class FeatureSwitch { public static class Meta { public static boolean canOverrideSwitch = false; + + public static boolean disableDynamicColor = false; } public static class RecyclerView { diff --git a/vcs/src/main/java/one/nem/lacerta/vcs/LacertaVcs.java b/vcs/src/main/java/one/nem/lacerta/vcs/LacertaVcs.java index 664c45e1..cccc44ac 100644 --- a/vcs/src/main/java/one/nem/lacerta/vcs/LacertaVcs.java +++ b/vcs/src/main/java/one/nem/lacerta/vcs/LacertaVcs.java @@ -3,7 +3,9 @@ package one.nem.lacerta.vcs; import java.util.ArrayList; import java.util.concurrent.CompletableFuture; +import one.nem.lacerta.model.VcsLogModel; import one.nem.lacerta.model.VcsRevModel; +import one.nem.lacerta.model.document.DocumentDetail; public interface LacertaVcs { @@ -14,12 +16,20 @@ public interface LacertaVcs { public void deletePage(int index); + public void undo(); + public void createDocument(String documentId); public void generateRevisionAtCurrent(String message); public CompletableFuture> getRevisionHistory(); + public CompletableFuture> getLogHistory(); + + public CompletableFuture> getLogHistoryInRev(String revId); + + public CompletableFuture> getDocumentPagePathListRev(String revId); + // debug public void printLog(); 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 93d701d2..5e193bcd 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,15 +1,13 @@ package one.nem.lacerta.vcs.impl; -import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.List; import java.util.UUID; import java.util.concurrent.CompletableFuture; -import javax.inject.Inject; - import dagger.assisted.Assisted; import dagger.assisted.AssistedInject; +import one.nem.lacerta.model.VcsLogModel; import one.nem.lacerta.model.VcsRevModel; import one.nem.lacerta.source.database.LacertaDatabase; import one.nem.lacerta.source.database.entity.VcsLogEntity; @@ -18,7 +16,9 @@ import one.nem.lacerta.utils.LacertaLogger; import one.nem.lacerta.vcs.ActionType; import one.nem.lacerta.vcs.LacertaVcs; import one.nem.lacerta.vcs.internal.JsonUtils; +import one.nem.lacerta.vcs.model.action.DeletePage; import one.nem.lacerta.vcs.model.action.InsertPage; +import one.nem.lacerta.vcs.model.action.UpdatePage; public class LacertaVcsImpl implements LacertaVcs { @@ -57,6 +57,7 @@ public class LacertaVcsImpl implements LacertaVcs { vcsLogEntity.documentId = documentId; vcsLogEntity.branchName = "master"; vcsLogEntity.createdAt = new java.util.Date(); + vcsLogEntity.actionType = ActionType.INSERT_PAGE.getValue(); vcsLogEntity.action = JsonUtils.toJson(insertPage); database.vcsLogDao().insert(vcsLogEntity); } @@ -66,6 +67,11 @@ public class LacertaVcsImpl implements LacertaVcs { } + @Override + public void undo() { + database.vcsLogDao().deleteLatestByDocumentId(documentId); + } + @Override public void createDocument(String documentId) { logger.debug(TAG, "createDocument"); @@ -75,7 +81,8 @@ public class LacertaVcsImpl implements LacertaVcs { vcsLogEntity.documentId = documentId; vcsLogEntity.branchName = "master"; vcsLogEntity.createdAt = new java.util.Date(); - vcsLogEntity.action = "ph-createDocument"; + vcsLogEntity.actionType = ActionType.CREATE_DOCUMENT.getValue(); + vcsLogEntity.action = ""; database.vcsLogDao().insert(vcsLogEntity); } @@ -147,6 +154,108 @@ public class LacertaVcsImpl implements LacertaVcs { }); } + @Override + public CompletableFuture> getLogHistory() { + return null; + } + + @Override + public CompletableFuture> getLogHistoryInRev(String revId) { + return CompletableFuture.supplyAsync(() -> { + logger.debug(TAG, "getLogHistoryInRev"); + ArrayList vcsLogModels = new ArrayList<>(); + + VcsRevEntity vcsRevEntity = database.vcsRevDao().findById(revId); + ArrayList vcsLogEntities = getLogInRevAsync(vcsRevEntity).join(); // TODO-rca: リファクタリング + vcsLogEntities.forEach(vcsLogEntity -> { + VcsLogModel vcsLogModel = new VcsLogModel(); + vcsLogModel.setId(vcsLogEntity.id); + vcsLogModel.setDocumentId(vcsLogEntity.documentId); + vcsLogModel.setBranchName(vcsLogEntity.branchName); + vcsLogModel.setCreatedAt(vcsLogEntity.createdAt); + vcsLogModel.setAction(vcsLogEntity.action); + vcsLogModels.add(vcsLogModel); + }); + + return vcsLogModels; + }); + } + + private CompletableFuture> getRevBeforeTargetIdAsync(String revId){ + return CompletableFuture.supplyAsync(() -> { + ArrayList vcsRevEntities = new ArrayList<>(database.vcsRevDao().findByDocumentId(this.documentId)); + ArrayList vcsRevEntitiesBeforeTarget = new ArrayList<>(); + vcsRevEntities.forEach(vcsRevEntity -> { + if(vcsRevEntity.id.equals(revId)){ + vcsRevEntitiesBeforeTarget.add(vcsRevEntity); + return; + } + vcsRevEntitiesBeforeTarget.add(vcsRevEntity); + }); + logger.debug(TAG, "getRevBeforeTargetIdAsync finished\nResult size: " + vcsRevEntitiesBeforeTarget.size()); + return vcsRevEntitiesBeforeTarget; + }); + } + + private CompletableFuture> getLogInRevsAsync(ArrayList vcsRevEntities){ + return CompletableFuture.supplyAsync(() -> { + List logIds = new ArrayList<>(); + vcsRevEntities.forEach(vcsRevEntity -> { + logIds.addAll(vcsRevEntity.logIds); + }); + // TODO-rca: ソートしないといけないかも(順番が保証されているわけではない + 順番が変わるとほぼ確実に壊れる) + ArrayList vcsLogEntities = new ArrayList<>(database.vcsLogDao().findByIds(logIds)); + logger.debug(TAG, "getLogInRevsAsync finished\nResult size: " + vcsLogEntities.size()); + return vcsLogEntities; + }); + } + + private CompletableFuture> getLogInRevAsync(VcsRevEntity revEntity) { + return CompletableFuture.supplyAsync(() -> { + ArrayList vcsLogEntities = new ArrayList<>(database.vcsLogDao().findByIds(revEntity.logIds)); + logger.debug(TAG, "getLogInRevAsync finished\nResult size: " + vcsLogEntities.size()); + return vcsLogEntities; + }); + } + + @Override + public CompletableFuture> getDocumentPagePathListRev(String revId) { + return CompletableFuture.supplyAsync(() -> { + logger.debug(TAG, "getDocumentPagePathListRev"); + ArrayList vcsLogEntities = getRevBeforeTargetIdAsync(revId).thenCompose(this::getLogInRevsAsync).join(); + + ArrayList fileNameList = new ArrayList<>(); + for(VcsLogEntity vcsLogEntity : vcsLogEntities){ + logger.debug(TAG, "getDocumentPagePathListRev: processing " + vcsLogEntity.id + "(Type: " + vcsLogEntity.actionType + ")"); + if (vcsLogEntity.actionType.equals(ActionType.INSERT_PAGE.getValue())){ + InsertPage insertPage = (InsertPage) JsonUtils.fromJson(vcsLogEntity.action, ActionType.INSERT_PAGE); + logger.debug(TAG, "getDocumentPagePathListRev: Inserting " + insertPage.getFileName() + " at " + insertPage.getIndex()); + if (fileNameList.size() <= insertPage.getIndex()) { + logger.debug(TAG, "Index out of range, appending"); + fileNameList.add(insertPage.getFileName()); + } else { + fileNameList.add(insertPage.getIndex(), insertPage.getFileName()); + } + } else if (vcsLogEntity.actionType.equals(ActionType.UPDATE_PAGE.getValue())){ + UpdatePage updatePage = (UpdatePage) JsonUtils.fromJson(vcsLogEntity.action, ActionType.UPDATE_PAGE); + logger.debug(TAG, "getDocumentPagePathListRev: Updating " + updatePage.getFileName() + " at " + updatePage.getIndex()); + fileNameList.set(updatePage.getIndex(), updatePage.getFileName()); + } else if (vcsLogEntity.actionType.equals(ActionType.DELETE_PAGE.getValue())){ + DeletePage deletePage = (DeletePage) JsonUtils.fromJson(vcsLogEntity.action, ActionType.DELETE_PAGE); + logger.debug(TAG, "getDocumentPagePathListRev: Deleting " + deletePage.getIndex()); + fileNameList.remove(deletePage.getIndex()); + } else if (vcsLogEntity.actionType.equals(ActionType.CREATE_DOCUMENT.getValue())) { + // Ignore + logger.debug(TAG, "getDocumentPagePathListRev: Ignored action type: " + vcsLogEntity.actionType); + } else { + logger.error(TAG, "getDocumentPagePathListRev: Unknown action type"); + } + } + + return fileNameList; + }); + } + @Override public void printLog() { logger.debug(TAG, "printLog");