package com.kabouzeid.gramophone.ui.fragments.mainactivity.folders;
import android.app.Dialog;
import android.content.Context;
import android.content.DialogInterface;
import android.media.MediaScannerConnection;
import android.os.Bundle;
import android.os.Environment;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.design.widget.AppBarLayout;
import android.support.design.widget.CoordinatorLayout;
import android.support.design.widget.Snackbar;
import android.support.v4.app.LoaderManager;
import android.support.v4.content.Loader;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.Toolbar;
import android.text.Html;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.webkit.MimeTypeMap;
import android.widget.PopupMenu;
import android.widget.Toast;
import com.afollestad.materialcab.MaterialCab;
import com.afollestad.materialdialogs.DialogAction;
import com.afollestad.materialdialogs.MaterialDialog;
import com.kabouzeid.appthemehelper.ThemeStore;
import com.kabouzeid.appthemehelper.common.ATHToolbarActivity;
import com.kabouzeid.appthemehelper.util.ToolbarContentTintHelper;
import com.kabouzeid.gramophone.R;
import com.kabouzeid.gramophone.adapter.SongFileAdapter;
import com.kabouzeid.gramophone.helper.MusicPlayerRemote;
import com.kabouzeid.gramophone.helper.menu.SongMenuHelper;
import com.kabouzeid.gramophone.helper.menu.SongsMenuHelper;
import com.kabouzeid.gramophone.interfaces.CabHolder;
import com.kabouzeid.gramophone.interfaces.LoaderIds;
import com.kabouzeid.gramophone.misc.DialogAsyncTask;
import com.kabouzeid.gramophone.misc.UpdateToastMediaScannerCompletionListener;
import com.kabouzeid.gramophone.misc.WrappedAsyncTaskLoader;
import com.kabouzeid.gramophone.model.Song;
import com.kabouzeid.gramophone.ui.activities.MainActivity;
import com.kabouzeid.gramophone.ui.fragments.mainactivity.AbsMainActivityFragment;
import com.kabouzeid.gramophone.util.FileUtil;
import com.kabouzeid.gramophone.util.PhonographColorUtil;
import com.kabouzeid.gramophone.util.PreferenceUtil;
import com.kabouzeid.gramophone.util.ViewUtil;
import com.kabouzeid.gramophone.views.BreadCrumbLayout;
import com.simplecityapps.recyclerview_fastscroll.views.FastScrollRecyclerView;
import java.io.File;
import java.io.FileFilter;
import java.io.IOException;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.LinkedList;
import java.util.List;
import butterknife.BindView;
import butterknife.ButterKnife;
import butterknife.Unbinder;
public class FoldersFragment extends AbsMainActivityFragment implements MainActivity.MainActivityFragmentCallbacks, CabHolder, BreadCrumbLayout.SelectionCallback, SongFileAdapter.Callbacks, AppBarLayout.OnOffsetChangedListener, LoaderManager.LoaderCallbacks<List<File>> {
public static final String TAG = FoldersFragment.class.getSimpleName();
private static final int LOADER_ID = LoaderIds.FOLDERS_FRAGMENT;
protected static final String PATH = "path";
protected static final String CRUMBS = "crumbs";
private Unbinder unbinder;
@BindView(R.id.coordinator_layout)
CoordinatorLayout coordinatorLayout;
@BindView(R.id.container)
View container;
@BindView(android.R.id.empty)
View empty;
@BindView(R.id.toolbar)
Toolbar toolbar;
@BindView(R.id.bread_crumbs)
BreadCrumbLayout breadCrumbs;
@BindView(R.id.appbar)
AppBarLayout appbar;
@BindView(R.id.recycler_view)
FastScrollRecyclerView recyclerView;
private SongFileAdapter adapter;
private MaterialCab cab;
public FoldersFragment() {
}
public static FoldersFragment newInstance(Context context) {
return newInstance(PreferenceUtil.getInstance(context).getStartDirectory());
}
public static FoldersFragment newInstance(File directory) {
FoldersFragment frag = new FoldersFragment();
Bundle b = new Bundle();
b.putSerializable(PATH, directory);
frag.setArguments(b);
return frag;
}
public void setCrumb(BreadCrumbLayout.Crumb crumb, boolean addToHistory) {
if (crumb == null) return;
saveScrollPosition();
breadCrumbs.setActiveOrAdd(crumb, false);
if (addToHistory) {
breadCrumbs.addHistory(crumb);
}
getLoaderManager().restartLoader(LOADER_ID, null, this);
}
private void saveScrollPosition() {
BreadCrumbLayout.Crumb crumb = getActiveCrumb();
if (crumb != null) {
crumb.setScrollPosition(((LinearLayoutManager) recyclerView.getLayoutManager()).findFirstVisibleItemPosition());
}
}
@Nullable
private BreadCrumbLayout.Crumb getActiveCrumb() {
return breadCrumbs != null && breadCrumbs.size() > 0 ? breadCrumbs.getCrumb(breadCrumbs.getActiveIndex()) : null;
}
@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putParcelable(CRUMBS, breadCrumbs.getStateWrapper());
}
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
if (savedInstanceState == null) {
setCrumb(new BreadCrumbLayout.Crumb(tryGetCanonicalFile((File) getArguments().getSerializable(PATH))), true);
} else {
breadCrumbs.restoreFromStateWrapper((BreadCrumbLayout.SavedStateWrapper) savedInstanceState.getParcelable(CRUMBS));
getLoaderManager().initLoader(LOADER_ID, null, this);
}
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_folder, container, false);
unbinder = ButterKnife.bind(this, view);
return view;
}
@Override
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
setStatusbarColorAuto(view);
getMainActivity().setNavigationbarColorAuto();
getMainActivity().setTaskDescriptionColorAuto();
setUpAppbarColor();
setUpToolbar();
setUpBreadCrumbs();
setUpRecyclerView();
setUpAdapter();
}
private void setUpAppbarColor() {
int primaryColor = ThemeStore.primaryColor(getActivity());
appbar.setBackgroundColor(primaryColor);
toolbar.setBackgroundColor(primaryColor);
breadCrumbs.setBackgroundColor(primaryColor);
breadCrumbs.setActivatedContentColor(ToolbarContentTintHelper.toolbarTitleColor(getActivity(), primaryColor));
breadCrumbs.setDeactivatedContentColor(ToolbarContentTintHelper.toolbarSubtitleColor(getActivity(), primaryColor));
}
private void setUpToolbar() {
toolbar.setNavigationIcon(R.drawable.ic_menu_white_24dp);
getActivity().setTitle(R.string.app_name);
getMainActivity().setSupportActionBar(toolbar);
}
private void setUpBreadCrumbs() {
breadCrumbs.setCallback(this);
}
private void setUpRecyclerView() {
ViewUtil.setUpFastScrollRecyclerViewColor(getActivity(), recyclerView, ThemeStore.accentColor(getActivity()));
recyclerView.setLayoutManager(new LinearLayoutManager(getActivity()));
appbar.addOnOffsetChangedListener(this);
}
private void setUpAdapter() {
adapter = new SongFileAdapter(getMainActivity(), new LinkedList<File>(), R.layout.item_list, this, this);
adapter.registerAdapterDataObserver(new RecyclerView.AdapterDataObserver() {
@Override
public void onChanged() {
super.onChanged();
checkIsEmpty();
}
});
recyclerView.setAdapter(adapter);
checkIsEmpty();
}
@Override
public void onPause() {
super.onPause();
saveScrollPosition();
}
@Override
public void onDestroyView() {
appbar.removeOnOffsetChangedListener(this);
unbinder.unbind();
super.onDestroyView();
}
@Override
public boolean handleBackPress() {
if (cab != null && cab.isActive()) {
cab.finish();
return true;
}
if (breadCrumbs.popHistory()) {
setCrumb(breadCrumbs.lastHistory(), false);
return true;
}
return false;
}
@NonNull
@Override
public MaterialCab openCab(int menuRes, MaterialCab.Callback callback) {
if (cab != null && cab.isActive()) cab.finish();
cab = new MaterialCab(getMainActivity(), R.id.cab_stub)
.setMenu(menuRes)
.setCloseDrawableRes(R.drawable.ic_close_white_24dp)
.setBackgroundColor(PhonographColorUtil.shiftBackgroundColorForLightText(ThemeStore.primaryColor(getActivity())))
.start(callback);
return cab;
}
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
super.onCreateOptionsMenu(menu, inflater);
inflater.inflate(R.menu.menu_folders, menu);
ToolbarContentTintHelper.handleOnCreateOptionsMenu(getActivity(), toolbar, menu, ATHToolbarActivity.getToolbarBackgroundColor(toolbar));
}
@Override
public void onPrepareOptionsMenu(Menu menu) {
super.onPrepareOptionsMenu(menu);
ToolbarContentTintHelper.handleOnPrepareOptionsMenu(getActivity(), toolbar);
}
@Override
public boolean onOptionsItemSelected(@NonNull MenuItem item) {
switch (item.getItemId()) {
case R.id.action_go_to_start_directory:
setCrumb(new BreadCrumbLayout.Crumb(tryGetCanonicalFile(PreferenceUtil.getInstance(getActivity()).getStartDirectory())), true);
return true;
}
return super.onOptionsItemSelected(item);
}
@Override
public void onCrumbSelection(BreadCrumbLayout.Crumb crumb, int index) {
setCrumb(crumb, true);
}
public static File getDefaultStartDirectory() {
File musicDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_MUSIC);
File startFolder;
if (musicDir.exists() && musicDir.isDirectory()) {
startFolder = musicDir;
} else {
File externalStorage = Environment.getExternalStorageDirectory();
if (externalStorage.exists() && externalStorage.isDirectory()) {
startFolder = externalStorage;
} else {
startFolder = new File("/"); // root
}
}
return startFolder;
}
@Override
public void onFileSelected(File file) {
file = tryGetCanonicalFile(file); // important as we compare the path value later
if (file.isDirectory()) {
setCrumb(new BreadCrumbLayout.Crumb(file), true);
} else {
FileFilter fileFilter = new FileFilter() {
@Override
public boolean accept(File pathname) {
return !pathname.isDirectory() && getFileFilter().accept(pathname);
}
};
new ListSongsAsyncTask(getActivity(), file, new ListSongsAsyncTask.OnSongsListedCallback() {
@Override
public void onSongsListed(@NonNull ArrayList<Song> songs, Object extra) {
File file = (File) extra;
int startIndex = -1;
for (int i = 0; i < songs.size(); i++) {
if (file.getPath().equals(songs.get(i).data)) { // path is already canonical here
startIndex = i;
break;
}
}
if (startIndex > -1) {
MusicPlayerRemote.openQueue(songs, startIndex, true);
} else {
final File finalFile = file;
Snackbar.make(coordinatorLayout, Html.fromHtml(String.format(getString(R.string.not_listed_in_media_store), file.getName())), Snackbar.LENGTH_LONG)
.setAction(R.string.action_scan, new View.OnClickListener() {
@Override
public void onClick(View v) {
new ListPathsAsyncTask(getActivity(), new ListPathsAsyncTask.OnPathsListedCallback() {
@Override
public void onPathsListed(@Nullable String[] paths) {
scanPaths(paths);
}
}).execute(new ListPathsAsyncTask.LoadingInfo(finalFile, getFileFilter()));
}
})
.setActionTextColor(ThemeStore.accentColor(getActivity()))
.show();
}
}
}).execute(new ListSongsAsyncTask.LoadingInfo(toList(file.getParentFile()), fileFilter, getFileComparator()));
}
}
@Override
public void onMultipleItemAction(MenuItem item, ArrayList<File> files) {
final int itemId = item.getItemId();
new ListSongsAsyncTask(getActivity(), null, new ListSongsAsyncTask.OnSongsListedCallback() {
@Override
public void onSongsListed(@NonNull ArrayList<Song> songs, Object extra) {
SongsMenuHelper.handleMenuClick(getActivity(), songs, itemId);
}
}).execute(new ListSongsAsyncTask.LoadingInfo(files, getFileFilter(), getFileComparator()));
}
@Override
public void onFileMenuClicked(final File file, View view) {
PopupMenu popupMenu = new PopupMenu(getActivity(), view);
if (file.isDirectory()) {
popupMenu.inflate(R.menu.menu_item_directory);
popupMenu.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() {
@Override
public boolean onMenuItemClick(MenuItem item) {
final int itemId = item.getItemId();
switch (itemId) {
case R.id.action_play_next:
case R.id.action_add_to_current_playing:
case R.id.action_add_to_playlist:
case R.id.action_delete_from_device:
new ListSongsAsyncTask(getActivity(), null, new ListSongsAsyncTask.OnSongsListedCallback() {
@Override
public void onSongsListed(@NonNull ArrayList<Song> songs, Object extra) {
SongsMenuHelper.handleMenuClick(getActivity(), songs, itemId);
}
}).execute(new ListSongsAsyncTask.LoadingInfo(toList(file), getFileFilter(), getFileComparator()));
return true;
case R.id.action_set_as_start_directory:
PreferenceUtil.getInstance(getActivity()).setStartDirectory(file);
Toast.makeText(getActivity(), String.format(getString(R.string.new_start_directory), file.getPath()), Toast.LENGTH_SHORT).show();
return true;
case R.id.action_scan:
new ListPathsAsyncTask(getActivity(), new ListPathsAsyncTask.OnPathsListedCallback() {
@Override
public void onPathsListed(@Nullable String[] paths) {
scanPaths(paths);
}
}).execute(new ListPathsAsyncTask.LoadingInfo(file, getFileFilter()));
return true;
}
return false;
}
});
} else {
popupMenu.inflate(R.menu.menu_item_file);
popupMenu.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() {
@Override
public boolean onMenuItemClick(MenuItem item) {
final int itemId = item.getItemId();
switch (itemId) {
case R.id.action_play_next:
case R.id.action_add_to_current_playing:
case R.id.action_add_to_playlist:
case R.id.action_go_to_album:
case R.id.action_go_to_artist:
case R.id.action_share:
case R.id.action_tag_editor:
case R.id.action_details:
case R.id.action_set_as_ringtone:
case R.id.action_delete_from_device:
new ListSongsAsyncTask(getActivity(), null, new ListSongsAsyncTask.OnSongsListedCallback() {
@Override
public void onSongsListed(@NonNull ArrayList<Song> songs, Object extra) {
SongMenuHelper.handleMenuClick(getActivity(), songs.get(0), itemId);
}
}).execute(new ListSongsAsyncTask.LoadingInfo(toList(file), getFileFilter(), getFileComparator()));
return true;
case R.id.action_scan:
new ListPathsAsyncTask(getActivity(), new ListPathsAsyncTask.OnPathsListedCallback() {
@Override
public void onPathsListed(@Nullable String[] paths) {
scanPaths(paths);
}
}).execute(new ListPathsAsyncTask.LoadingInfo(file, getFileFilter()));
return true;
}
return false;
}
});
}
popupMenu.show();
}
private ArrayList<File> toList(File file) {
ArrayList<File> files = new ArrayList<>(1);
files.add(file);
return files;
}
Comparator<File> fileComparator = new Comparator<File>() {
@Override
public int compare(File lhs, File rhs) {
if (lhs.isDirectory() && !rhs.isDirectory()) {
return -1;
} else if (!lhs.isDirectory() && rhs.isDirectory()) {
return 1;
} else {
return lhs.getName().compareToIgnoreCase
(rhs.getName());
}
}
};
private Comparator<File> getFileComparator() {
return fileComparator;
}
FileFilter audioFileFilter = new FileFilter() {
@Override
public boolean accept(File file) {
return !file.isHidden() && (file.isDirectory() || FileUtil.fileIsMimeType(file, "audio/*", MimeTypeMap.getSingleton()));
}
};
private FileFilter getFileFilter() {
return audioFileFilter;
}
@Override
public void onOffsetChanged(AppBarLayout appBarLayout, int verticalOffset) {
container.setPadding(container.getPaddingLeft(), container.getPaddingTop(), container.getPaddingRight(), appbar.getTotalScrollRange() + verticalOffset);
}
private void checkIsEmpty() {
if (empty != null) {
empty.setVisibility(adapter == null || adapter.getItemCount() == 0 ? View.VISIBLE : View.GONE);
}
}
private static File tryGetCanonicalFile(File file) {
try {
return file.getCanonicalFile();
} catch (IOException e) {
e.printStackTrace();
return file;
}
}
private void scanPaths(@Nullable String[] toBeScanned) {
if (getActivity() == null) return;
if (toBeScanned == null || toBeScanned.length < 1) {
Toast.makeText(getActivity(), R.string.nothing_to_scan, Toast.LENGTH_SHORT).show();
} else {
MediaScannerConnection.scanFile(getActivity().getApplicationContext(), toBeScanned, null, new UpdateToastMediaScannerCompletionListener(getActivity(), toBeScanned));
}
}
private void updateAdapter(@NonNull List<File> files) {
adapter.swapDataSet(files);
BreadCrumbLayout.Crumb crumb = getActiveCrumb();
if (crumb != null && recyclerView != null) {
((LinearLayoutManager) recyclerView.getLayoutManager()).scrollToPositionWithOffset(crumb.getScrollPosition(), 0);
}
}
@Override
public Loader<List<File>> onCreateLoader(int id, Bundle args) {
return new AsyncFileLoader(this);
}
@Override
public void onLoadFinished(Loader<List<File>> loader, List<File> data) {
updateAdapter(data);
}
@Override
public void onLoaderReset(Loader<List<File>> loader) {
updateAdapter(new LinkedList<File>());
}
private static class AsyncFileLoader extends WrappedAsyncTaskLoader<List<File>> {
private WeakReference<FoldersFragment> fragmentWeakReference;
public AsyncFileLoader(FoldersFragment foldersFragment) {
super(foldersFragment.getActivity());
fragmentWeakReference = new WeakReference<>(foldersFragment);
}
@Override
public List<File> loadInBackground() {
FoldersFragment foldersFragment = fragmentWeakReference.get();
File directory = null;
if (foldersFragment != null) {
BreadCrumbLayout.Crumb crumb = foldersFragment.getActiveCrumb();
if (crumb != null) {
directory = crumb.getFile();
}
}
if (directory != null) {
List<File> files = FileUtil.listFiles(directory, foldersFragment.getFileFilter());
Collections.sort(files, foldersFragment.getFileComparator());
return files;
} else {
return new LinkedList<>();
}
}
}
private static class ListSongsAsyncTask extends ListingFilesDialogAsyncTask<ListSongsAsyncTask.LoadingInfo, Void, ArrayList<Song>> {
private WeakReference<Context> contextWeakReference;
private WeakReference<OnSongsListedCallback> callbackWeakReference;
private final Object extra;
public ListSongsAsyncTask(Context context, Object extra, OnSongsListedCallback callback) {
super(context);
this.extra = extra;
contextWeakReference = new WeakReference<>(context);
callbackWeakReference = new WeakReference<>(callback);
}
@Override
protected void onPreExecute() {
super.onPreExecute();
checkCallbackReference();
checkContextReference();
}
@Override
protected ArrayList<Song> doInBackground(LoadingInfo... params) {
try {
LoadingInfo info = params[0];
List<File> files = FileUtil.listFilesDeep(info.files, info.fileFilter);
if (isCancelled() || checkContextReference() == null || checkCallbackReference() == null)
return null;
Collections.sort(files, info.fileComparator);
Context context = checkContextReference();
if (isCancelled() || context == null || checkCallbackReference() == null)
return null;
return FileUtil.matchFilesWithMediaStore(context, files);
} catch (Exception e) {
e.printStackTrace();
cancel(false);
return null;
}
}
@Override
protected void onPostExecute(ArrayList<Song> songs) {
super.onPostExecute(songs);
OnSongsListedCallback callback = checkCallbackReference();
if (songs != null && callback != null && !songs.isEmpty())
callback.onSongsListed(songs, extra);
}
private Context checkContextReference() {
Context context = contextWeakReference.get();
if (context == null) {
cancel(false);
}
return context;
}
private OnSongsListedCallback checkCallbackReference() {
OnSongsListedCallback callback = callbackWeakReference.get();
if (callback == null) {
cancel(false);
}
return callback;
}
public static class LoadingInfo {
public final Comparator<File> fileComparator;
public final FileFilter fileFilter;
public final List<File> files;
public LoadingInfo(@NonNull List<File> files, @NonNull FileFilter fileFilter, @NonNull Comparator<File> fileComparator) {
this.fileComparator = fileComparator;
this.fileFilter = fileFilter;
this.files = files;
}
}
public interface OnSongsListedCallback {
void onSongsListed(@NonNull ArrayList<Song> songs, Object extra);
}
}
private static class ListPathsAsyncTask extends ListingFilesDialogAsyncTask<ListPathsAsyncTask.LoadingInfo, String, String[]> {
private WeakReference<OnPathsListedCallback> onPathsListedCallbackWeakReference;
public ListPathsAsyncTask(Context context, OnPathsListedCallback callback) {
super(context);
onPathsListedCallbackWeakReference = new WeakReference<>(callback);
}
@Override
protected void onPreExecute() {
super.onPreExecute();
checkCallbackReference();
}
@Override
protected String[] doInBackground(LoadingInfo... params) {
try {
if (isCancelled() || checkCallbackReference() == null) return null;
LoadingInfo info = params[0];
final String[] paths;
if (info.file.isDirectory()) {
List<File> files = FileUtil.listFilesDeep(info.file, info.fileFilter);
if (isCancelled() || checkCallbackReference() == null) return null;
paths = new String[files.size()];
for (int i = 0; i < files.size(); i++) {
File f = files.get(i);
try {
paths[i] = f.getCanonicalPath(); // canonical path is important here because we want to compare the path with the media store entry later
} catch (IOException e) {
e.printStackTrace();
paths[i] = f.getPath();
}
if (isCancelled() || checkCallbackReference() == null) return paths;
}
} else {
paths = new String[1];
paths[0] = info.file.getPath();
}
return paths;
} catch (Exception e) {
e.printStackTrace();
cancel(false);
return null;
}
}
@Override
protected void onPostExecute(String[] paths) {
super.onPostExecute(paths);
OnPathsListedCallback callback = checkCallbackReference();
if (callback != null) {
callback.onPathsListed(paths);
}
}
private OnPathsListedCallback checkCallbackReference() {
OnPathsListedCallback callback = onPathsListedCallbackWeakReference.get();
if (callback == null) {
cancel(false);
}
return callback;
}
public static class LoadingInfo {
public final File file;
public final FileFilter fileFilter;
public LoadingInfo(File file, FileFilter fileFilter) {
this.file = file;
this.fileFilter = fileFilter;
}
}
public interface OnPathsListedCallback {
void onPathsListed(@Nullable String[] paths);
}
}
private static abstract class ListingFilesDialogAsyncTask<Params, Progress, Result> extends DialogAsyncTask<Params, Progress, Result> {
public ListingFilesDialogAsyncTask(Context context) {
super(context);
}
public ListingFilesDialogAsyncTask(Context context, int showDelay) {
super(context, showDelay);
}
@Override
protected Dialog createDialog(@NonNull Context context) {
return new MaterialDialog.Builder(context)
.title(R.string.listing_files)
.progress(true, 0)
.progressIndeterminateStyle(true)
.cancelListener(new DialogInterface.OnCancelListener() {
@Override
public void onCancel(DialogInterface dialog) {
cancel(false);
}
})
.dismissListener(new DialogInterface.OnDismissListener() {
@Override
public void onDismiss(DialogInterface dialog) {
cancel(false);
}
})
.negativeText(android.R.string.cancel)
.onNegative(new MaterialDialog.SingleButtonCallback() {
@Override
public void onClick(@NonNull MaterialDialog dialog, @NonNull DialogAction which) {
cancel(false);
}
})
.show();
}
}
}