ShareActivity.java 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375
  1. /*
  2. * ownCloud Android client application
  3. *
  4. * @author masensio
  5. * @author David A. Velasco
  6. * @author Juan Carlos González Cabrero
  7. * Copyright (C) 2015 ownCloud Inc.
  8. *
  9. * This program is free software: you can redistribute it and/or modify
  10. * it under the terms of the GNU General Public License version 2,
  11. * as published by the Free Software Foundation.
  12. *
  13. * This program is distributed in the hope that it will be useful,
  14. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  16. * GNU General Public License for more details.
  17. *
  18. * You should have received a copy of the GNU General Public License
  19. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  20. */
  21. package com.owncloud.android.ui.activity;
  22. import android.app.SearchManager;
  23. import android.content.Intent;
  24. import android.net.Uri;
  25. import android.os.Bundle;
  26. import android.text.TextUtils;
  27. import com.google.android.material.snackbar.Snackbar;
  28. import com.owncloud.android.R;
  29. import com.owncloud.android.datamodel.OCFile;
  30. import com.owncloud.android.lib.common.OwnCloudAccount;
  31. import com.owncloud.android.lib.common.accounts.AccountUtils;
  32. import com.owncloud.android.lib.common.operations.RemoteOperation;
  33. import com.owncloud.android.lib.common.operations.RemoteOperationResult;
  34. import com.owncloud.android.lib.common.utils.Log_OC;
  35. import com.owncloud.android.lib.resources.shares.OCShare;
  36. import com.owncloud.android.lib.resources.shares.ShareType;
  37. import com.owncloud.android.operations.CreateShareViaLinkOperation;
  38. import com.owncloud.android.operations.GetSharesForFileOperation;
  39. import com.owncloud.android.operations.UnshareOperation;
  40. import com.owncloud.android.operations.UpdateSharePermissionsOperation;
  41. import com.owncloud.android.providers.UsersAndGroupsSearchProvider;
  42. import com.owncloud.android.ui.dialog.ShareLinkToDialog;
  43. import com.owncloud.android.ui.fragment.EditShareFragment;
  44. import com.owncloud.android.ui.fragment.SearchShareesFragment;
  45. import com.owncloud.android.ui.fragment.ShareFileFragment;
  46. import com.owncloud.android.ui.fragment.ShareFragmentListener;
  47. import com.owncloud.android.utils.ErrorMessageAdapter;
  48. import com.owncloud.android.utils.GetShareWithUsersAsyncTask;
  49. import java.util.ArrayList;
  50. import java.util.Locale;
  51. import androidx.fragment.app.DialogFragment;
  52. import androidx.fragment.app.Fragment;
  53. import androidx.fragment.app.FragmentTransaction;
  54. /**
  55. * Activity for sharing files.
  56. */
  57. public class ShareActivity extends FileActivity implements ShareFragmentListener {
  58. private static final String TAG = ShareActivity.class.getSimpleName();
  59. private static final String TAG_SHARE_FRAGMENT = "SHARE_FRAGMENT";
  60. private static final String TAG_SEARCH_FRAGMENT = "SEARCH_USER_AND_GROUPS_FRAGMENT";
  61. private static final String TAG_EDIT_SHARE_FRAGMENT = "EDIT_SHARE_FRAGMENT";
  62. private static final String TAG_PUBLIC_LINK = "PUBLIC_LINK";
  63. /// Tags for dialog fragments
  64. private static final String FTAG_CHOOSER_DIALOG = "CHOOSER_DIALOG";
  65. @Override
  66. protected void onCreate(Bundle savedInstanceState) {
  67. super.onCreate(savedInstanceState);
  68. setContentView(R.layout.share_activity);
  69. FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
  70. if (savedInstanceState == null) {
  71. // Add Share fragment on first creation
  72. Fragment fragment = ShareFileFragment.newInstance(getFile(), getAccount());
  73. ft.replace(R.id.share_fragment_container, fragment, TAG_SHARE_FRAGMENT);
  74. ft.commit();
  75. }
  76. }
  77. protected void onAccountSet(boolean stateWasRecovered) {
  78. super.onAccountSet(stateWasRecovered);
  79. // Load data into the list
  80. Log_OC.d(TAG, "Refreshing lists on account set");
  81. refreshSharesFromStorageManager();
  82. // Request for a refresh of the data through the server (starts an Async Task)
  83. refreshUsersOrGroupsListFromServer();
  84. }
  85. @Override
  86. protected void onNewIntent(Intent intent) {
  87. // Verify the action and get the query
  88. super.onNewIntent(intent);
  89. if (Intent.ACTION_SEARCH.equals(intent.getAction())) {
  90. String query = intent.getStringExtra(SearchManager.QUERY);
  91. Log_OC.w(TAG, "Ignored Intent requesting to query for " + query);
  92. } else if (UsersAndGroupsSearchProvider.ACTION_SHARE_WITH.equals(intent.getAction())) {
  93. Uri data = intent.getData();
  94. String dataString = intent.getDataString();
  95. String shareWith = dataString.substring(dataString.lastIndexOf('/') + 1);
  96. ArrayList<String> shareeNames = new ArrayList<>();
  97. for (OCShare share : getStorageManager().getSharesWithForAFile(getFile().getRemotePath(), getAccount().name)) {
  98. shareeNames.add(share.getShareWith());
  99. }
  100. if (!shareeNames.contains(shareWith)) {
  101. doShareWith(
  102. shareWith,
  103. data.getAuthority()
  104. );
  105. }
  106. } else {
  107. Log_OC.e(TAG, String.format(Locale.US, "Unexpected intent %s", intent));
  108. }
  109. }
  110. private void doShareWith(String shareeName, String dataAuthority) {
  111. ShareType shareType = UsersAndGroupsSearchProvider.getShareType(dataAuthority);
  112. getFileOperationsHelper().shareFileWithSharee(
  113. getFile(),
  114. shareeName,
  115. shareType,
  116. getAppropiatePermissions(shareType)
  117. );
  118. }
  119. private int getAppropiatePermissions(ShareType shareType) {
  120. // check if the Share is FEDERATED
  121. boolean isFederated = ShareType.FEDERATED.equals(shareType);
  122. if (getFile().isSharedWithMe()) {
  123. return OCShare.READ_PERMISSION_FLAG; // minimum permissions
  124. } else if (isFederated) {
  125. return getFile().isFolder() ? OCShare.FEDERATED_PERMISSIONS_FOR_FOLDER_AFTER_OC9 :
  126. OCShare.FEDERATED_PERMISSIONS_FOR_FILE_AFTER_OC9;
  127. } else {
  128. return getFile().isFolder() ? OCShare.MAXIMUM_PERMISSIONS_FOR_FOLDER :
  129. OCShare.MAXIMUM_PERMISSIONS_FOR_FILE;
  130. }
  131. }
  132. @Override
  133. public void showSearchUsersAndGroups() {
  134. // replace ShareFragment with SearchFragment on demand
  135. FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
  136. Fragment searchFragment = SearchShareesFragment.newInstance(getFile(), getAccount());
  137. ft.replace(R.id.share_fragment_container, searchFragment, TAG_SEARCH_FRAGMENT);
  138. ft.addToBackStack(null); // BACK button will recover the ShareFragment
  139. ft.commit();
  140. }
  141. @Override
  142. public void showEditShare(OCShare share) {
  143. // replace current fragment with EditShareFragment on demand
  144. FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
  145. Fragment editShareFragment = EditShareFragment.newInstance(share, getFile(), getAccount());
  146. ft.replace(R.id.share_fragment_container, editShareFragment, TAG_EDIT_SHARE_FRAGMENT);
  147. ft.addToBackStack(null); // BACK button will recover the previous fragment
  148. ft.commit();
  149. }
  150. @Override
  151. // Call to Unshare operation
  152. public void unshareWith(OCShare share) {
  153. OCFile file = getFile();
  154. getFileOperationsHelper().unshareFileWithUserOrGroup(file, share.getShareType(), share.getShareWith());
  155. }
  156. /**
  157. * Get users and groups from the server to fill in the "share with" list
  158. */
  159. @Override
  160. public void refreshUsersOrGroupsListFromServer() {
  161. // Show loading
  162. showLoadingDialog(getString(R.string.common_loading));
  163. // Get Users and Groups
  164. GetShareWithUsersAsyncTask getTask = new GetShareWithUsersAsyncTask(this);
  165. Object[] params = {getFile(), getAccount(), getStorageManager()};
  166. getTask.execute(params);
  167. }
  168. /**
  169. * Updates the view associated to the activity after the finish of some operation over files
  170. * in the current account.
  171. *
  172. * @param operation Removal operation performed.
  173. * @param result Result of the removal.
  174. */
  175. @Override
  176. public void onRemoteOperationFinish(RemoteOperation operation, RemoteOperationResult result) {
  177. super.onRemoteOperationFinish(operation, result);
  178. if (result.isSuccess() ||
  179. (operation instanceof GetSharesForFileOperation &&
  180. result.getCode() == RemoteOperationResult.ResultCode.SHARE_NOT_FOUND
  181. )
  182. ) {
  183. Log_OC.d(TAG, "Refreshing view on successful operation or finished refresh");
  184. refreshSharesFromStorageManager();
  185. }
  186. if (operation instanceof CreateShareViaLinkOperation) {
  187. onCreateShareViaLinkOperationFinish((CreateShareViaLinkOperation) operation, result);
  188. }
  189. if (operation instanceof UnshareOperation && result.isSuccess() && getEditShareFragment() != null) {
  190. getSupportFragmentManager().popBackStack();
  191. }
  192. if (operation instanceof UpdateSharePermissionsOperation
  193. && getEditShareFragment() != null && getEditShareFragment().isAdded()) {
  194. getEditShareFragment().onUpdateSharePermissionsFinished(result);
  195. }
  196. }
  197. /**
  198. * Updates the view, reading data from {@link com.owncloud.android.datamodel.FileDataStorageManager}.
  199. */
  200. private void refreshSharesFromStorageManager() {
  201. ShareFileFragment shareFileFragment = getShareFileFragment();
  202. if (shareFileFragment != null
  203. && shareFileFragment.isAdded()) { // only if added to the view hierarchy!!
  204. shareFileFragment.refreshCapabilitiesFromDB();
  205. shareFileFragment.refreshUsersOrGroupsListFromDB();
  206. shareFileFragment.refreshPublicShareFromDB();
  207. }
  208. SearchShareesFragment searchShareesFragment = getSearchFragment();
  209. if (searchShareesFragment != null &&
  210. searchShareesFragment.isAdded()) { // only if added to the view hierarchy!!
  211. searchShareesFragment.refreshUsersOrGroupsListFromDB();
  212. }
  213. EditShareFragment editShareFragment = getEditShareFragment();
  214. if (editShareFragment != null &&
  215. editShareFragment.isAdded()) {
  216. editShareFragment.refreshUiFromDB();
  217. }
  218. }
  219. /**
  220. * Shortcut to get access to the {@link ShareFileFragment} instance, if any
  221. *
  222. * @return A {@link ShareFileFragment} instance, or null
  223. */
  224. private ShareFileFragment getShareFileFragment() {
  225. return (ShareFileFragment) getSupportFragmentManager().findFragmentByTag(TAG_SHARE_FRAGMENT);
  226. }
  227. /**
  228. * Shortcut to get access to the {@link SearchShareesFragment} instance, if any
  229. *
  230. * @return A {@link SearchShareesFragment} instance, or null
  231. */
  232. private SearchShareesFragment getSearchFragment() {
  233. return (SearchShareesFragment) getSupportFragmentManager().findFragmentByTag(TAG_SEARCH_FRAGMENT);
  234. }
  235. /**
  236. * Shortcut to get access to the {@link EditShareFragment} instance, if any
  237. *
  238. * @return A {@link EditShareFragment} instance, or null
  239. */
  240. private EditShareFragment getEditShareFragment() {
  241. return (EditShareFragment) getSupportFragmentManager().findFragmentByTag(TAG_EDIT_SHARE_FRAGMENT);
  242. }
  243. private void onCreateShareViaLinkOperationFinish(CreateShareViaLinkOperation operation,
  244. RemoteOperationResult result) {
  245. if (result.isSuccess()) {
  246. updateFileFromDB();
  247. // Create dialog to allow the user choose an app to send the link
  248. Intent intentToShareLink = new Intent(Intent.ACTION_SEND);
  249. // if share to user and share via link multiple ocshares are returned,
  250. // therefore filtering for public_link
  251. String link = "";
  252. for (Object object : result.getData()) {
  253. OCShare shareLink = (OCShare) object;
  254. if (TAG_PUBLIC_LINK.equalsIgnoreCase(shareLink.getShareType().name())) {
  255. link = shareLink.getShareLink();
  256. break;
  257. }
  258. }
  259. intentToShareLink.putExtra(Intent.EXTRA_TEXT, link);
  260. intentToShareLink.setType("text/plain");
  261. String username;
  262. try {
  263. OwnCloudAccount oca = new OwnCloudAccount(getAccount(), this);
  264. if (oca.getDisplayName() != null && !oca.getDisplayName().isEmpty()) {
  265. username = oca.getDisplayName();
  266. } else {
  267. username = AccountUtils.getUsernameForAccount(getAccount());
  268. }
  269. } catch (Exception e) {
  270. username = AccountUtils.getUsernameForAccount(getAccount());
  271. }
  272. if (username != null) {
  273. intentToShareLink.putExtra(
  274. Intent.EXTRA_SUBJECT,
  275. getString(
  276. R.string.subject_user_shared_with_you,
  277. username,
  278. getFile().getFileName()
  279. )
  280. );
  281. } else {
  282. intentToShareLink.putExtra(
  283. Intent.EXTRA_SUBJECT,
  284. getString(
  285. R.string.subject_shared_with_you,
  286. getFile().getFileName()
  287. )
  288. );
  289. }
  290. String[] packagesToExclude = new String[]{getPackageName()};
  291. DialogFragment chooserDialog = ShareLinkToDialog.newInstance(intentToShareLink, packagesToExclude);
  292. chooserDialog.show(getSupportFragmentManager(), FTAG_CHOOSER_DIALOG);
  293. } else {
  294. // Detect Failure (403) --> maybe needs password
  295. String password = operation.getPassword();
  296. if (result.getCode() == RemoteOperationResult.ResultCode.SHARE_FORBIDDEN &&
  297. TextUtils.isEmpty(password) &&
  298. getCapabilities().getFilesSharingPublicEnabled().isUnknown()) {
  299. // Was tried without password, but not sure that it's optional.
  300. // Try with password before giving up; see also ShareFileFragment#OnShareViaLinkListener
  301. ShareFileFragment shareFileFragment = getShareFileFragment();
  302. if (shareFileFragment != null && shareFileFragment.isAdded()) { // only if added to the view hierarchy!!
  303. boolean askForPassword = getCapabilities().getFilesSharingPublicAskForOptionalPassword().isTrue();
  304. shareFileFragment.requestPasswordForShareViaLink(true, askForPassword);
  305. }
  306. } else {
  307. Snackbar.make(
  308. findViewById(android.R.id.content),
  309. ErrorMessageAdapter.getErrorCauseMessage(result, operation, getResources()),
  310. Snackbar.LENGTH_LONG
  311. ).show();
  312. }
  313. }
  314. }
  315. }