package de.geeksfactory.opacclient.frontend; import android.annotation.SuppressLint; import android.annotation.TargetApi; import android.app.Activity; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; import android.content.SharedPreferences; import android.graphics.Bitmap; import android.graphics.Color; import android.os.AsyncTask; import android.os.Build; import android.os.Bundle; import android.preference.PreferenceManager; import android.print.PrintAttributes; import android.print.PrintDocumentAdapter; import android.print.PrintManager; import android.support.design.widget.AppBarLayout; import android.support.v4.app.Fragment; import android.support.v7.app.AlertDialog; import android.support.v7.graphics.Palette; import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; import android.support.v7.widget.Toolbar; import android.util.Log; import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; import android.view.View; import android.view.View.OnClickListener; import android.view.ViewGroup; import android.view.animation.AnimationUtils; import android.webkit.WebView; import android.webkit.WebViewClient; import android.widget.AdapterView; import android.widget.AdapterView.OnItemClickListener; import android.widget.CheckBox; import android.widget.FrameLayout; import android.widget.ImageView; import android.widget.ListView; import android.widget.ProgressBar; import android.widget.TextView; import android.widget.Toast; import net.opacapp.multilinecollapsingtoolbar.CollapsingToolbarLayout; import org.apache.http.client.HttpClient; import org.joda.time.LocalDate; import org.joda.time.format.DateTimeFormat; import java.io.IOException; import java.io.UnsupportedEncodingException; import java.util.List; import de.geeksfactory.opacclient.OpacClient; import de.geeksfactory.opacclient.R; import de.geeksfactory.opacclient.apis.BaseApi; import de.geeksfactory.opacclient.apis.EbookServiceApi; import de.geeksfactory.opacclient.apis.EbookServiceApi.BookingResult; import de.geeksfactory.opacclient.apis.OpacApi; import de.geeksfactory.opacclient.apis.OpacApi.MultiStepResult; import de.geeksfactory.opacclient.apis.OpacApi.ReservationResult; import de.geeksfactory.opacclient.frontend.MultiStepResultHelper.Callback; import de.geeksfactory.opacclient.frontend.MultiStepResultHelper.StepTask; import de.geeksfactory.opacclient.networking.AndroidHttpClientFactory; import de.geeksfactory.opacclient.networking.CoverDownloadTask; import de.geeksfactory.opacclient.objects.Account; import de.geeksfactory.opacclient.objects.Copy; import de.geeksfactory.opacclient.objects.CoverHolder; import de.geeksfactory.opacclient.objects.Detail; import de.geeksfactory.opacclient.objects.DetailedItem; import de.geeksfactory.opacclient.objects.SearchResult; import de.geeksfactory.opacclient.storage.AccountDataSource; import de.geeksfactory.opacclient.storage.StarDataSource; import de.geeksfactory.opacclient.ui.AppCompatProgressDialog; import de.geeksfactory.opacclient.ui.WhitenessUtils; import de.geeksfactory.opacclient.utils.BitmapUtils; import de.geeksfactory.opacclient.utils.CompatibilityUtils; import de.geeksfactory.opacclient.utils.ErrorReporter; import de.geeksfactory.opacclient.utils.PrintUtils; import su.j2e.rvjoiner.JoinableAdapter; import su.j2e.rvjoiner.JoinableLayout; import su.j2e.rvjoiner.RvJoiner; /** * A fragment representing a single SearchResult detail screen. This fragment is either contained in * a {@link SearchResultListActivity} in two-pane mode (on tablets) or a {@link * SearchResultDetailActivity} on handsets. */ public class SearchResultDetailFragment extends Fragment implements Toolbar.OnMenuItemClickListener { /** * The fragment argument representing the item ID that this fragment represents. */ public static final String ARG_ITEM_ID = "item_id"; public static final String ARG_ITEM_NR = "item_nr"; public static final String ARG_ITEM_COVER_BITMAP = "item_cover_bitmap"; public static final String ARG_ITEM_MEDIATYPE = "item_mediatype"; /** * A dummy implementation of the {@link Callbacks} interface that does nothing. Used only when * this fragment is not attached to an activity. */ private static Callbacks dummyCallbacks = new Callbacks() { @Override public void removeFragment() { } }; /** * The fragment's current callback object, which is notified of list item clicks. */ private Callbacks callbacks = dummyCallbacks; protected boolean back_button_visible = false; protected boolean image_analyzed = false; protected Toolbar toolbar; protected ImageView ivCover; protected FrameLayout coverWrapper; protected View gradientBottom, gradientTop; protected RecyclerView rvDetails; protected ProgressBar progressBar; protected FrameLayout errorView; protected CollapsingToolbarLayout collapsingToolbar; protected AppBarLayout appBarLayout; /** * The detailled item that this fragment represents. */ private DetailedItem item; private String id; private Integer nr; private OpacClient app; private View view; private FetchTask ft; private AppCompatProgressDialog dialog; private AlertDialog adialog; private boolean account_switched = false; private boolean invalidated = false; private boolean progress = false; private Boolean[] cardAnimations; /** * Mandatory empty constructor for the fragment manager to instantiate the fragment (e.g. upon * screen orientation changes). */ public SearchResultDetailFragment() { } @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setRetainInstance(true); } public void setProgress(boolean show, boolean animate) { progress = show; if (view != null) { View content = rvDetails; if (show) { if (animate) { progressBar.startAnimation(AnimationUtils.loadAnimation( getActivity(), android.R.anim.fade_in)); content.startAnimation(AnimationUtils.loadAnimation( getActivity(), android.R.anim.fade_out)); } else { progressBar.clearAnimation(); content.clearAnimation(); } progressBar.setVisibility(View.VISIBLE); content.setVisibility(View.GONE); } else { if (animate) { progressBar.startAnimation(AnimationUtils.loadAnimation( getActivity(), android.R.anim.fade_out)); content.startAnimation(AnimationUtils.loadAnimation( getActivity(), android.R.anim.fade_in)); } else { progressBar.clearAnimation(); content.clearAnimation(); } progressBar.setVisibility(View.GONE); content.setVisibility(View.VISIBLE); } } } public void showConnectivityError() { errorView.removeAllViews(); View connError = getActivity().getLayoutInflater().inflate( R.layout.error_connectivity, errorView); connError.findViewById(R.id.btRetry) .setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { errorView.removeAllViews(); reload(); } }); progressBar.startAnimation(AnimationUtils.loadAnimation(getActivity(), android.R.anim.fade_out)); rvDetails.startAnimation(AnimationUtils.loadAnimation(getActivity(), android.R.anim.fade_out)); connError.startAnimation(AnimationUtils.loadAnimation(getActivity(), android.R.anim.fade_in)); progressBar.setVisibility(View.GONE); rvDetails.setVisibility(View.GONE); connError.setVisibility(View.VISIBLE); } public void setProgress() { setProgress(progress, false); } private void load(int nr, String id) { setProgress(true, true); this.id = id; this.nr = nr; ft = new FetchTask(nr, id); ft.execute(); } private void reload() { load(nr, id); } @Override public void onAttach(Activity activity) { super.onAttach(activity); app = (OpacClient) activity.getApplication(); // Activities containing this fragment must implement its callbacks. if (!(activity instanceof Callbacks)) { throw new IllegalStateException( "Activity must implement fragment's callbacks."); } callbacks = (Callbacks) activity; } @Override public void onViewCreated(View view, Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); if (item != null) { display(); } else if (getArguments().containsKey(ARG_ITEM_ID) || getArguments().containsKey(ARG_ITEM_NR)) { load(getArguments().getInt(ARG_ITEM_NR), getArguments().getString(ARG_ITEM_ID)); } } @Override public void onDetach() { super.onDetach(); // Reset the active callbacks interface to the dummy implementation. callbacks = dummyCallbacks; } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View rootView = inflater.inflate(R.layout.fragment_searchresult_detail, container, false); view = rootView; findViews(); setHasOptionsMenu(false); if (getActivity() instanceof SearchResultDetailActivity) { // This applies on phones, where the Toolbar is also the // main ActionBar of the Activity and needs a back button toolbar.setNavigationIcon(R.drawable.ic_arrow_back_white_24dp); toolbar.setNavigationOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { getActivity().supportFinishAfterTransition(); } }); back_button_visible = true; } toolbar.inflateMenu(R.menu.search_result_details_activity); refreshMenu(toolbar.getMenu()); toolbar.setOnMenuItemClickListener(this); setRetainInstance(true); setProgress(); if (getArguments().containsKey(ARG_ITEM_COVER_BITMAP)) { Bitmap bitmap = getArguments().getParcelable(ARG_ITEM_COVER_BITMAP); ivCover.setImageBitmap(bitmap); analyzeCover(bitmap); showCoverView(true); } else { showCoverView(false); } return rootView; } private void analyzeCover(Bitmap bitmap) { try { Palette.from(bitmap).generate(new Palette.PaletteAsyncListener() { @Override public void onGenerated(Palette palette) { Palette.Swatch swatch = palette.getDarkVibrantSwatch(); if (swatch == null) swatch = palette.getDarkMutedSwatch(); if (swatch == null) swatch = palette.getLightVibrantSwatch(); if (swatch == null) swatch = palette.getLightMutedSwatch(); if (swatch == null && palette.getSwatches().size() > 0) { swatch = palette.getSwatches().get(0); } if (swatch != null) { appBarLayout.setBackgroundColor(swatch.getRgb()); collapsingToolbar.setContentScrimColor(swatch.getRgb()); if (getActivity() != null && getActivity() instanceof SearchResultDetailActivity && Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { // show darkened color in status bar float[] hsv = swatch.getHsl(); hsv[2] *= 0.95f; getActivity().getWindow().setStatusBarColor(Color.HSVToColor(hsv)); } } } }); analyzeWhitenessOfCoverAsync(bitmap); image_analyzed = true; } catch (IllegalArgumentException ignored) { Log.w("analyzeCover", "Invalid bitmap received"); } } private void findViews() { toolbar = (Toolbar) view.findViewById(R.id.toolbar); ivCover = (ImageView) view.findViewById(R.id.ivCover); coverWrapper = (FrameLayout) view.findViewById(R.id.coverWrapper); gradientBottom = view.findViewById(R.id.gradient_bottom); gradientTop = view.findViewById(R.id.gradient_top); collapsingToolbar = (CollapsingToolbarLayout) view.findViewById(R.id.collapsingToolbar); appBarLayout = (AppBarLayout) view.findViewById(R.id.appBarLayout); rvDetails = (RecyclerView) view.findViewById(R.id.rvDetails); progressBar = (ProgressBar) view.findViewById(R.id.progress); errorView = (FrameLayout) view.findViewById(R.id.error_view); } /** * Examine how many white pixels are in the bitmap in order to determine whether or not we need * gradient overlays on top of the image. */ @SuppressLint("NewApi") private void analyzeWhitenessOfCoverAsync(final Bitmap bitmap) { AnalyzeWhitenessTask task = new AnalyzeWhitenessTask(); // Execute in parallel with FetchTask if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) { task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, bitmap); } else { task.execute(bitmap); } } protected void display() { try { Log.i("result", getItem().toString()); } catch (Exception e) { ErrorReporter.handleException(e); } collapsingToolbar.setTitle(getItem().getTitle()); rvDetails.setLayoutManager(new LinearLayoutManager(getActivity())); RvJoiner joiner = new RvJoiner(); addSubhead(joiner, R.string.details_head); joiner.add(new JoinableAdapter(new DetailsAdapter(item.getDetails(), getActivity()))); if (item.getCopies().size() != 0) { addSubhead(joiner, R.string.copies_head); joiner.add(new JoinableAdapter(new CopiesAdapter(item.getCopies(), getActivity()))); } if (item.getVolumesearch() != null) { addSubhead(joiner, R.string.volumes); joiner.add(new JoinableLayout( R.layout.listitem_details_volumesearch, new JoinableLayout.Callback() { @Override public void onInflateComplete(View view, ViewGroup parent) { view.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { app.startVolumeSearch(getActivity(), getItem().getVolumesearch()); } }); } })); } else if (item.getVolumes().size() > 0) { addSubhead(joiner, R.string.volumes); joiner.add(new JoinableAdapter(new VolumesAdapter(item.getVolumes(), getActivity()))); } rvDetails.setAdapter(joiner.getAdapter()); if (id == null || id.equals("")) { id = getItem().getId(); } refreshMenu(toolbar.getMenu()); setProgress(false, true); } private void addSubhead(RvJoiner joiner, final int text) { joiner.add(new JoinableLayout( R.layout.listitem_details_subhead, new JoinableLayout.Callback() { @Override public void onInflateComplete(View view, ViewGroup parent) { ((TextView) view).setText(text); } })); } private void displayCover() { if (getItem().getCoverBitmap() != null) { coverWrapper.setVisibility(View.VISIBLE); Bitmap bm = BitmapUtils.bitmapFromBytes(getItem().getCoverBitmap()); ivCover.setImageBitmap(bm); if (!image_analyzed) { analyzeCover(bm); } showCoverView(true); } else if (getArguments().containsKey(ARG_ITEM_COVER_BITMAP)) { showCoverView(true); } else { showCoverView(false); collapsingToolbar.setTitle(getItem().getTitle()); } } private void showCoverView(boolean b) { if (getActivity() == null) { return; } coverWrapper.setVisibility(b ? View.VISIBLE : View.GONE); if (!b) { appBarLayout.setBackgroundResource(getToolbarBackgroundColor()); } } private int getToolbarBackgroundColor() { if (getActivity() != null) { if (getActivity() instanceof SearchResultDetailActivity) { return R.color.primary_red; } else { return R.color.primary_red_dark; } } else { return R.color.primary_red; } } @Override public void onViewStateRestored(Bundle savedInstanceState) { super.onViewStateRestored(savedInstanceState); if (item != null) { display(); } } protected void dialog_wrong_credentials(String s, final boolean finish) { if (getActivity() == null) { return; } AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()); builder.setMessage(getString(R.string.opac_error) + " " + s) .setCancelable(false) .setNegativeButton(R.string.close, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int id) { dialog.cancel(); if (finish) { callbacks.removeFragment(); } } }) .setPositiveButton(R.string.prefs, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int id) { Intent intent = new Intent(getActivity(), AccountEditActivity.class); intent.putExtra( AccountEditActivity.EXTRA_ACCOUNT_ID, app.getAccount().getId()); startActivity(intent); } }); AlertDialog alert = builder.create(); alert.show(); } @Override public void onStop() { super.onStop(); if (dialog != null) { if (dialog.isShowing()) { dialog.cancel(); } } try { if (ft != null) { if (!ft.isCancelled()) { ft.cancel(true); } } } catch (Exception e) { e.printStackTrace(); } } @Override public void onDestroy() { super.onDestroy(); // TODO: what was this? /*OpacActivity.unbindDrawables(view.findViewById(R.id.rootView)); System.gc();*/ } @Override public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { inflater.inflate(R.menu.search_result_details_activity, menu); refreshMenu(menu); menu.findItem(R.id.action_print).setVisible( Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT); super.onCreateOptionsMenu(menu, inflater); } protected void refreshMenu(Menu menu) { if (item != null) { if (item.isReservable()) { menu.findItem(R.id.action_reservation).setVisible(true); } else { menu.findItem(R.id.action_reservation).setVisible(false); } OpacApi api; try { api = app.getApi(); } catch (OpacClient.LibraryRemovedException e) { return; } if (item.isBookable() && api instanceof EbookServiceApi) { if (((EbookServiceApi) api).isEbook(item)) { menu.findItem(R.id.action_lendebook).setVisible(true); } else { menu.findItem(R.id.action_lendebook).setVisible(false); } } else { menu.findItem(R.id.action_lendebook).setVisible(false); } menu.findItem(R.id.action_tocollection).setVisible( item.getCollectionId() != null); } else { menu.findItem(R.id.action_reservation).setVisible(false); menu.findItem(R.id.action_lendebook).setVisible(false); menu.findItem(R.id.action_tocollection).setVisible(false); } String bib = app.getLibrary().getIdent(); StarDataSource data = new StarDataSource(getActivity()); String _id = id; if (item != null) { _id = item.getId(); } if ((_id == null || _id.equals("")) && item != null) { if (data.isStarredTitle(bib, item.getTitle())) { menu.findItem(R.id.action_star).setIcon( R.drawable.ic_star_1_white_24dp); } } else { if (data.isStarred(bib, _id)) { menu.findItem(R.id.action_star).setIcon( R.drawable.ic_star_1_white_24dp); } } } @Override public boolean onMenuItemClick(MenuItem item) { return onOptionsItemSelected(item); } @Override public boolean onOptionsItemSelected(MenuItem item) { final String bib = app.getLibrary().getIdent(); if (item.getItemId() == R.id.action_reservation) { reservationStart(); return true; } else if (item.getItemId() == R.id.action_lendebook) { bookingStart(); return true; } else if (item.getItemId() == R.id.action_tocollection) { if (getActivity().getIntent().getBooleanExtra("from_collection", false)) { getActivity().finish(); } else { Intent intent = new Intent(getActivity(), SearchResultDetailActivity.class); intent.putExtra(SearchResultDetailFragment.ARG_ITEM_ID, getItem().getCollectionId()); startActivity(intent); getActivity().finish(); } return true; } else if (item.getItemId() == R.id.action_share) { if (getItem() == null) { Toast toast = Toast.makeText(getActivity(), getString(R.string.share_wait), Toast.LENGTH_SHORT); toast.show(); } else { final String title = getItem().getTitle(); final String id = getItem().getId(); final CharSequence[] items = {getString(R.string.share_link), getString(R.string.share_details)}; AlertDialog.Builder builder = new AlertDialog.Builder( getActivity()); builder.setTitle(R.string.share_dialog_select); builder.setItems(items, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int di) { OpacApi api = null; try { api = app.getApi(); } catch (OpacClient.LibraryRemovedException e) { return; } if (di == 0) { // Share link Intent intent = new Intent( android.content.Intent.ACTION_SEND); intent.setType("text/plain"); intent.addFlags(CompatibilityUtils.getNewDocumentIntentFlag()); // Add data to the intent, the receiving app will // decide // what to do with it. intent.putExtra(Intent.EXTRA_SUBJECT, title); String t = title; try { t = java.net.URLEncoder.encode(t, "UTF-8"); } catch (UnsupportedEncodingException e) { } String shareUrl = api.getShareUrl(id, t); if (shareUrl != null) { intent.putExtra(Intent.EXTRA_TEXT, shareUrl); startActivity(Intent.createChooser(intent, getResources() .getString(R.string.share))); } else { Toast toast = Toast.makeText(getActivity(), getString(R.string.share_notsupported), Toast.LENGTH_SHORT); toast.show(); } } else { // Share details Intent intent = new Intent( android.content.Intent.ACTION_SEND); intent.setType("text/plain"); intent.addFlags(CompatibilityUtils.getNewDocumentIntentFlag()); // Add data to the intent, the receiving app will // decide // what to do with it. intent.putExtra(Intent.EXTRA_SUBJECT, title); String t = title; try { t = t != null ? java.net.URLEncoder.encode(t, "UTF-8") : ""; } catch (UnsupportedEncodingException e) { } String text = title + "\n\n"; for (Detail detail : getItem().getDetails()) { String colon = ""; if (!detail.getDesc().endsWith(":")) { colon = ":"; } text += detail.getDesc() + colon + "\n" + detail.getContent() + "\n\n"; } List<Copy> copies = getItem().getCopies(); if (copies.size() > 0) { text += getString(R.string.copies_head) + ":\n\n"; } for (Copy copy : copies) { String labelSeparator = ": "; String infoTypeSeparator = "\n"; String branch = copy.getBranch(); String branchTxt = ""; if (branch != null && !branch.isEmpty()) { branchTxt += getString(R.string.branch) + labelSeparator + branch + infoTypeSeparator; } String dept = copy.getDepartment(); String deptTxt = ""; if (dept != null && !dept.isEmpty()) { deptTxt += getString(R.string.department) + labelSeparator + dept + infoTypeSeparator; } String loc = copy.getLocation(); String locTxt = ""; if (loc != null && !loc.isEmpty()) { locTxt += getString(R.string.location) + labelSeparator + loc + infoTypeSeparator; } String shelfMark = copy.getShelfmark(); String shelfMarkTxt = ""; if (shelfMark != null && !shelfMark.isEmpty()) { shelfMarkTxt += getString(R.string.shelfmark) + labelSeparator + shelfMark + infoTypeSeparator; } String status = copy.getStatus(); String statusTxt = ""; if (status != null && !status.isEmpty()) { statusTxt += getString(R.string.status) + labelSeparator + status + infoTypeSeparator; } String res = copy.getReservations(); String resTxt = ""; if (res != null && !res.isEmpty()) { resTxt += getString(R.string.reservations) + labelSeparator + res + infoTypeSeparator; } String url = copy.getUrl(); String urlTxt = ""; if (url != null && !url.isEmpty()) { urlTxt += getString(R.string.url) + labelSeparator + url + infoTypeSeparator; } LocalDate retDate = copy.getReturnDate(); String retDateTxt = ""; if (retDate != null) { retDateTxt += getString(R.string.return_date) + labelSeparator + DateTimeFormat.shortDate().print(copy.getReturnDate()) + infoTypeSeparator; } text += branchTxt + deptTxt + locTxt + shelfMarkTxt + statusTxt + resTxt + urlTxt + retDateTxt + "\n"; } String shareUrl = api.getShareUrl(id, t); if (shareUrl != null) { text += shareUrl; } intent.putExtra(Intent.EXTRA_TEXT, text); startActivity(Intent.createChooser(intent, getResources().getString(R.string.share))); } } }); AlertDialog alert = builder.create(); alert.show(); } return true; } else if (item.getItemId() == R.id.action_star) { StarDataSource star = new StarDataSource(getActivity()); if (getItem() == null) { Toast toast = Toast.makeText(getActivity(), getString(R.string.star_wait), Toast.LENGTH_SHORT); toast.show(); } else if (getItem().getId() == null || getItem().getId().equals("")) { final String title = getItem().getTitle(); if (title == null || title.equals("")) { Toast toast = Toast.makeText(getActivity(), getString(R.string.star_unsupported), Toast.LENGTH_LONG); toast.show(); } else { if (star.isStarredTitle(bib, title)) { star.remove(star.getItemByTitle(bib, title)); item.setIcon(R.drawable.ic_star_0_white_24dp); } else { star.star(null, title, bib, getItem().getMediaType()); Toast toast = Toast.makeText(getActivity(), getString(R.string.starred), Toast.LENGTH_SHORT); toast.show(); item.setIcon(R.drawable.ic_star_1_white_24dp); } } } else { final String title = getItem().getTitle(); final String id = getItem().getId(); if (star.isStarred(bib, id)) { star.remove(star.getItem(bib, id)); item.setIcon(R.drawable.ic_star_0_white_24dp); } else { star.star(id, title, bib, getItem().getMediaType()); Toast toast = Toast.makeText(getActivity(), getString(R.string.starred), Toast.LENGTH_SHORT); toast.show(); item.setIcon(R.drawable.ic_star_1_white_24dp); } } return true; } else if (item.getItemId() == R.id.action_print) { if (getItem() == null) { Toast toast = Toast.makeText(getActivity(), getString(R.string.print_wait), Toast.LENGTH_SHORT); toast.show(); } else { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { print(); } } return true; } else { return super.onOptionsItemSelected(item); } } @TargetApi(Build.VERSION_CODES.KITKAT) private void print() { WebView webView = new WebView(getActivity()); webView.setWebViewClient(new WebViewClient() { @Override public void onPageFinished(WebView webView, String url) { PrintManager printManager = (PrintManager) getActivity() .getSystemService(Context.PRINT_SERVICE); PrintDocumentAdapter printAdapter = webView.createPrintDocumentAdapter(); String jobName = getItem().getTitle(); if (jobName == null || jobName.equals("")) { jobName = getString(R.string.no_title); } printManager.print(jobName, printAdapter, new PrintAttributes.Builder().build()); } }); String templateDetailles = PrintUtils.printDetails(getItem(), getContext()); webView.loadDataWithBaseURL(null, templateDetailles, "text/HTML", "UTF-8", null); } public DetailedItem getItem() { return item; } protected void dialog_no_credentials() { if (getActivity() == null) { return; } AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()); builder.setMessage(R.string.status_nouser) .setCancelable(false) .setNegativeButton(R.string.close, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int id) { dialog.cancel(); } }) .setPositiveButton(R.string.accounts_edit, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int id) { Intent intent = new Intent(getActivity(), AccountEditActivity.class); intent.putExtra( AccountEditActivity.EXTRA_ACCOUNT_ID, app.getAccount().getId()); startActivity(intent); } }); AlertDialog alert = builder.create(); alert.show(); } protected void reservationStart() { if (invalidated) { new RestoreSessionTask(false).execute(); } OpacApi api = null; try { api = app.getApi(); } catch (OpacClient.LibraryRemovedException e) { return; } if (api instanceof EbookServiceApi) { SharedPreferences sp = PreferenceManager .getDefaultSharedPreferences(getActivity()); if (sp.getString("email", "").equals("") && ((EbookServiceApi) api).isEbook(item)) { AlertDialog.Builder builder = new AlertDialog.Builder( getActivity()); builder.setMessage(getString(R.string.opac_error_email)) .setCancelable(false) .setNegativeButton(R.string.close, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int id) { dialog.cancel(); } }) .setPositiveButton(R.string.prefs, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int id) { dialog.dismiss(); app.toPrefs(getActivity()); } }); AlertDialog alert = builder.create(); alert.show(); return; } } AccountDataSource data = new AccountDataSource(getActivity()); final List<Account> accounts = data.getAccountsWithPassword(app .getLibrary().getIdent()); if (accounts.size() == 0) { dialog_no_credentials(); } else if (accounts.size() > 1 && !getActivity().getIntent().getBooleanExtra("reservation", false) && (api.getSupportFlags() & OpacApi.SUPPORT_FLAG_CHANGE_ACCOUNT) != 0 && !(SearchResultDetailFragment.this.id == null || SearchResultDetailFragment.this.id.equals("null") || SearchResultDetailFragment.this.id .equals(""))) { AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()); // Get the layout inflater LayoutInflater inflater = getActivity().getLayoutInflater(); View view = inflater.inflate(R.layout.dialog_simple_list, null, false); ListView lv = (ListView) view.findViewById(R.id.lvBibs); AccountListAdapter adapter = new AccountListAdapter(getActivity(), accounts); lv.setAdapter(adapter); lv.setOnItemClickListener(new OnItemClickListener() { @Override public void onItemClick(AdapterView<?> parent, View view, int position, long id) { if (accounts.get(position).getId() != app.getAccount() .getId() || account_switched) { if (SearchResultDetailFragment.this.id == null || SearchResultDetailFragment.this.id .equals("null") || SearchResultDetailFragment.this.id .equals("")) { Toast.makeText(getActivity(), R.string.accchange_sorry, Toast.LENGTH_LONG) .show(); } else { if (app.getAccount().getId() != accounts.get(position).getId()) { app.setAccount(accounts.get(position).getId()); } Intent intent = new Intent(getActivity(), SearchResultDetailActivity.class); intent.putExtra( SearchResultDetailFragment.ARG_ITEM_ID, SearchResultDetailFragment.this.id); // TODO: refresh fragment instead intent.putExtra("reservation", true); startActivity(intent); } } else { reservationDo(); } adialog.dismiss(); } }); builder.setTitle(R.string.account_select) .setView(view) .setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int id) { adialog.cancel(); } }); adialog = builder.create(); adialog.show(); } else { reservationDo(); } } public void reservationDo() { final SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(getActivity()); OpacApi api; try { api = app.getApi(); } catch (OpacClient.LibraryRemovedException e) { return; } if (sp.getBoolean("reservation_fee_warning_ignore", false) || (api.getSupportFlags() & OpacApi.SUPPORT_FLAG_WARN_RESERVATION_FEES) > 0) { reservationPerform(); } else { AlertDialog.Builder builder = new AlertDialog.Builder( getActivity()); View content = getActivity().getLayoutInflater() .inflate(R.layout.dialog_reservation_fees, null); final CheckBox check = (CheckBox) content.findViewById(R.id.check_box1); builder.setView(content) .setCancelable(false) .setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() { @Override public void onClick( DialogInterface dialog, int id) { dialog.cancel(); } }) .setPositiveButton(R.string.reservation_fee_continue, new DialogInterface.OnClickListener() { @Override public void onClick( DialogInterface dialog, int id) { if (check.isChecked()) { sp.edit().putBoolean("reservation_fee_warning_ignore", true).apply(); } reservationPerform(); } }); AlertDialog alert = builder.create(); alert.show(); } } public void reservationPerform() { MultiStepResultHelper<DetailedItem> msrhReservation = new MultiStepResultHelper<>( getActivity(), item, R.string.doing_res); msrhReservation.setCallback(new Callback<DetailedItem>() { @Override public void onSuccess(MultiStepResult result) { AccountDataSource adata = new AccountDataSource(getActivity()); adata.invalidateCachedAccountData(app.getAccount()); if (result.getMessage() != null) { AlertDialog.Builder builder = new AlertDialog.Builder( getActivity()); builder.setMessage(result.getMessage()) .setCancelable(false) .setNegativeButton(R.string.close, new DialogInterface.OnClickListener() { @Override public void onClick( DialogInterface dialog, int id) { dialog.cancel(); } }) .setPositiveButton(R.string.account, new DialogInterface.OnClickListener() { @Override public void onClick( DialogInterface dialog, int id) { Intent intent = new Intent( getActivity(), app.getMainActivity()); intent.putExtra(MainActivity.EXTRA_FRAGMENT, "account"); getActivity().startActivity(intent); getActivity().finish(); } }); AlertDialog alert = builder.create(); alert.show(); } else { Intent intent = new Intent(getActivity(), app .getMainActivity()); intent.putExtra(MainActivity.EXTRA_FRAGMENT, "account"); getActivity().startActivity(intent); getActivity().finish(); } } @Override public void onError(MultiStepResult result) { dialog_wrong_credentials(result.getMessage(), false); } @Override public void onUnhandledResult(MultiStepResult result) { } @Override public void onUserCancel() { } @Override public StepTask<?> newTask(MultiStepResultHelper helper, int useraction, String selection, DetailedItem item) { return new ResTask(helper, useraction, selection, item); } }); msrhReservation.start(); } protected void bookingStart() { AccountDataSource data = new AccountDataSource(getActivity()); final List<Account> accounts = data.getAccountsWithPassword(app .getLibrary().getIdent()); if (accounts.size() == 0) { dialog_no_credentials(); } else if (accounts.size() > 1) { AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()); // Get the layout inflater LayoutInflater inflater = getActivity().getLayoutInflater(); View view = inflater.inflate(R.layout.dialog_simple_list, null, false); ListView lv = (ListView) view.findViewById(R.id.lvBibs); AccountListAdapter adapter = new AccountListAdapter(getActivity(), accounts); lv.setAdapter(adapter); lv.setOnItemClickListener(new OnItemClickListener() { @Override public void onItemClick(AdapterView<?> parent, View view, int position, long id) { if (app.getAccount().getId() != accounts.get(position).getId()) { app.setAccount(accounts.get(position).getId()); } bookingDo(); adialog.dismiss(); } }); builder.setTitle(R.string.account_select) .setView(view) .setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int id) { adialog.cancel(); } }); adialog = builder.create(); adialog.show(); } else { bookingDo(); } } public void bookingDo() { MultiStepResultHelper<DetailedItem> msrhBooking = new MultiStepResultHelper<>( getActivity(), item, R.string.doing_booking); msrhBooking.setCallback(new Callback<DetailedItem>() { @Override public void onSuccess(MultiStepResult result) { if (getActivity() == null) { return; } AccountDataSource adata = new AccountDataSource(getActivity()); adata.invalidateCachedAccountData(app.getAccount()); Intent intent = new Intent(getActivity(), app.getMainActivity()); intent.putExtra(MainActivity.EXTRA_FRAGMENT, "account"); getActivity().startActivity(intent); getActivity().finish(); } @Override public void onError(MultiStepResult result) { if (getActivity() == null) { return; } dialog_wrong_credentials(result.getMessage(), false); } @Override public void onUnhandledResult(MultiStepResult result) { } @Override public void onUserCancel() { } @Override public StepTask<?> newTask(MultiStepResultHelper helper, int useraction, String selection, DetailedItem item) { return new BookingTask(helper, useraction, selection, item); } }); msrhBooking.start(); } /** * A callback interface that all activities containing this fragment must implement. This * mechanism allows activities to be notified of item selections. */ public interface Callbacks { /** * Callback for when the fragment should be deleted */ public void removeFragment(); } public class LoadCoverTask extends CoverDownloadTask { public LoadCoverTask(CoverHolder item, int width, int height, HttpClient httpClient) { super(getActivity(), item, httpClient); this.width = width; this.height = height; } protected void onPostExecute(CoverHolder item) { displayCover(); } } public class FetchTask extends AsyncTask<Void, Void, DetailedItem> { protected boolean success = true; protected Integer nr; protected String id; public FetchTask(Integer nr, String id) { this.nr = nr; this.id = id; } @Override protected DetailedItem doInBackground(Void... voids) { try { DetailedItem res; if (id != null && !id.equals("")) { SharedPreferences sp = PreferenceManager .getDefaultSharedPreferences(getActivity()); String homebranch = sp.getString( OpacClient.PREF_HOME_BRANCH_PREFIX + app.getAccount().getId(), null); if (getActivity().getIntent().hasExtra("reservation") && getActivity().getIntent().getBooleanExtra( "reservation", false)) { app.getApi().start(); } res = app.getApi().getResultById(id, homebranch); if (res.getId() == null) res.setId(id); } else { res = app.getApi().getResult(nr); } if (res.getMediaType() == null && getArguments().containsKey(ARG_ITEM_MEDIATYPE)) { res.setMediaType(SearchResult.MediaType .valueOf(getArguments().getString(ARG_ITEM_MEDIATYPE))); } success = true; return res; } catch (Exception e) { success = false; e.printStackTrace(); } return null; } @Override @SuppressLint("NewApi") protected void onPostExecute(DetailedItem result) { if (getActivity() == null) { return; } if (!success || result == null) { showConnectivityError(); return; } item = result; if (item.getCover() != null && item.getCoverBitmap() == null) { HttpClient httpClient; try { if (app.getApi() instanceof BaseApi) { httpClient = ((BaseApi) app.getApi()).http_client; } else { httpClient = new AndroidHttpClientFactory() .getNewApacheHttpClient(false, true, false); } } catch (OpacClient.LibraryRemovedException e) { httpClient = new AndroidHttpClientFactory() .getNewApacheHttpClient(false, true, false); } new LoadCoverTask(item, collapsingToolbar.getWidth(), collapsingToolbar.getHeight(), httpClient).execute(); } else { displayCover(); } display(); if (getActivity().getIntent().hasExtra("reservation") && getActivity().getIntent().getBooleanExtra("reservation", false)) { reservationStart(); } } } private class AnalyzeWhitenessTask extends AsyncTask<Bitmap, Void, Boolean> { @Override protected Boolean doInBackground(Bitmap... params) { return WhitenessUtils.isBitmapWhiteAtTopOrBottom(params[0]); } @Override protected void onPostExecute(Boolean isWhite) { super.onPostExecute(isWhite); gradientBottom.setVisibility(isWhite ? View.VISIBLE : View.GONE); gradientTop.setVisibility(isWhite ? View.VISIBLE : View.GONE); } } public class ResTask extends StepTask<ReservationResult> { private DetailedItem item; public ResTask(MultiStepResultHelper helper, int useraction, String selection, DetailedItem item) { super(helper, useraction, selection); this.item = item; } @Override protected ReservationResult doInBackground(Void... voids) { try { return app.getApi().reservation(item, app.getAccount(), useraction, selection); } catch (IOException e) { publishProgress(e, "ioerror"); } catch (Exception e) { ErrorReporter.handleException(e); } return null; } @Override protected void onPostExecute(ReservationResult res) { if (getActivity() == null) { return; } if (res == null) { AlertDialog.Builder builder = new AlertDialog.Builder( getActivity()); builder.setMessage(R.string.error) .setCancelable(true) .setNegativeButton(R.string.close, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int id) { dialog.cancel(); } }); AlertDialog alert = builder.create(); alert.show(); return; } super.onPostExecute(res); } } public class BookingTask extends StepTask<BookingResult> { private DetailedItem item; public BookingTask(MultiStepResultHelper helper, int useraction, String selection, DetailedItem item) { super(helper, useraction, selection); this.item = item; } @Override protected BookingResult doInBackground(Void... voids) { try { return ((EbookServiceApi) app.getApi()).booking( item, app.getAccount(), useraction, selection); } catch (IOException | OpacClient.LibraryRemovedException e) { publishProgress(e, "ioerror"); } catch (Exception e) { ErrorReporter.handleException(e); } return null; } @Override protected void onPostExecute(BookingResult res) { if (getActivity() == null) { return; } if (res == null) { AlertDialog.Builder builder = new AlertDialog.Builder( getActivity()); builder.setMessage(R.string.error) .setCancelable(true) .setNegativeButton(R.string.close, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int id) { dialog.cancel(); } }); AlertDialog alert = builder.create(); alert.show(); return; } super.onPostExecute(res); } } public class RestoreSessionTask extends AsyncTask<Void, Void, Integer> { private boolean reservation; public RestoreSessionTask(boolean reservation) { this.reservation = reservation; } @Override protected Integer doInBackground(Void... voids) { try { if (id != null) { SharedPreferences sp = PreferenceManager .getDefaultSharedPreferences(getActivity()); String homebranch = sp.getString( OpacClient.PREF_HOME_BRANCH_PREFIX + app.getAccount().getId(), null); app.getApi().getResultById(id, homebranch); return 0; } else { ErrorReporter.handleException( new Throwable("No ID supplied")); } } catch (IOException e) { e.printStackTrace(); } catch (Exception e) { ErrorReporter.handleException(e); } return 1; } @Override protected void onPostExecute(Integer result) { if (getActivity() == null) { return; } if (reservation) { reservationDo(); } } } }