/* * ownCloud Android client application * * @author masensio * @author David A. Velasco * @author Juan Carlos González Cabrero * Copyright (C) 2015 ownCloud Inc. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2, * as published by the Free Software Foundation. * * 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package com.owncloud.android.ui.activity; import android.app.SearchManager; import android.content.Intent; import android.net.Uri; import android.os.Bundle; import android.text.TextUtils; import com.google.android.material.snackbar.Snackbar; import com.owncloud.android.R; import com.owncloud.android.datamodel.OCFile; import com.owncloud.android.lib.common.OwnCloudAccount; import com.owncloud.android.lib.common.accounts.AccountUtils; import com.owncloud.android.lib.common.operations.RemoteOperation; import com.owncloud.android.lib.common.operations.RemoteOperationResult; import com.owncloud.android.lib.common.utils.Log_OC; import com.owncloud.android.lib.resources.shares.OCShare; import com.owncloud.android.lib.resources.shares.ShareType; import com.owncloud.android.operations.CreateShareViaLinkOperation; import com.owncloud.android.operations.GetSharesForFileOperation; import com.owncloud.android.operations.UnshareOperation; import com.owncloud.android.operations.UpdateSharePermissionsOperation; import com.owncloud.android.providers.UsersAndGroupsSearchProvider; import com.owncloud.android.ui.dialog.ShareLinkToDialog; import com.owncloud.android.ui.fragment.EditShareFragment; import com.owncloud.android.ui.fragment.SearchShareesFragment; import com.owncloud.android.ui.fragment.ShareFileFragment; import com.owncloud.android.ui.fragment.ShareFragmentListener; import com.owncloud.android.utils.ErrorMessageAdapter; import com.owncloud.android.utils.GetShareWithUsersAsyncTask; import java.util.ArrayList; import java.util.Locale; import androidx.fragment.app.DialogFragment; import androidx.fragment.app.Fragment; import androidx.fragment.app.FragmentTransaction; /** * Activity for sharing files. */ public class ShareActivity extends FileActivity implements ShareFragmentListener { private static final String TAG = ShareActivity.class.getSimpleName(); private static final String TAG_SHARE_FRAGMENT = "SHARE_FRAGMENT"; private static final String TAG_SEARCH_FRAGMENT = "SEARCH_USER_AND_GROUPS_FRAGMENT"; private static final String TAG_EDIT_SHARE_FRAGMENT = "EDIT_SHARE_FRAGMENT"; private static final String TAG_PUBLIC_LINK = "PUBLIC_LINK"; /// Tags for dialog fragments private static final String FTAG_CHOOSER_DIALOG = "CHOOSER_DIALOG"; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.share_activity); FragmentTransaction ft = getSupportFragmentManager().beginTransaction(); if (savedInstanceState == null) { // Add Share fragment on first creation Fragment fragment = ShareFileFragment.newInstance(getFile(), getAccount()); ft.replace(R.id.share_fragment_container, fragment, TAG_SHARE_FRAGMENT); ft.commit(); } } protected void onAccountSet(boolean stateWasRecovered) { super.onAccountSet(stateWasRecovered); // Load data into the list Log_OC.d(TAG, "Refreshing lists on account set"); refreshSharesFromStorageManager(); // Request for a refresh of the data through the server (starts an Async Task) refreshUsersOrGroupsListFromServer(); } @Override protected void onNewIntent(Intent intent) { // Verify the action and get the query super.onNewIntent(intent); if (Intent.ACTION_SEARCH.equals(intent.getAction())) { String query = intent.getStringExtra(SearchManager.QUERY); Log_OC.w(TAG, "Ignored Intent requesting to query for " + query); } else if (UsersAndGroupsSearchProvider.ACTION_SHARE_WITH.equals(intent.getAction())) { Uri data = intent.getData(); String dataString = intent.getDataString(); String shareWith = dataString.substring(dataString.lastIndexOf('/') + 1); ArrayList shareeNames = new ArrayList<>(); for (OCShare share : getStorageManager().getSharesWithForAFile(getFile().getRemotePath(), getAccount().name)) { shareeNames.add(share.getShareWith()); } if (!shareeNames.contains(shareWith)) { doShareWith( shareWith, data.getAuthority() ); } } else { Log_OC.e(TAG, String.format(Locale.US, "Unexpected intent %s", intent)); } } private void doShareWith(String shareeName, String dataAuthority) { ShareType shareType = UsersAndGroupsSearchProvider.getShareType(dataAuthority); getFileOperationsHelper().shareFileWithSharee( getFile(), shareeName, shareType, getAppropiatePermissions(shareType) ); } private int getAppropiatePermissions(ShareType shareType) { // check if the Share is FEDERATED boolean isFederated = ShareType.FEDERATED.equals(shareType); if (getFile().isSharedWithMe()) { return OCShare.READ_PERMISSION_FLAG; // minimum permissions } else if (isFederated) { return getFile().isFolder() ? OCShare.FEDERATED_PERMISSIONS_FOR_FOLDER_AFTER_OC9 : OCShare.FEDERATED_PERMISSIONS_FOR_FILE_AFTER_OC9; } else { return getFile().isFolder() ? OCShare.MAXIMUM_PERMISSIONS_FOR_FOLDER : OCShare.MAXIMUM_PERMISSIONS_FOR_FILE; } } @Override public void showSearchUsersAndGroups() { // replace ShareFragment with SearchFragment on demand FragmentTransaction ft = getSupportFragmentManager().beginTransaction(); Fragment searchFragment = SearchShareesFragment.newInstance(getFile(), getAccount()); ft.replace(R.id.share_fragment_container, searchFragment, TAG_SEARCH_FRAGMENT); ft.addToBackStack(null); // BACK button will recover the ShareFragment ft.commit(); } @Override public void showEditShare(OCShare share) { // replace current fragment with EditShareFragment on demand FragmentTransaction ft = getSupportFragmentManager().beginTransaction(); Fragment editShareFragment = EditShareFragment.newInstance(share, getFile(), getAccount()); ft.replace(R.id.share_fragment_container, editShareFragment, TAG_EDIT_SHARE_FRAGMENT); ft.addToBackStack(null); // BACK button will recover the previous fragment ft.commit(); } @Override // Call to Unshare operation public void unshareWith(OCShare share) { OCFile file = getFile(); getFileOperationsHelper().unshareFileWithUserOrGroup(file, share.getShareType(), share.getShareWith()); } /** * Get users and groups from the server to fill in the "share with" list */ @Override public void refreshUsersOrGroupsListFromServer() { // Show loading showLoadingDialog(getString(R.string.common_loading)); // Get Users and Groups GetShareWithUsersAsyncTask getTask = new GetShareWithUsersAsyncTask(this); Object[] params = {getFile(), getAccount(), getStorageManager()}; getTask.execute(params); } /** * Updates the view associated to the activity after the finish of some operation over files * in the current account. * * @param operation Removal operation performed. * @param result Result of the removal. */ @Override public void onRemoteOperationFinish(RemoteOperation operation, RemoteOperationResult result) { super.onRemoteOperationFinish(operation, result); if (result.isSuccess() || (operation instanceof GetSharesForFileOperation && result.getCode() == RemoteOperationResult.ResultCode.SHARE_NOT_FOUND ) ) { Log_OC.d(TAG, "Refreshing view on successful operation or finished refresh"); refreshSharesFromStorageManager(); } if (operation instanceof CreateShareViaLinkOperation) { onCreateShareViaLinkOperationFinish((CreateShareViaLinkOperation) operation, result); } if (operation instanceof UnshareOperation && result.isSuccess() && getEditShareFragment() != null) { getSupportFragmentManager().popBackStack(); } if (operation instanceof UpdateSharePermissionsOperation && getEditShareFragment() != null && getEditShareFragment().isAdded()) { getEditShareFragment().onUpdateSharePermissionsFinished(result); } } /** * Updates the view, reading data from {@link com.owncloud.android.datamodel.FileDataStorageManager}. */ private void refreshSharesFromStorageManager() { ShareFileFragment shareFileFragment = getShareFileFragment(); if (shareFileFragment != null && shareFileFragment.isAdded()) { // only if added to the view hierarchy!! shareFileFragment.refreshCapabilitiesFromDB(); shareFileFragment.refreshUsersOrGroupsListFromDB(); shareFileFragment.refreshPublicShareFromDB(); } SearchShareesFragment searchShareesFragment = getSearchFragment(); if (searchShareesFragment != null && searchShareesFragment.isAdded()) { // only if added to the view hierarchy!! searchShareesFragment.refreshUsersOrGroupsListFromDB(); } EditShareFragment editShareFragment = getEditShareFragment(); if (editShareFragment != null && editShareFragment.isAdded()) { editShareFragment.refreshUiFromDB(); } } /** * Shortcut to get access to the {@link ShareFileFragment} instance, if any * * @return A {@link ShareFileFragment} instance, or null */ private ShareFileFragment getShareFileFragment() { return (ShareFileFragment) getSupportFragmentManager().findFragmentByTag(TAG_SHARE_FRAGMENT); } /** * Shortcut to get access to the {@link SearchShareesFragment} instance, if any * * @return A {@link SearchShareesFragment} instance, or null */ private SearchShareesFragment getSearchFragment() { return (SearchShareesFragment) getSupportFragmentManager().findFragmentByTag(TAG_SEARCH_FRAGMENT); } /** * Shortcut to get access to the {@link EditShareFragment} instance, if any * * @return A {@link EditShareFragment} instance, or null */ private EditShareFragment getEditShareFragment() { return (EditShareFragment) getSupportFragmentManager().findFragmentByTag(TAG_EDIT_SHARE_FRAGMENT); } private void onCreateShareViaLinkOperationFinish(CreateShareViaLinkOperation operation, RemoteOperationResult result) { if (result.isSuccess()) { updateFileFromDB(); // Create dialog to allow the user choose an app to send the link Intent intentToShareLink = new Intent(Intent.ACTION_SEND); // if share to user and share via link multiple ocshares are returned, // therefore filtering for public_link String link = ""; for (Object object : result.getData()) { OCShare shareLink = (OCShare) object; if (TAG_PUBLIC_LINK.equalsIgnoreCase(shareLink.getShareType().name())) { link = shareLink.getShareLink(); break; } } intentToShareLink.putExtra(Intent.EXTRA_TEXT, link); intentToShareLink.setType("text/plain"); String username; try { OwnCloudAccount oca = new OwnCloudAccount(getAccount(), this); if (oca.getDisplayName() != null && !oca.getDisplayName().isEmpty()) { username = oca.getDisplayName(); } else { username = AccountUtils.getUsernameForAccount(getAccount()); } } catch (Exception e) { username = AccountUtils.getUsernameForAccount(getAccount()); } if (username != null) { intentToShareLink.putExtra( Intent.EXTRA_SUBJECT, getString( R.string.subject_user_shared_with_you, username, getFile().getFileName() ) ); } else { intentToShareLink.putExtra( Intent.EXTRA_SUBJECT, getString( R.string.subject_shared_with_you, getFile().getFileName() ) ); } String[] packagesToExclude = new String[]{getPackageName()}; DialogFragment chooserDialog = ShareLinkToDialog.newInstance(intentToShareLink, packagesToExclude); chooserDialog.show(getSupportFragmentManager(), FTAG_CHOOSER_DIALOG); } else { // Detect Failure (403) --> maybe needs password String password = operation.getPassword(); if (result.getCode() == RemoteOperationResult.ResultCode.SHARE_FORBIDDEN && TextUtils.isEmpty(password) && getCapabilities().getFilesSharingPublicEnabled().isUnknown()) { // Was tried without password, but not sure that it's optional. // Try with password before giving up; see also ShareFileFragment#OnShareViaLinkListener ShareFileFragment shareFileFragment = getShareFileFragment(); if (shareFileFragment != null && shareFileFragment.isAdded()) { // only if added to the view hierarchy!! boolean askForPassword = getCapabilities().getFilesSharingPublicAskForOptionalPassword().isTrue(); shareFileFragment.requestPasswordForShareViaLink(true, askForPassword); } } else { Snackbar.make( findViewById(android.R.id.content), ErrorMessageAdapter.getErrorCauseMessage(result, operation, getResources()), Snackbar.LENGTH_LONG ).show(); } } } }