Java tutorial
/* * Nextcloud Android client application * * @author Andy Scherzinger * Copyright (C) 2018 Andy Scherzinger * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE * License as published by the Free Software Foundation; either * version 3 of the License, or any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU AFFERO GENERAL PUBLIC LICENSE for more details. * * You should have received a copy of the GNU Affero General Public * License along with this program. If not, see <http://www.gnu.org/licenses/>. */ package com.owncloud.android.ui.fragment; import android.accounts.Account; import android.app.SearchManager; import android.content.Context; import android.content.Intent; import android.content.res.Resources; import android.net.Uri; import android.os.Bundle; import android.support.annotation.NonNull; import android.support.design.widget.Snackbar; import android.support.v4.app.Fragment; import android.support.v7.widget.AppCompatCheckBox; import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; import android.support.v7.widget.SearchView; import android.text.TextUtils; import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.PopupMenu; import android.widget.TextView; import com.owncloud.android.R; import com.owncloud.android.datamodel.FileDataStorageManager; import com.owncloud.android.datamodel.OCFile; import com.owncloud.android.lib.common.operations.RemoteOperationResult; import com.owncloud.android.lib.resources.shares.OCShare; import com.owncloud.android.lib.resources.shares.SharePermissionsBuilder; import com.owncloud.android.lib.resources.shares.ShareType; import com.owncloud.android.lib.resources.status.OCCapability; import com.owncloud.android.ui.activity.FileActivity; import com.owncloud.android.ui.activity.FileDisplayActivity; import com.owncloud.android.ui.adapter.UserListAdapter; import com.owncloud.android.ui.decoration.SimpleListItemDividerDecoration; import com.owncloud.android.ui.dialog.ExpirationDatePickerDialogFragment; import com.owncloud.android.ui.dialog.SharePasswordDialogFragment; import com.owncloud.android.ui.fragment.util.FileDetailSharingFragmentHelper; import com.owncloud.android.ui.fragment.util.SharingMenuHelper; import com.owncloud.android.utils.ClipboardUtil; import com.owncloud.android.utils.ThemeUtils; import java.util.List; import butterknife.BindView; import butterknife.ButterKnife; import butterknife.OnClick; import butterknife.Unbinder; public class FileDetailSharingFragment extends Fragment implements UserListAdapter.ShareeListAdapterListener { private static final String ARG_FILE = "FILE"; private static final String ARG_ACCOUNT = "ACCOUNT"; // to show share with users/groups info private List<OCShare> shares; private OCFile file; private Account account; private OCCapability capabilities; private OCShare publicShare; private Unbinder unbinder; @BindView(R.id.searchView) SearchView searchView; @BindView(R.id.shareUsersList) RecyclerView usersList; @BindView(R.id.shareNoUsers) TextView noList; @BindView(R.id.share_by_link) AppCompatCheckBox shareByLink; @BindView(R.id.share_link_copy_icon) ImageView shareLinkCopyIcon; @BindView(R.id.overflow_menu_share_link) ImageView overflowMenuShareLink; @BindView(R.id.share_by_link_allow_editing) AppCompatCheckBox shareByLinkAllowEditing; @BindView(R.id.share_by_link_container) LinearLayout shareByLinkContainer; public static FileDetailSharingFragment newInstance(OCFile file, Account account) { FileDetailSharingFragment fragment = new FileDetailSharingFragment(); Bundle args = new Bundle(); args.putParcelable(ARG_FILE, file); args.putParcelable(ARG_ACCOUNT, account); fragment.setArguments(args); return fragment; } @Override public void onActivityCreated(Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); refreshCapabilitiesFromDB(); refreshPublicShareFromDB(); } @Override public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { file = getArguments().getParcelable(ARG_FILE); account = getArguments().getParcelable(ARG_ACCOUNT); if (savedInstanceState != null) { file = savedInstanceState.getParcelable(FileActivity.EXTRA_FILE); account = savedInstanceState.getParcelable(FileActivity.EXTRA_ACCOUNT); } View view = inflater.inflate(R.layout.file_details_sharing_fragment, container, false); unbinder = ButterKnife.bind(this, view); setupView(); return view; } @Override public void onDestroy() { super.onDestroy(); unbinder.unbind(); } @Override public void onAttach(Context context) { super.onAttach(context); if (!(getActivity() instanceof FileActivity)) { throw new IllegalArgumentException("Calling activity must be of type FileActivity"); } } private void setupView() { setShareByLinkInfo(file.isSharedViaLink()); setShareWithUserInfo(); FileDetailSharingFragmentHelper.setupSearchView( (SearchManager) getActivity().getSystemService(Context.SEARCH_SERVICE), searchView, getActivity().getComponentName()); } /** * Updates Share by link UI * * @param isShareByLink flag is share by link is enable */ public void setShareByLinkInfo(boolean isShareByLink) { shareByLink.setChecked(isShareByLink); if (isShareByLink) { shareLinkCopyIcon.setVisibility(View.VISIBLE); } else { shareLinkCopyIcon.setVisibility(View.INVISIBLE); } int accentColor = ThemeUtils.primaryAccentColor(getContext()); ThemeUtils.tintCheckbox(shareByLink, accentColor); ThemeUtils.tintCheckbox(shareByLinkAllowEditing, accentColor); setLinkDetailVisible(isShareByLink); } private void setLinkDetailVisible(boolean visible) { if (visible) { if (file.isFolder()) { shareByLinkAllowEditing.setVisibility(View.VISIBLE); } else { shareByLinkAllowEditing.setVisibility(View.INVISIBLE); } overflowMenuShareLink.setVisibility(View.VISIBLE); } else { shareByLinkAllowEditing.setVisibility(View.INVISIBLE); overflowMenuShareLink.setVisibility(View.INVISIBLE); } } /** * Update Share With data */ public void setShareWithUserInfo() { // Get Users and Groups if (((FileActivity) getActivity()).getStorageManager() != null) { FileDataStorageManager fileDataStorageManager = ((FileActivity) getActivity()).getStorageManager(); shares = fileDataStorageManager.getSharesWithForAFile(file.getRemotePath(), account.name); // Update list of users/groups updateListOfUserGroups(); } } private void updateListOfUserGroups() { // TODO Refactoring: create a new {@link ShareUserListAdapter} instance with every call should not be needed if (shares.size() > 0) { usersList.setVisibility(View.VISIBLE); usersList.setAdapter(new UserListAdapter(getActivity().getSupportFragmentManager(), getActivity().getApplicationContext(), shares, account, file, this)); usersList.setLayoutManager(new LinearLayoutManager(getContext())); usersList.addItemDecoration(new SimpleListItemDividerDecoration(getContext())); noList.setVisibility(View.GONE); } else { usersList.setVisibility(View.GONE); noList.setVisibility(View.VISIBLE); } } @OnClick(R.id.share_by_link) public void toggleShareByLink() { if (shareByLink.isChecked()) { createShareLink(); } else { ((FileActivity) getActivity()).getFileOperationsHelper().unshareFileViaLink(file); } } private void createShareLink() { if (capabilities != null && capabilities.getFilesSharingPublicPasswordEnforced().isTrue()) { // password enforced by server, request to the user before trying to create requestPasswordForShareViaLink(true); } else { // create without password if not enforced by server or we don't know if enforced; ((FileActivity) getActivity()).getFileOperationsHelper().shareFileViaLink(file, null); } } private void showSendLinkTo() { if (file.isSharedViaLink()) { if (TextUtils.isEmpty(file.getPublicLink())) { ((FileActivity) getActivity()).getFileOperationsHelper().getFileWithLink(file); } else { ((FileDisplayActivity) getActivity()).showShareLinkDialog(file.getPublicLink()); } } } @OnClick({ R.id.share_link_copy_icon }) public void copyLinkToClipboard() { if (file.isSharedViaLink()) { if (TextUtils.isEmpty(file.getPublicLink())) { ((FileActivity) getActivity()).getFileOperationsHelper().getFileWithLink(file); } else { ClipboardUtil.copyToClipboard(getActivity(), file.getPublicLink()); } } } @OnClick(R.id.share_by_link_allow_editing) public void toggleShareLinkAllowEditing() { if (file.isSharedViaLink()) { ((FileActivity) getActivity()).getFileOperationsHelper().setUploadPermissionsToShare(file, shareByLinkAllowEditing.isChecked()); } } @OnClick(R.id.overflow_menu_share_link) public void showLinkOverflowMenu() { Context context = getContext(); if (context != null && ThemeUtils.themingEnabled(context)) { // use grey as fallback for elements where custom theming is not available context.getTheme().applyStyle(R.style.FallbackThemingTheme, true); } else { context = getActivity(); } PopupMenu popup = new PopupMenu(context, overflowMenuShareLink); popup.inflate(R.menu.file_detail_sharing_link_menu); prepareOptionsMenu(popup.getMenu()); popup.setOnMenuItemClickListener(this::optionsItemSelected); popup.show(); } private void prepareOptionsMenu(Menu menu) { Resources res = getResources(); SharingMenuHelper.setupHideFileListingMenuItem(menu.findItem(R.id.action_hide_file_listing), file.isFolder(), shareByLinkAllowEditing.isChecked(), publicShare.getPermissions()); SharingMenuHelper.setupPasswordMenuItem(menu.findItem(R.id.action_password), publicShare.isPasswordProtected()); SharingMenuHelper.setupExpirationDateMenuItem(menu.findItem(R.id.action_share_expiration_date), publicShare.getExpirationDate(), res); } private boolean optionsItemSelected(MenuItem item) { switch (item.getItemId()) { case R.id.action_hide_file_listing: { item.setChecked(!item.isChecked()); if (capabilities.getFilesFileDrop().isTrue()) { setHideFileListingPermissionsToShare(publicShare, item.isChecked()); } else { // not supported in ownCloud showNotSupportedByOcMessage(); } return true; } case R.id.action_password: { requestPasswordForShareViaLink(false); return true; } case R.id.action_share_expiration_date: { ExpirationDatePickerDialogFragment dialog = ExpirationDatePickerDialogFragment.newInstance(file, -1); dialog.show(getActivity().getSupportFragmentManager(), ExpirationDatePickerDialogFragment.DATE_PICKER_DIALOG); return true; } case R.id.action_share_send_link: { if (shareByLink.isChecked() && file.isSharedViaLink() && !TextUtils.isEmpty(file.getPublicLink())) { ((FileDisplayActivity) getActivity()).showShareLinkDialog(file.getPublicLink()); } else { showSendLinkTo(); } return true; } default: return super.onOptionsItemSelected(item); } } @Override public void setHideFileListingPermissionsToShare(OCShare share, boolean hideFileListing) { ((FileActivity) getActivity()).getFileOperationsHelper().setHideFileListingPermissionsToShare(share, hideFileListing); } @Override public void showNotSupportedByOcMessage() { if (getView() != null) { Snackbar.make(getView(), R.string.files_drop_not_supported, Snackbar.LENGTH_LONG) .setAction(R.string.learn_more, v -> { Intent i = new Intent(Intent.ACTION_VIEW); i.setData(Uri.parse(getString(R.string.url_server_install))); startActivity(i); }).show(); } } /** * Updates the UI after the result of an update operation on the edited {@link OCFile}. * * @param result {@link RemoteOperationResult} of an update on the edited {@link OCFile} sharing information. * @param file the edited {@link OCFile} */ public void onUpdateShareInformation(RemoteOperationResult result, OCFile file) { this.file = file; if (result.isSuccess()) { refreshUiFromDB(); } else { setupView(); } } /** * Get {@link OCShare} instance from DB and updates the UI. * * Depends on the parent Activity provides a {@link com.owncloud.android.datamodel.FileDataStorageManager} * instance ready to use. If not ready, does nothing. */ private void refreshUiFromDB() { FileDataStorageManager storageManager = ((FileActivity) getActivity()).getStorageManager(); if (storageManager != null) { if (publicShare != null) { // Get edited shared by link publicShare = storageManager.getShareById(publicShare.getId()); } // Updates UI with new state setupView(); } } @Override public void unshareWith(OCShare share) { ((FileActivity) getActivity()).getFileOperationsHelper().unshareFileWithUserOrGroup(file, share.getShareType(), share.getShareWith()); } @Override public int updatePermissionsToShare(OCShare share, boolean canReshare, boolean canEdit, boolean canEditCreate, boolean canEditChange, boolean canEditDelete) { SharePermissionsBuilder spb = new SharePermissionsBuilder(); spb.setSharePermission(canReshare); if (file.isFolder()) { spb.setUpdatePermission(canEditChange).setCreatePermission(canEditCreate) .setDeletePermission(canEditDelete); } else { spb.setUpdatePermission(canEdit); } int permissions = spb.build(); ((FileActivity) getActivity()).getFileOperationsHelper().setPermissionsToShare(share, permissions); return permissions; } /** * Starts a dialog that requests a password to the user to protect a share link. * * @param createShare When 'true', the request for password will be followed by the creation of a new * public link; when 'false', a public share is assumed to exist, and the password * is bound to it. */ public void requestPasswordForShareViaLink(boolean createShare) { SharePasswordDialogFragment dialog = SharePasswordDialogFragment.newInstance(file, createShare); dialog.show(getChildFragmentManager(), SharePasswordDialogFragment.PASSWORD_FRAGMENT); } @Override public void requestPasswordForShare(OCShare share) { SharePasswordDialogFragment dialog = SharePasswordDialogFragment.newInstance(share); dialog.show(getChildFragmentManager(), SharePasswordDialogFragment.PASSWORD_FRAGMENT); } /** * Get known server capabilities from DB * * Depends on the parent Activity provides a {@link com.owncloud.android.datamodel.FileDataStorageManager} * instance ready to use. If not ready, does nothing. */ public void refreshCapabilitiesFromDB() { if (((FileActivity) getActivity()).getStorageManager() != null) { capabilities = ((FileActivity) getActivity()).getStorageManager().getCapability(account.name); } } /** * Get public link from the DB to fill in the "Share link" section in the UI. * * Takes into account server capabilities before reading database. * * Depends on the parent Activity provides a {@link com.owncloud.android.datamodel.FileDataStorageManager} * instance ready to use. If not ready, does nothing. */ public void refreshPublicShareFromDB() { if (FileDetailSharingFragmentHelper.isPublicShareDisabled(capabilities)) { shareByLinkContainer.setVisibility(View.GONE); } else if (((FileActivity) getActivity()).getStorageManager() != null) { // Get public share publicShare = ((FileActivity) getActivity()).getStorageManager() .getFirstShareByPathAndType(file.getRemotePath(), ShareType.PUBLIC_LINK, ""); // Update public share section updatePublicShareSection(); } } /** * Updates in the UI the section about public share with the information * in the current public share bound to, if any. */ private void updatePublicShareSection() { if (publicShare != null && ShareType.PUBLIC_LINK.equals(publicShare.getShareType())) { shareByLink.setChecked(true); if (publicShare.getPermissions() > OCShare.READ_PERMISSION_FLAG) { shareByLinkAllowEditing.setChecked(true); } else { shareByLinkAllowEditing.setChecked(false); } setShareByLinkInfo(true); } else { setShareByLinkInfo(false); } } @Override public void onSaveInstanceState(@NonNull Bundle outState) { super.onSaveInstanceState(outState); outState.putParcelable(FileActivity.EXTRA_FILE, file); outState.putParcelable(FileActivity.EXTRA_ACCOUNT, account); } }