Merge pull request #118 from lacerta-doc/improve/ui3

UI改善
This commit is contained in:
ろむねこ 2024-01-27 16:33:22 +09:00 committed by GitHub
commit 77830ca96b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
45 changed files with 768 additions and 66 deletions

View File

@ -8,9 +8,11 @@ import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager; import androidx.fragment.app.FragmentManager;
import androidx.fragment.app.FragmentTransaction; import androidx.fragment.app.FragmentTransaction;
import androidx.navigation.NavController; import androidx.navigation.NavController;
import androidx.navigation.NavOptions;
import androidx.navigation.fragment.NavHostFragment; import androidx.navigation.fragment.NavHostFragment;
import androidx.navigation.ui.NavigationUI; import androidx.navigation.ui.NavigationUI;
import android.app.ActivityOptions;
import android.content.Intent; import android.content.Intent;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;
import android.graphics.Color; import android.graphics.Color;
@ -60,6 +62,19 @@ public class MainActivity extends AppCompatActivity implements FragmentNavigatio
assert navHostFragment != null; assert navHostFragment != null;
NavController navController = navHostFragment.getNavController(); NavController navController = navHostFragment.getNavController();
NavigationUI.setupWithNavController(bottomNavigationView, navController); NavigationUI.setupWithNavController(bottomNavigationView, navController);
bottomNavigationView.setOnItemSelectedListener(item -> {
NavOptions navOptions = new NavOptions.Builder()
.setLaunchSingleTop(true)
.setEnterAnim(androidx.navigation.ui.R.anim.nav_default_enter_anim)
.setExitAnim(androidx.navigation.ui.R.anim.nav_default_exit_anim)
.setPopEnterAnim(androidx.navigation.ui.R.anim.nav_default_pop_enter_anim)
.setPopExitAnim(androidx.navigation.ui.R.anim.nav_default_pop_exit_anim)
.build();
navController.navigate(item.getItemId(), null, navOptions);
return true;
});
} }
catch (Exception e) { catch (Exception e) {
Log.e("Init", "Failed to init navigation"); Log.e("Init", "Failed to init navigation");
@ -83,7 +98,8 @@ public class MainActivity extends AppCompatActivity implements FragmentNavigatio
findViewById(R.id.scanFab).setOnClickListener(v -> { findViewById(R.id.scanFab).setOnClickListener(v -> {
Toast.makeText(this, "Scan", Toast.LENGTH_SHORT).show(); Toast.makeText(this, "Scan", Toast.LENGTH_SHORT).show();
Intent intent = new Intent(this.getApplicationContext(), ScannerManagerActivity.class); Intent intent = new Intent(this.getApplicationContext(), ScannerManagerActivity.class);
startActivity(intent); // startActivity(intent);
startActivity(intent, ActivityOptions.makeCustomAnimation(this, one.nem.lacerta.shared.ui.R.anim.nav_up_enter_anim, one.nem.lacerta.shared.ui.R.anim.nav_up_exit_anim).toBundle());
}); });
} }

View File

@ -42,4 +42,6 @@ dependencies {
implementation project(':utils') implementation project(':utils')
implementation project(':model') implementation project(':model')
implementation project(':data') implementation project(':data')
implementation project(':vcs')
} }

View File

@ -79,8 +79,8 @@ public class LacertaSelectDirDialog extends DialogFragment {
LayoutInflater inflater = requireActivity().getLayoutInflater(); LayoutInflater inflater = requireActivity().getLayoutInflater();
View view = inflater.inflate(R.layout.lacerta_dialog_select_dir, null); View view = inflater.inflate(R.layout.lacerta_dialog_select_dir, null);
// 高さを画面の60%にする // 高さを画面の40%にする
int height = (int) (getResources().getDisplayMetrics().heightPixels * 0.6); int height = (int) (getResources().getDisplayMetrics().heightPixels * 0.4);
view.setMinimumHeight(height); view.setMinimumHeight(height);
this.recyclerView = view.findViewById(R.id.select_dir_recycler_view); this.recyclerView = view.findViewById(R.id.select_dir_recycler_view);

View File

@ -0,0 +1,112 @@
package one.nem.lacerta.component.common;
import android.app.Dialog;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.Toast;
import androidx.fragment.app.DialogFragment;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
import javax.inject.Inject;
import dagger.hilt.android.AndroidEntryPoint;
import one.nem.lacerta.data.LacertaLibrary;
import one.nem.lacerta.utils.LacertaLogger;
import one.nem.lacerta.vcs.LacertaVcs;
import one.nem.lacerta.vcs.factory.LacertaVcsFactory;
@AndroidEntryPoint
public class LacertaSelectRevDialog extends DialogFragment {
@Inject
LacertaVcsFactory lacertaVcsFactory;
@Inject
LacertaLogger logger;
String title;
String documentId;
String message;
String negativeButtonText;
LacertaSelectRevDialogListener listener;
public LacertaSelectRevDialog setTitle(String title) {
this.title = title;
return this;
}
public LacertaSelectRevDialog setDocumentId(String documentId) {
this.documentId = documentId;
return this;
}
public LacertaSelectRevDialog setMessage(String message) {
this.message = message;
return this;
}
public LacertaSelectRevDialog setNegativeButtonText(String negativeButtonText) {
this.negativeButtonText = negativeButtonText;
return this;
}
public LacertaSelectRevDialog setListener(LacertaSelectRevDialogListener listener) {
this.listener = listener;
return this;
}
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
super.onCreateDialog(savedInstanceState);
MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(getActivity());
LayoutInflater inflater = requireActivity().getLayoutInflater();
View view = inflater.inflate(R.layout.lacerta_dialog_select_rev, null);
int height = (int) (getResources().getDisplayMetrics().heightPixels * 0.4);
view.setMinimumHeight(height);
RecyclerView recyclerView = view.findViewById(R.id.select_rev_recycler_view);
SelectRevDialogItemAdapter adapter = new SelectRevDialogItemAdapter(revId -> {
if (listener != null) {
listener.onItemSelected(revId);
dismiss();
}
});
recyclerView.setAdapter(adapter);
recyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
builder.setView(view);
if (this.documentId == null) {
logger.error("SelectRevDialog", "documentId is null");
logger.e_code("0296fb0c-07a3-4971-a280-bd1a61461bb7");
Toast.makeText(getContext(), "Sorry, something went wrong", Toast.LENGTH_SHORT).show();
dismiss();
}
LacertaVcs lacertaVcs = lacertaVcsFactory.create(this.documentId);
lacertaVcs.getRevisionHistory().thenAccept(revList -> {
adapter.setRevList(revList);
adapter.notifyDataSetChanged(); // TODO-rca:時間に応じてアニメーションをつける
});
builder.setTitle(title == null ? "Select Rev" : title);
builder.setMessage(message == null ? "Select Rev" : message);
builder.setNegativeButton(negativeButtonText == null ? "Cancel" : negativeButtonText, (dialog, which) -> {
if (listener != null) {
listener.onDialogCanceled();
dismiss();
}
});
return builder.create();
}
}

View File

@ -0,0 +1,6 @@
package one.nem.lacerta.component.common;
public interface LacertaSelectRevDialogInternalListener {
void onItemSelected(String revId);
}

View File

@ -0,0 +1,8 @@
package one.nem.lacerta.component.common;
public interface LacertaSelectRevDialogListener {
void onItemSelected(String revId);
void onDialogCanceled();
}

View File

@ -0,0 +1,66 @@
package one.nem.lacerta.component.common;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import one.nem.lacerta.model.VcsRevModel;
public class SelectRevDialogItemAdapter extends RecyclerView.Adapter<SelectRevDialogItemAdapter.SelectRevDialogItemViewHolder>{
ArrayList<VcsRevModel> revList;
LacertaSelectRevDialogInternalListener listener;
public SelectRevDialogItemAdapter(LacertaSelectRevDialogInternalListener listener) {
this.listener = listener;
}
public void setRevList(ArrayList<VcsRevModel> revList) {
this.revList = revList;
}
@NonNull
@Override
public SelectRevDialogItemAdapter.SelectRevDialogItemViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.common_rev_list_item, parent, false);
return new SelectRevDialogItemAdapter.SelectRevDialogItemViewHolder(view);
}
@Override
public void onBindViewHolder(@NonNull SelectRevDialogItemAdapter.SelectRevDialogItemViewHolder holder, int position) {
VcsRevModel rev = revList.get(position);
holder.title.setText(rev.getCommitMessage());
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
holder.description.setText(simpleDateFormat.format(rev.getCreatedAt()));
holder.revId.setText("RevID: " + rev.getId());
holder.itemView.setOnClickListener(v -> listener.onItemSelected(rev.getId()));
}
@Override
public int getItemCount() {
return this.revList == null ? 0 : this.revList.size();
}
public class SelectRevDialogItemViewHolder extends RecyclerView.ViewHolder {
TextView title;
TextView description;
TextView revId;
public SelectRevDialogItemViewHolder(@NonNull View itemView) {
super(itemView);
title = itemView.findViewById(R.id.rev_item_title);
description = itemView.findViewById(R.id.rev_item_detail);
revId = itemView.findViewById(R.id.rev_item_id);
}
}
}

View File

@ -0,0 +1,55 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingHorizontal="24dp"
android:paddingVertical="16dp">
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/item_text_container"
android:layout_width="0dp"
android:layout_height="wrap_content"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<TextView
android:id="@+id/rev_item_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="16sp"
android:layout_marginEnd="8dp"
android:text="Placeholder Title"
android:textColor="@color/colorOnSurface"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/rev_item_detail"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="14sp"
android:text="2023/01/01 00:00:00 - hogehoge"
android:textColor="@color/colorOnSurfaceSecondary"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/rev_item_title" />
<TextView
android:id="@+id/rev_item_id"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="12sp"
android:text="dd76a029-51f1-40f7-b316-a0c74bbfebb1(Example)"
android:textColor="@color/colorOnSurfaceSecondary"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/rev_item_detail" />
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/select_rev_recycler_view"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</FrameLayout>

View File

@ -21,7 +21,11 @@ import javax.inject.Inject;
import dagger.hilt.android.AndroidEntryPoint; import dagger.hilt.android.AndroidEntryPoint;
import one.nem.lacerta.component.common.LacertaSelectDirDialog; import one.nem.lacerta.component.common.LacertaSelectDirDialog;
import one.nem.lacerta.component.common.LacertaSelectDirDialogListener; import one.nem.lacerta.component.common.LacertaSelectDirDialogListener;
import one.nem.lacerta.component.common.LacertaSelectRevDialog;
import one.nem.lacerta.component.common.LacertaSelectRevDialogListener;
import one.nem.lacerta.data.Document; 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.page.Page;
import one.nem.lacerta.utils.FeatureSwitch; import one.nem.lacerta.utils.FeatureSwitch;
import one.nem.lacerta.utils.LacertaLogger; import one.nem.lacerta.utils.LacertaLogger;
@ -39,6 +43,9 @@ public class ViewerListFragment extends Fragment {
@Inject @Inject
Document document; Document document;
@Inject
LacertaLibrary lacertaLibrary;
@Inject @Inject
LacertaLogger logger; LacertaLogger logger;
@ -92,7 +99,7 @@ public class ViewerListFragment extends Fragment {
// Toolbar // Toolbar
Toolbar toolbar = view.findViewById(R.id.toolbar); Toolbar toolbar = view.findViewById(R.id.toolbar);
toolbarSetup(toolbar, true, this.documentName == null ? "Document" : this.documentName); toolbarSetup(toolbar, true, this.documentName == null ? "Document" : this.documentName, null);
RecyclerView recyclerView = view.findViewById(R.id.body_recycler_view); RecyclerView recyclerView = view.findViewById(R.id.body_recycler_view);
recyclerView.setLayoutManager(new LinearLayoutManager(getContext())); recyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
@ -112,6 +119,8 @@ public class ViewerListFragment extends Fragment {
viewerBodyAdapter.notifyItemRangeChanged(0, pages.size()); viewerBodyAdapter.notifyItemRangeChanged(0, pages.size());
if (FeatureSwitch.Viewer.showProgressBarWhenLoading) view.findViewById(R.id.loading_progress_bar).setVisibility(View.GONE); if (FeatureSwitch.Viewer.showProgressBarWhenLoading) view.findViewById(R.id.loading_progress_bar).setVisibility(View.GONE);
}); });
}).thenCompose(aVoid -> lacertaLibrary.getPublicPath(documentId, ListItemType.ITEM_TYPE_DOCUMENT)).thenAccept(publicPath -> {
updateToolbarSubtitle(toolbar, "/" + publicPath.parent().getStringPath());
}); });
} else { } else {
logger.debug(TAG, "revisionId: " + revisionId); logger.debug(TAG, "revisionId: " + revisionId);
@ -127,23 +136,35 @@ public class ViewerListFragment extends Fragment {
if (FeatureSwitch.Viewer.showProgressBarWhenLoading) view.findViewById(R.id.loading_progress_bar).setVisibility(View.GONE); if (FeatureSwitch.Viewer.showProgressBarWhenLoading) view.findViewById(R.id.loading_progress_bar).setVisibility(View.GONE);
}); });
}); });
}).thenCompose(aVoid -> lacertaLibrary.getPublicPath(documentId, ListItemType.ITEM_TYPE_DOCUMENT)).thenAccept(publicPath -> {
updateToolbarSubtitle(toolbar, "/" + publicPath.parent().getStringPath()); // TODO-rca: リビジョンの場合はリビジョン名を表示する?
}); });
} }
return view; return view;
} }
/**
* Toolbarのサブタイトルを更新
* @param subtitle サブタイトル
*/
private void updateToolbarSubtitle(Toolbar toolbar, String subtitle) {
getActivity().runOnUiThread(() -> {
toolbar.setSubtitle(subtitle);
});
}
/** /**
* ToolbarをInitする * ToolbarをInitする
* *
* @param toolbar Toolbar * @param toolbar Toolbar
* @param showBackButton 戻るボタンを表示するか * @param showCloseButton 戻るボタンを表示するか
* @param title タイトル * @param title タイトル
*/ */
private void toolbarSetup(Toolbar toolbar, boolean showBackButton, String title) { private void toolbarSetup(Toolbar toolbar, boolean showCloseButton, String title, String Subtitle) {
getActivity().runOnUiThread(() -> { getActivity().runOnUiThread(() -> {
if (showBackButton) { if (showCloseButton) {
toolbar.setNavigationIcon(one.nem.lacerta.shared.ui.R.drawable.arrow_back_24px); toolbar.setNavigationIcon(one.nem.lacerta.shared.ui.R.drawable.close_24px);
toolbar.setNavigationOnClickListener(v -> { toolbar.setNavigationOnClickListener(v -> {
// Stop Activity // Stop Activity
getActivity().finish(); getActivity().finish();
@ -152,13 +173,26 @@ public class ViewerListFragment extends Fragment {
toolbar.setNavigationIcon(null); toolbar.setNavigationIcon(null);
} }
toolbar.setTitle(title); toolbar.setTitle(title);
if (Subtitle != null) toolbar.setSubtitle(Subtitle);
toolbar.inflateMenu(R.menu.viewer_menu); toolbar.inflateMenu(R.menu.viewer_menu);
toolbar.setOnMenuItemClickListener(item -> { toolbar.setOnMenuItemClickListener(item -> {
if (item.getItemId() == R.id.action_open_vcs_rev_list) { if (item.getItemId() == R.id.action_open_vcs_rev_list) {
// Open vcs rev list LacertaSelectRevDialog lacertaSelectRevDialog = new LacertaSelectRevDialog();
lacertaSelectRevDialog.setDocumentId(this.documentId).setTitle("リビジョンの選択").setMessage("リビジョンを選択してください。").setNegativeButtonText("キャンセル");
lacertaSelectRevDialog.setListener(new LacertaSelectRevDialogListener() {
@Override
public void onItemSelected(String revId) {
getParentFragmentManager().beginTransaction() getParentFragmentManager().beginTransaction()
.replace(R.id.nav_host_fragment, ViewerVcsRevListFragment.newInstance(documentId, documentName)) .replace(R.id.nav_host_fragment, ViewerListFragment.newInstance(documentId, documentName, revisionId))
.commit(); .commit();
}
@Override
public void onDialogCanceled() {
logger.debug(TAG, "Canceled");
}
});
lacertaSelectRevDialog.show(getParentFragmentManager(), "select_rev_dialog");
return true; return true;
} else if (item.getItemId() == R.id.action_rename) { } else if (item.getItemId() == R.id.action_rename) {
@ -205,7 +239,6 @@ public class ViewerListFragment extends Fragment {
builder.show(); builder.show();
return true; return true;
} else if (item.getItemId() == R.id.action_move) { } else if (item.getItemId() == R.id.action_move) {
// Toast.makeText(getContext(), "Work in progress", Toast.LENGTH_SHORT).show();
LacertaSelectDirDialog lacertaSelectDirDialog = new LacertaSelectDirDialog(); LacertaSelectDirDialog lacertaSelectDirDialog = new LacertaSelectDirDialog();
lacertaSelectDirDialog.setListener(new LacertaSelectDirDialogListener() { lacertaSelectDirDialog.setListener(new LacertaSelectDirDialogListener() {
@Override @Override

View File

@ -5,6 +5,7 @@ import android.os.Bundle;
import android.widget.Toast; import android.widget.Toast;
import androidx.activity.EdgeToEdge; import androidx.activity.EdgeToEdge;
import androidx.activity.OnBackPressedCallback;
import androidx.appcompat.app.AppCompatActivity; import androidx.appcompat.app.AppCompatActivity;
import androidx.core.graphics.Insets; import androidx.core.graphics.Insets;
import androidx.core.view.ViewCompat; import androidx.core.view.ViewCompat;
@ -41,6 +42,9 @@ public class ViewerMainActivity extends AppCompatActivity {
return insets; return insets;
}); });
// // Transition
// overridePendingTransition(one.nem.lacerta.shared.ui.R.anim.nav_up_enter_anim, one.nem.lacerta.shared.ui.R.anim.nav_up_exit_anim);
Intent intent = getIntent(); Intent intent = getIntent();
try { try {
documentId = intent.getStringExtra("documentId"); documentId = intent.getStringExtra("documentId");
@ -58,4 +62,5 @@ public class ViewerMainActivity extends AppCompatActivity {
.replace(R.id.nav_host_fragment, ViewerListFragment.newInstance(documentId, documentName)) .replace(R.id.nav_host_fragment, ViewerListFragment.newInstance(documentId, documentName))
.commit(); .commit();
} }
} }

View File

@ -13,7 +13,7 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content"> android:layout_height="wrap_content">
<com.google.android.material.appbar.CollapsingToolbarLayout <com.google.android.material.appbar.SubtitleCollapsingToolbarLayout
android:id="@+id/collapsing_toolbar" android:id="@+id/collapsing_toolbar"
app:contentScrim="@color/colorSecondaryContainer" app:contentScrim="@color/colorSecondaryContainer"
android:background="@color/colorSurface" android:background="@color/colorSurface"
@ -35,7 +35,7 @@
app:layout_collapseMode="pin" app:layout_collapseMode="pin"
app:title="Title" /> app:title="Title" />
</com.google.android.material.appbar.CollapsingToolbarLayout> </com.google.android.material.appbar.SubtitleCollapsingToolbarLayout>
<ProgressBar <ProgressBar

View File

@ -1,5 +1,6 @@
package one.nem.lacerta.feature.home; package one.nem.lacerta.feature.home;
import android.app.ActivityOptions;
import android.content.Intent; import android.content.Intent;
import android.os.Bundle; import android.os.Bundle;
import android.util.Log; import android.util.Log;
@ -89,7 +90,7 @@ public class HomeTopFragment extends Fragment {
Log.d("HomeTopFragment", "onDocumentSelect: " + documentId + " " + documentName); Log.d("HomeTopFragment", "onDocumentSelect: " + documentId + " " + documentName);
intent.putExtra("documentId", documentId); intent.putExtra("documentId", documentId);
intent.putExtra("documentName", documentName); intent.putExtra("documentName", documentName);
startActivity(intent); startActivity(intent, ActivityOptions.makeCustomAnimation(getContext(), one.nem.lacerta.shared.ui.R.anim.nav_up_enter_anim, one.nem.lacerta.shared.ui.R.anim.nav_up_exit_anim).toBundle());
} }
}); });
recyclerView.setAdapter(listItemAdapter); recyclerView.setAdapter(listItemAdapter);
@ -106,20 +107,22 @@ public class HomeTopFragment extends Fragment {
} }
private void updateList() { private void updateList() {
long startTime = System.currentTimeMillis();
lacertaLibrary.getRecentDocument(10).thenAccept(listItems -> { lacertaLibrary.getRecentDocument(10).thenAccept(listItems -> {
long endTime = System.currentTimeMillis();
if (listItems == null) { if (listItems == null) {
return; return;
} }
this.listItemAdapter.setListItems(listItems); this.listItemAdapter.setListItems(listItems);
if (endTime - startTime > 500) { // 500ms以上かかった場合は表示アニメーションをする
getActivity().runOnUiThread(() -> { getActivity().runOnUiThread(() -> {
Log.d("HomeTopFragment", "onViewCreated: " + listItems.size()); this.listItemAdapter.notifyItemRangeInserted(0, listItems.size());
if (FeatureSwitch.RecyclerView.useSimpleNotifyMethod) {
this.listItemAdapter.notifyDataSetChanged();
} else {
// IndexOutOfBoundsExceptionを吐くことがあったので いったん
this.listItemAdapter.notifyItemRangeInserted(0, listItems.size() - 1);
}
}); });
} else {
getActivity().runOnUiThread(() -> {
this.listItemAdapter.notifyDataSetChanged();
});
}
}); });
} }

View File

@ -55,5 +55,6 @@ dependencies {
implementation project(':component:viewer') implementation project(':component:viewer')
// TODO-rca:
implementation "com.hendraanggrian.material:collapsingtoolbarlayout-subtitle:1.5.0"
} }

View File

@ -23,6 +23,7 @@ import dagger.hilt.android.AndroidEntryPoint;
import one.nem.lacerta.component.viewer.ViewerMainActivity; import one.nem.lacerta.component.viewer.ViewerMainActivity;
import one.nem.lacerta.data.LacertaLibrary; import one.nem.lacerta.data.LacertaLibrary;
import one.nem.lacerta.model.LibraryItemPage; import one.nem.lacerta.model.LibraryItemPage;
import one.nem.lacerta.model.ListItemType;
import one.nem.lacerta.utils.FeatureSwitch; import one.nem.lacerta.utils.FeatureSwitch;
import one.nem.lacerta.utils.LacertaLogger; import one.nem.lacerta.utils.LacertaLogger;
@ -43,6 +44,7 @@ public class LibraryPageFragment extends Fragment {
String folderId; String folderId;
String title; String title;
String parentId; String parentId;
Toolbar toolbar;
@Inject @Inject
@ -128,12 +130,19 @@ public class LibraryPageFragment extends Fragment {
logger.debug("LibraryTopFragment", "getArguments() is null(maybe root)"); logger.debug("LibraryTopFragment", "getArguments() is null(maybe root)");
this.libraryItemPage = new LibraryItemPage(); this.libraryItemPage = new LibraryItemPage();
} }
this.toolbar = view.findViewById(R.id.library_toolbar);
// Toolbar Setup // Toolbar Setup
toolbarSetup(view.findViewById(R.id.library_toolbar), this.folderId != null, this.title != null ? this.title : "ライブラリ"); toolbarSetup(this.toolbar, this.folderId != null, this.title != null ? this.title : "ライブラリ");
if(this.folderId == null) {
updateToolbarSubtitle(this.toolbar, null); //負荷軽減のため+邪魔なのでfolderIdがnullの場合はルートフォルダを表示しているので
} else {
lacertaLibrary.getPublicPath(this.folderId, ListItemType.ITEM_TYPE_FOLDER).thenAccept(publicPath -> {
updateToolbarSubtitle(this.toolbar, "/" + publicPath.parent().getStringPath());
});
}
// RecyclerView Setup // RecyclerView Setup
RecyclerView recyclerView = view.findViewById(R.id.library_item_recycler_view); RecyclerView recyclerView = view.findViewById(R.id.library_item_recycler_view);
this.listItemAdapter = new ListItemAdapter(new DocumentSelectListener() { this.listItemAdapter = new ListItemAdapter(new DocumentSelectListener() {
@Override @Override
@ -168,33 +177,8 @@ public class LibraryPageFragment extends Fragment {
recyclerView.setLayoutManager(new LinearLayoutManager(getContext())); recyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
// Get library page and update RecyclerView items // Get library page and update RecyclerView items
lacertaLibrary.getLibraryPage(this.folderId, 10).thenAccept(libraryItemPage -> {
this.libraryItemPage = libraryItemPage;
if (this.parentId == null) { updateItem(this.folderId);
this.parentId = libraryItemPage.getParentId();
}
if (this.title == null) {
this.title = libraryItemPage.getPageTitle();
// Toolbar init again
toolbarSetup(view.findViewById(R.id.library_toolbar), this.folderId != null, this.title != null ? this.title : "ライブラリ");
}
logger.debug("LibraryTopFragment", "Item selected! Total item page: " + this.libraryItemPage.getListItems().size());
if (!FeatureSwitch.RecyclerView.useSimpleNotifyMethod) {
getActivity().runOnUiThread(() -> { // TODO-rca: 実行条件を考える
listItemAdapter.notifyItemRangeRemoved(0, this.libraryItemPage.getListItems().size() - 1);
});
}
listItemAdapter.setLibraryItemPage(this.libraryItemPage);
getActivity().runOnUiThread(() -> {
if (FeatureSwitch.RecyclerView.useSimpleNotifyMethod) {
listItemAdapter.notifyDataSetChanged();
} else {
listItemAdapter.notifyItemRangeInserted(0, this.libraryItemPage.getListItems().size() - 1);
}
});
});
} }
/** /**
@ -216,6 +200,7 @@ public class LibraryPageFragment extends Fragment {
lacertaLibrary.createFolder(pageId, input.getText().toString()).thenAccept(folderId -> { lacertaLibrary.createFolder(pageId, input.getText().toString()).thenAccept(folderId -> {
// Refresh // Refresh
updateItem(pageId); updateItem(pageId);
}); });
}); });
builder.setNegativeButton("キャンセル", (dialog, which) -> { builder.setNegativeButton("キャンセル", (dialog, which) -> {
@ -226,19 +211,34 @@ public class LibraryPageFragment extends Fragment {
} }
/** /**
* RecyclerViewのアイテムを更新する * RecyclerViewのアイテムとUIを更新する
*/ */
private void updateItem(String pageId) { private void updateItem(String pageId) {
long startTime = System.currentTimeMillis();
lacertaLibrary.getLibraryPage(pageId, 10).thenAccept(libraryItemPage -> { lacertaLibrary.getLibraryPage(pageId, 10).thenAccept(libraryItemPage -> {
long endTime = System.currentTimeMillis();
this.libraryItemPage = libraryItemPage; this.libraryItemPage = libraryItemPage;
logger.debug("LibraryTopFragment", "Item selected! libraryItemPage.getListItems().size(): " + libraryItemPage.getListItems().size()); logger.debug("LibraryTopFragment", "Item selected! libraryItemPage.getListItems().size(): " + libraryItemPage.getListItems().size());
getActivity().runOnUiThread(() -> {
listItemAdapter.notifyItemRangeRemoved(0, libraryItemPage.getListItems().size() - 1);
});
listItemAdapter.setLibraryItemPage(libraryItemPage); listItemAdapter.setLibraryItemPage(libraryItemPage);
if (endTime - startTime > 500) { // 500ms以上かかった場合は表示アニメーションをする
getActivity().runOnUiThread(() -> { getActivity().runOnUiThread(() -> {
listItemAdapter.notifyItemRangeInserted(0, libraryItemPage.getListItems().size() - 1); listItemAdapter.notifyItemRangeInserted(0, this.libraryItemPage.getListItems().size());
}); });
} else {
getActivity().runOnUiThread(() -> {
listItemAdapter.notifyDataSetChanged();
});
}
});
}
/**
* Toolbarのサブタイトルを更新
* @param subtitle サブタイトル
*/
private void updateToolbarSubtitle(Toolbar toolbar, String subtitle) {
getActivity().runOnUiThread(() -> {
toolbar.setSubtitle(subtitle);
}); });
} }

View File

@ -13,7 +13,7 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content"> android:layout_height="wrap_content">
<com.google.android.material.appbar.CollapsingToolbarLayout <com.google.android.material.appbar.SubtitleCollapsingToolbarLayout
android:id="@+id/collapsing_toolbar" android:id="@+id/collapsing_toolbar"
app:contentScrim="@color/colorSecondaryContainer" app:contentScrim="@color/colorSecondaryContainer"
android:background="@color/colorSurface" android:background="@color/colorSurface"
@ -35,7 +35,7 @@
app:layout_collapseMode="pin" app:layout_collapseMode="pin"
app:title="HOGE" /> app:title="HOGE" />
</com.google.android.material.appbar.CollapsingToolbarLayout> </com.google.android.material.appbar.SubtitleCollapsingToolbarLayout>
</com.google.android.material.appbar.AppBarLayout> </com.google.android.material.appbar.AppBarLayout>

View File

@ -12,10 +12,10 @@
<action <action
android:id="@+id/action_feature_library_top_fragment_self" android:id="@+id/action_feature_library_top_fragment_self"
app:destination="@id/feature_library_top_fragment" app:destination="@id/feature_library_top_fragment"
app:enterAnim="@anim/nav_default_enter_anim" app:enterAnim="@anim/nav_twitter_enter_anim"
app:exitAnim="@anim/nav_default_exit_anim" app:exitAnim="@anim/nav_twitter_exit_anim"
app:popEnterAnim="@anim/nav_default_pop_enter_anim" app:popEnterAnim="@anim/nav_twitter_pop_enter_anim"
app:popExitAnim="@anim/nav_default_pop_exit_anim" app:popExitAnim="@anim/nav_twitter_pop_exit_anim"
app:popUpTo="@id/feature_library_top_fragment" app:popUpTo="@id/feature_library_top_fragment"
app:popUpToInclusive="false"/> app:popUpToInclusive="false"/>
</fragment> </fragment>

View File

@ -0,0 +1,25 @@
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<translate
android:duration="@android:integer/config_mediumAnimTime"
android:fromXDelta="105%"
android:interpolator="@android:anim/accelerate_decelerate_interpolator"
android:toXDelta="0" />
<scale
android:duration="@android:integer/config_mediumAnimTime"
android:fromXScale="0.9"
android:fromYScale="0.9"
android:interpolator="@android:anim/accelerate_decelerate_interpolator"
android:pivotX="50%"
android:pivotY="50%"
android:toXScale="1"
android:toYScale="1" />
<alpha
android:duration="@android:integer/config_mediumAnimTime"
android:fromAlpha="0.5"
android:interpolator="@android:anim/linear_interpolator"
android:toAlpha="1" />
</set>

View File

@ -0,0 +1,24 @@
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<translate
android:duration="@android:integer/config_mediumAnimTime"
android:fromXDelta="0"
android:interpolator="@android:anim/accelerate_decelerate_interpolator"
android:toXDelta="-105%" />
<scale
android:duration="@android:integer/config_mediumAnimTime"
android:fromXScale="1"
android:fromYScale="1"
android:interpolator="@android:anim/accelerate_decelerate_interpolator"
android:pivotX="50%"
android:pivotY="50%"
android:toXScale="0.9"
android:toYScale="0.9" />
<alpha
android:duration="@android:integer/config_mediumAnimTime"
android:fromAlpha="1"
android:interpolator="@android:anim/linear_interpolator"
android:toAlpha="0.5" />
</set>

View File

@ -0,0 +1,24 @@
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<translate
android:duration="@android:integer/config_mediumAnimTime"
android:fromXDelta="-105%"
android:interpolator="@android:anim/accelerate_decelerate_interpolator"
android:toXDelta="0" />
<scale
android:duration="@android:integer/config_mediumAnimTime"
android:fromXScale="0.9"
android:fromYScale="0.9"
android:interpolator="@android:anim/accelerate_decelerate_interpolator"
android:pivotX="50%"
android:pivotY="50%"
android:toXScale="1"
android:toYScale="1" />
<alpha
android:duration="@android:integer/config_mediumAnimTime"
android:fromAlpha="0.5"
android:interpolator="@android:anim/linear_interpolator"
android:toAlpha="1" />
</set>

View File

@ -0,0 +1,25 @@
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<translate
android:duration="@android:integer/config_mediumAnimTime"
android:fromXDelta="0"
android:interpolator="@android:anim/accelerate_decelerate_interpolator"
android:toXDelta="105%" />
<scale
android:duration="@android:integer/config_mediumAnimTime"
android:fromXScale="1"
android:fromYScale="1"
android:interpolator="@android:anim/accelerate_decelerate_interpolator"
android:pivotX="50%"
android:pivotY="50%"
android:toXScale="0.9"
android:toYScale="0.9" />
<alpha
android:duration="@android:integer/config_mediumAnimTime"
android:fromAlpha="1"
android:interpolator="@android:anim/linear_interpolator"
android:toAlpha="0.5" />
</set>

View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<translate
android:duration="@android:integer/config_shortAnimTime"
android:fromXDelta="100%p"
android:interpolator="@android:anim/decelerate_interpolator"
android:toXDelta="0" />
</set>

View File

@ -0,0 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<translate
android:duration="@android:integer/config_shortAnimTime"
android:fromXDelta="0"
android:interpolator="@android:anim/accelerate_interpolator"
android:toXDelta="-20%p" />
<alpha
android:duration="@android:integer/config_shortAnimTime"
android:fromAlpha="1"
android:interpolator="@android:anim/linear_interpolator"
android:toAlpha="0.5" />
</set>

View File

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<translate
android:duration="@android:integer/config_shortAnimTime"
android:fromXDelta="-20%p"
android:interpolator="@android:anim/decelerate_interpolator"
android:toXDelta="0" />
<alpha
android:duration="@android:integer/config_shortAnimTime"
android:fromAlpha="0.5"
android:interpolator="@android:anim/linear_interpolator"
android:toAlpha="1" />
</set>

View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<translate
android:duration="@android:integer/config_shortAnimTime"
android:fromXDelta="0"
android:interpolator="@android:anim/accelerate_interpolator"
android:toXDelta="100%p" />
</set>

View File

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<rotate
android:duration="@android:integer/config_shortAnimTime"
android:fromDegrees="30"
android:interpolator="@android:anim/decelerate_interpolator"
android:pivotX="50%"
android:pivotY="90%"
android:toDegrees="0" />
<alpha
android:duration="@android:integer/config_shortAnimTime"
android:fromAlpha="0.0"
android:toAlpha="1.0" />
</set>

View File

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<rotate
android:duration="@android:integer/config_shortAnimTime"
android:fromDegrees="0"
android:interpolator="@android:anim/accelerate_interpolator"
android:pivotX="50%"
android:pivotY="90%"
android:toDegrees="-30" />
<alpha
android:duration="@android:integer/config_shortAnimTime"
android:fromAlpha="1.0"
android:toAlpha="0.0" />
</set>

View File

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<rotate
android:duration="@android:integer/config_shortAnimTime"
android:fromDegrees="-30"
android:interpolator="@android:anim/decelerate_interpolator"
android:pivotX="50%"
android:pivotY="90%"
android:toDegrees="0" />
<alpha
android:duration="@android:integer/config_shortAnimTime"
android:fromAlpha="0.0"
android:toAlpha="1.0" />
</set>

View File

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<rotate
android:duration="@android:integer/config_shortAnimTime"
android:fromDegrees="0"
android:interpolator="@android:anim/accelerate_interpolator"
android:pivotX="50%"
android:pivotY="90%"
android:toDegrees="30" />
<alpha
android:duration="@android:integer/config_shortAnimTime"
android:fromAlpha="1"
android:toAlpha="0" />
</set>

View File

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<translate
android:duration="@integer/config_navAnimTime"
android:fromXDelta="50%p"
android:interpolator="@android:anim/decelerate_interpolator"
android:toXDelta="0" />
<alpha
android:duration="@integer/config_navAnimTime"
android:fromAlpha="0.0"
android:toAlpha="1.0" />
</set>

View File

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<translate
android:duration="@integer/config_navAnimTime"
android:fromXDelta="0%p"
android:interpolator="@android:anim/accelerate_interpolator"
android:toXDelta="-50%p" />
<alpha
android:duration="@integer/config_navAnimTime"
android:fromAlpha="1.0"
android:toAlpha="0.0" />
</set>

View File

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<translate
android:duration="@integer/config_navAnimTime"
android:fromXDelta="-50%p"
android:interpolator="@android:anim/decelerate_interpolator"
android:toXDelta="0%p" />
<alpha
android:duration="@integer/config_navAnimTime"
android:fromAlpha="0.0"
android:toAlpha="1.0" />
</set>

View File

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<translate
android:duration="@integer/config_navAnimTime"
android:fromXDelta="0"
android:interpolator="@android:anim/accelerate_interpolator"
android:toXDelta="50%p" />
<alpha
android:duration="@integer/config_navAnimTime"
android:fromAlpha="1.0"
android:toAlpha="0.0" />
</set>

View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<translate
android:duration="@android:integer/config_shortAnimTime"
android:fromXDelta="100%p"
android:interpolator="@android:anim/decelerate_interpolator"
android:toXDelta="0" />
</set>

View File

@ -0,0 +1,18 @@
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<scale
android:duration="@android:integer/config_shortAnimTime"
android:fromXScale="1"
android:fromYScale="1"
android:interpolator="@android:anim/accelerate_decelerate_interpolator"
android:pivotX="50%"
android:pivotY="50%"
android:toXScale="0.9"
android:toYScale="0.9" />
<alpha
android:duration="@android:integer/config_shortAnimTime"
android:fromAlpha="1"
android:interpolator="@android:anim/linear_interpolator"
android:toAlpha="0.5" />
</set>

View File

@ -0,0 +1,18 @@
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<scale
android:duration="@android:integer/config_shortAnimTime"
android:fromXScale="0.9"
android:fromYScale="0.9"
android:interpolator="@android:anim/accelerate_decelerate_interpolator"
android:pivotX="50%"
android:pivotY="50%"
android:toXScale="1"
android:toYScale="1" />
<alpha
android:duration="@android:integer/config_shortAnimTime"
android:fromAlpha="0.5"
android:interpolator="@android:anim/linear_interpolator"
android:toAlpha="1" />
</set>

View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<translate
android:duration="@android:integer/config_shortAnimTime"
android:fromXDelta="0"
android:interpolator="@android:anim/accelerate_interpolator"
android:toXDelta="100%p" />
</set>

View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<translate
android:duration="@android:integer/config_mediumAnimTime"
android:fromYDelta="100%p"
android:interpolator="@android:anim/decelerate_interpolator"
android:toYDelta="0" />
</set>

View File

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<alpha
android:duration="@android:integer/config_mediumAnimTime"
android:fromAlpha="1"
android:toAlpha="1" />
</set>

View File

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<alpha
android:duration="@android:integer/config_mediumAnimTime"
android:fromAlpha="1"
android:toAlpha="1" />
</set>

View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<translate
android:duration="@android:integer/config_mediumAnimTime"
android:fromYDelta="0"
android:interpolator="@android:anim/accelerate_interpolator"
android:toYDelta="100%p" />
</set>

View File

@ -0,0 +1,16 @@
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<scale
android:duration="@integer/config_navAnimTime"
android:fromXScale="0.9"
android:fromYScale="0.9"
android:interpolator="@android:anim/decelerate_interpolator"
android:pivotX="50%"
android:pivotY="50%"
android:toXScale="1"
android:toYScale="1" />
<alpha
android:duration="@integer/config_navAnimTime"
android:fromAlpha="0.0"
android:toAlpha="1.0" />
</set>

View File

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<scale
android:duration="@integer/config_navAnimTime"
android:fromXScale="1"
android:fromYScale="1"
android:interpolator="@android:anim/accelerate_decelerate_interpolator"
android:pivotX="50%"
android:pivotY="50%"
android:toXScale="1.05"
android:toYScale="1.05" />
</set>

View File

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<scale
android:duration="@integer/config_navAnimTime"
android:fromXScale="1.05"
android:fromYScale="1.05"
android:interpolator="@android:anim/accelerate_decelerate_interpolator"
android:pivotX="50%"
android:pivotY="50%"
android:toXScale="1"
android:toYScale="1" />
</set>

View File

@ -0,0 +1,16 @@
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<scale
android:duration="@integer/config_navAnimTime"
android:fromXScale="1"
android:fromYScale="1"
android:interpolator="@android:anim/accelerate_interpolator"
android:pivotX="50%"
android:pivotY="50%"
android:toXScale="0.9"
android:toYScale="0.9" />
<alpha
android:duration="@integer/config_navAnimTime"
android:fromAlpha="1.0"
android:toAlpha="0.0" />
</set>