ShareFileFragment.java 37 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947
  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. * <p/>
  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. * <p/>
  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. * <p/>
  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.fragment;
  22. import android.accounts.Account;
  23. import android.app.Activity;
  24. import android.content.Intent;
  25. import android.graphics.Bitmap;
  26. import android.net.Uri;
  27. import android.os.Bundle;
  28. import android.support.design.widget.Snackbar;
  29. import android.support.v4.app.Fragment;
  30. import android.support.v7.widget.AppCompatButton;
  31. import android.support.v7.widget.SwitchCompat;
  32. import android.view.LayoutInflater;
  33. import android.view.View;
  34. import android.view.ViewGroup;
  35. import android.widget.Button;
  36. import android.widget.CompoundButton;
  37. import android.widget.ImageView;
  38. import android.widget.LinearLayout;
  39. import android.widget.ListAdapter;
  40. import android.widget.ListView;
  41. import android.widget.ScrollView;
  42. import android.widget.TextView;
  43. import android.widget.Toast;
  44. import com.owncloud.android.R;
  45. import com.owncloud.android.authentication.AccountUtils;
  46. import com.owncloud.android.datamodel.OCFile;
  47. import com.owncloud.android.datamodel.ThumbnailsCacheManager;
  48. import com.owncloud.android.lib.common.utils.Log_OC;
  49. import com.owncloud.android.lib.resources.shares.OCShare;
  50. import com.owncloud.android.lib.resources.shares.ShareType;
  51. import com.owncloud.android.lib.resources.status.OCCapability;
  52. import com.owncloud.android.ui.activity.FileActivity;
  53. import com.owncloud.android.ui.adapter.ShareUserListAdapter;
  54. import com.owncloud.android.ui.dialog.ExpirationDatePickerDialogFragment;
  55. import com.owncloud.android.ui.dialog.SharePasswordDialogFragment;
  56. import com.owncloud.android.utils.DisplayUtils;
  57. import com.owncloud.android.utils.MimeTypeUtil;
  58. import java.text.SimpleDateFormat;
  59. import java.util.ArrayList;
  60. import java.util.Date;
  61. /**
  62. * Fragment for Sharing a file with sharees (users or groups) or creating
  63. * a public link.
  64. * <p/>
  65. * A simple {@link Fragment} subclass.
  66. * <p/>
  67. * Activities that contain this fragment must implement the
  68. * {@link ShareFragmentListener} interface
  69. * to handle interaction events.
  70. * <p/>
  71. * Use the {@link ShareFileFragment#newInstance} factory method to
  72. * create an instance of this fragment.
  73. */
  74. public class ShareFileFragment extends Fragment implements ShareUserListAdapter.ShareUserAdapterListener {
  75. private static final String TAG = ShareFileFragment.class.getSimpleName();
  76. /**
  77. * The fragment initialization parameters.
  78. */
  79. private static final String ARG_FILE = "FILE";
  80. private static final String ARG_ACCOUNT = "ACCOUNT";
  81. // /** Tag for dialog */
  82. // private static final String FTAG_CHOOSER_DIALOG = "CHOOSER_DIALOG";
  83. /**
  84. * File to share, received as a parameter in construction time.
  85. */
  86. private OCFile mFile;
  87. /**
  88. * OC account holding the file to share, received as a parameter in construction time.
  89. */
  90. private Account mAccount;
  91. /**
  92. * Reference to parent listener.
  93. */
  94. private ShareFragmentListener mListener;
  95. /**
  96. * List of private shares bound to the file.
  97. */
  98. private ArrayList<OCShare> mPrivateShares;
  99. /**
  100. * Capabilities of the server.
  101. */
  102. private OCCapability mCapabilities;
  103. /**
  104. * Adapter to show private shares.
  105. */
  106. private ShareUserListAdapter mUserGroupsAdapter = null;
  107. /**
  108. * Public share bound to the file.
  109. */
  110. private OCShare mPublicShare;
  111. /**
  112. * Listener for changes on switch to share / unshare publicly.
  113. */
  114. private CompoundButton.OnCheckedChangeListener mOnShareViaLinkSwitchCheckedChangeListener;
  115. /**
  116. * Listener for user actions to set, update or clear password on public link.
  117. */
  118. private OnPasswordInteractionListener mOnPasswordInteractionListener = null;
  119. /**
  120. * Listener for user actions to set, update or clear expiration date on public link.
  121. */
  122. private OnExpirationDateInteractionListener mOnExpirationDateInteractionListener = null;
  123. /**
  124. * Listener for user actions to set or unset edit permission on public link.
  125. */
  126. private OnEditPermissionInteractionListener mOnEditPermissionInteractionListener = null;
  127. /**
  128. * Listener for user actions to set or unset hide file listing permission on public link.
  129. */
  130. private OnHideFileListingPermissionInteractionListener mOnHideFileListingPermissionInteractionListener = null;
  131. /**
  132. * Public factory method to create new ShareFileFragment instances.
  133. *
  134. * @param fileToShare An {@link OCFile} to show in the fragment
  135. * @param account An ownCloud account
  136. * @return A new instance of fragment ShareFileFragment.
  137. */
  138. public static ShareFileFragment newInstance(OCFile fileToShare, Account account) {
  139. ShareFileFragment fragment = new ShareFileFragment();
  140. Bundle args = new Bundle();
  141. args.putParcelable(ARG_FILE, fileToShare);
  142. args.putParcelable(ARG_ACCOUNT, account);
  143. fragment.setArguments(args);
  144. return fragment;
  145. }
  146. @Override
  147. public void onCreate(Bundle savedInstanceState) {
  148. super.onCreate(savedInstanceState);
  149. Log_OC.d(TAG, "onCreate");
  150. if (getArguments() != null) {
  151. mFile = getArguments().getParcelable(ARG_FILE);
  152. mAccount = getArguments().getParcelable(ARG_ACCOUNT);
  153. }
  154. }
  155. @Override
  156. public View onCreateView(LayoutInflater inflater, ViewGroup container,
  157. Bundle savedInstanceState) {
  158. Log_OC.d(TAG, "onCreateView");
  159. // Inflate the layout for this fragment
  160. View view = inflater.inflate(R.layout.share_file_layout, container, false);
  161. // Setup layout
  162. // Image
  163. ImageView icon = (ImageView) view.findViewById(R.id.shareFileIcon);
  164. icon.setImageResource(MimeTypeUtil.getFileTypeIconId(mFile.getMimetype(),
  165. mFile.getFileName()));
  166. if (MimeTypeUtil.isImage(mFile)) {
  167. String remoteId = String.valueOf(mFile.getRemoteId());
  168. Bitmap thumbnail = ThumbnailsCacheManager.getBitmapFromDiskCache(remoteId);
  169. if (thumbnail != null) {
  170. icon.setImageBitmap(thumbnail);
  171. }
  172. }
  173. // Name
  174. TextView fileNameHeader = (TextView) view.findViewById(R.id.shareFileName);
  175. fileNameHeader.setText(getResources().getString(R.string.share_file, mFile.getFileName()));
  176. // Size
  177. TextView size = (TextView) view.findViewById(R.id.shareFileSize);
  178. if (mFile.isFolder()) {
  179. size.setVisibility(View.GONE);
  180. } else {
  181. size.setText(DisplayUtils.bytesToHumanReadable(mFile.getFileLength()));
  182. }
  183. // Add User Button
  184. Button addUserGroupButton = (Button)
  185. view.findViewById(R.id.addUserButton);
  186. addUserGroupButton.setOnClickListener(new View.OnClickListener() {
  187. @Override
  188. public void onClick(View view) {
  189. boolean shareWithUsersEnable = AccountUtils.hasSearchUsersSupport(mAccount);
  190. if (shareWithUsersEnable) {
  191. // Show Search Fragment
  192. mListener.showSearchUsersAndGroups();
  193. } else {
  194. String message = getString(R.string.share_sharee_unavailable);
  195. Toast.makeText(getActivity(), message, Toast.LENGTH_LONG).show();
  196. }
  197. }
  198. });
  199. // Set listener for user actions on switch for sharing/unsharing via link
  200. initShareViaLinkListener(view);
  201. // Set listener for user actions on expiration date
  202. initExpirationListener(view);
  203. // Set listener for user actions on password
  204. initPasswordListener(view);
  205. // Set listener for user actions on edit permission
  206. initEditPermissionListener(view);
  207. // Set listener for hide file listing
  208. initHideFileListingListener(view);
  209. // Hide share features sections that are not enabled
  210. hideNotEnabledShareSections(view);
  211. return view;
  212. }
  213. /**
  214. * Binds listener for user actions to create or delete a public share
  215. * to the views receiving the user events.
  216. *
  217. * @param shareView Root view in the fragment.
  218. */
  219. private void initShareViaLinkListener(View shareView) {
  220. mOnShareViaLinkSwitchCheckedChangeListener = new OnShareViaLinkListener();
  221. SwitchCompat shareViaLinkSwitch = (SwitchCompat) shareView.findViewById(R.id.shareViaLinkSectionSwitch);
  222. shareViaLinkSwitch.setOnCheckedChangeListener(mOnShareViaLinkSwitchCheckedChangeListener);
  223. }
  224. /**
  225. * Listener for user actions that create or delete a public share.
  226. */
  227. private class OnShareViaLinkListener
  228. implements CompoundButton.OnCheckedChangeListener {
  229. /**
  230. * Called by R.id.shareViaLinkSectionSwitch to create or delete a public link.
  231. *
  232. * @param switchView {@link SwitchCompat} toggled by the user, R.id.shareViaLinkSectionSwitch
  233. * @param isChecked New switch state.
  234. */
  235. @Override
  236. public void onCheckedChanged(CompoundButton switchView, boolean isChecked) {
  237. if (!isResumed()) {
  238. // very important, setCheched(...) is called automatically during
  239. // Fragment recreation on device rotations
  240. return;
  241. }
  242. if (isChecked) {
  243. if (mCapabilities != null &&
  244. mCapabilities.getFilesSharingPublicPasswordEnforced().isTrue()) {
  245. // password enforced by server, request to the user before trying to create
  246. requestPasswordForShareViaLink(true);
  247. } else {
  248. // create without password if not enforced by server or we don't know if enforced;
  249. ((FileActivity) getActivity()).getFileOperationsHelper().
  250. shareFileViaLink(mFile, null);
  251. // ShareActivity#onCreateShareViaLinkOperationFinish will take care if password
  252. // is enforced by the server but app doesn't know, or if server version is
  253. // older than OwnCloudVersion#MINIMUM_VERSION_CAPABILITIES_API
  254. }
  255. } else {
  256. ((FileActivity) getActivity()).getFileOperationsHelper().
  257. unshareFileViaLink(mFile);
  258. }
  259. // undo the toggle to grant the view will be correct if any intermediate dialog is cancelled or
  260. // the create/delete operation fails
  261. switchView.setOnCheckedChangeListener(null);
  262. switchView.toggle();
  263. switchView.setOnCheckedChangeListener(mOnShareViaLinkSwitchCheckedChangeListener);
  264. }
  265. }
  266. /**
  267. * Binds listener for user actions that start any update on a expiration date
  268. * for the public link to the views receiving the user events.
  269. *
  270. * @param shareView Root view in the fragment.
  271. */
  272. private void initExpirationListener(View shareView) {
  273. mOnExpirationDateInteractionListener = new OnExpirationDateInteractionListener();
  274. ((SwitchCompat) shareView.findViewById(R.id.shareViaLinkExpirationSwitch)).
  275. setOnCheckedChangeListener(mOnExpirationDateInteractionListener);
  276. shareView.findViewById(R.id.shareViaLinkExpirationLabel).
  277. setOnClickListener(mOnExpirationDateInteractionListener);
  278. shareView.findViewById(R.id.shareViaLinkExpirationValue).
  279. setOnClickListener(mOnExpirationDateInteractionListener);
  280. }
  281. /**
  282. * Listener for user actions that start any update on the expiration date for the public link.
  283. */
  284. private class OnExpirationDateInteractionListener
  285. implements CompoundButton.OnCheckedChangeListener, View.OnClickListener {
  286. /**
  287. * Called by R.id.shareViaLinkExpirationSwitch to set or clear the expiration date.
  288. *
  289. * @param switchView {@link SwitchCompat} toggled by the user, R.id.shareViaLinkExpirationSwitch
  290. * @param isChecked New switch state.
  291. */
  292. @Override
  293. public void onCheckedChanged(CompoundButton switchView, boolean isChecked) {
  294. if (!isResumed()) {
  295. // very important, setCheched(...) is called automatically during
  296. // Fragment recreation on device rotations
  297. return;
  298. }
  299. if (isChecked) {
  300. ExpirationDatePickerDialogFragment dialog =
  301. ExpirationDatePickerDialogFragment.newInstance(mFile, -1);
  302. dialog.show(
  303. getActivity().getSupportFragmentManager(),
  304. ExpirationDatePickerDialogFragment.DATE_PICKER_DIALOG
  305. );
  306. } else {
  307. ((FileActivity) getActivity()).getFileOperationsHelper().setExpirationDateToShareViaLink(mFile, -1);
  308. }
  309. // undo the toggle to grant the view will be correct if the dialog is cancelled
  310. switchView.setOnCheckedChangeListener(null);
  311. switchView.toggle();
  312. switchView.setOnCheckedChangeListener(mOnExpirationDateInteractionListener);
  313. }
  314. /**
  315. * Called by R.id.shareViaLinkExpirationLabel or R.id.shareViaLinkExpirationValue to change the current
  316. * expiration date.
  317. *
  318. * @param expirationView Label or value view touched by the user.
  319. */
  320. @Override
  321. public void onClick(View expirationView) {
  322. if (mPublicShare != null && mPublicShare.getExpirationDate() > 0) {
  323. long chosenDateInMillis = -1;
  324. if (mPublicShare != null) {
  325. chosenDateInMillis = mPublicShare.getExpirationDate();
  326. }
  327. ExpirationDatePickerDialogFragment dialog =
  328. ExpirationDatePickerDialogFragment.newInstance(mFile, chosenDateInMillis);
  329. dialog.show(
  330. getActivity().getSupportFragmentManager(),
  331. ExpirationDatePickerDialogFragment.DATE_PICKER_DIALOG
  332. );
  333. }
  334. }
  335. }
  336. /**
  337. * Binds listener for user actions that start any update on a password for the public link to the views receiving
  338. * the user events.
  339. *
  340. * @param shareView Root view in the fragment.
  341. */
  342. private void initPasswordListener(View shareView) {
  343. mOnPasswordInteractionListener = new OnPasswordInteractionListener();
  344. ((SwitchCompat) shareView.findViewById(R.id.shareViaLinkPasswordSwitch)).
  345. setOnCheckedChangeListener(mOnPasswordInteractionListener);
  346. shareView.findViewById(R.id.shareViaLinkPasswordLabel).setOnClickListener(mOnPasswordInteractionListener);
  347. shareView.findViewById(R.id.shareViaLinkPasswordValue).setOnClickListener(mOnPasswordInteractionListener);
  348. }
  349. /**
  350. * Listener for user actions that start any update on a password for the public link.
  351. */
  352. private class OnPasswordInteractionListener
  353. implements CompoundButton.OnCheckedChangeListener, View.OnClickListener {
  354. /**
  355. * Called by R.id.shareViaLinkPasswordSwitch to set or clear the password.
  356. *
  357. * @param switchView {@link SwitchCompat} toggled by the user, R.id.shareViaLinkPasswordSwitch
  358. * @param isChecked New switch state.
  359. */
  360. @Override
  361. public void onCheckedChanged(CompoundButton switchView, boolean isChecked) {
  362. if (!isResumed()) {
  363. // very important, setCheched(...) is called automatically during
  364. // Fragment recreation on device rotations
  365. return;
  366. }
  367. if (isChecked) {
  368. requestPasswordForShareViaLink(false);
  369. } else {
  370. ((FileActivity) getActivity()).getFileOperationsHelper().
  371. setPasswordToShareViaLink(mFile, ""); // "" clears
  372. }
  373. // undo the toggle to grant the view will be correct if the dialog is cancelled
  374. switchView.setOnCheckedChangeListener(null);
  375. switchView.toggle();
  376. switchView.setOnCheckedChangeListener(mOnPasswordInteractionListener);
  377. }
  378. /**
  379. * Called by R.id.shareViaLinkPasswordLabel or R.id.shareViaLinkPasswordValue
  380. * to change the current password.
  381. *
  382. * @param passwordView Label or value view touched by the user.
  383. */
  384. @Override
  385. public void onClick(View passwordView) {
  386. if (mPublicShare != null && mPublicShare.isPasswordProtected()) {
  387. requestPasswordForShareViaLink(false);
  388. }
  389. }
  390. }
  391. /**
  392. * Binds listener for user actions that start any update the edit permissions
  393. * for the public link to the views receiving the user events.
  394. *
  395. * @param shareView Root view in the fragment.
  396. */
  397. private void initEditPermissionListener(View shareView) {
  398. mOnEditPermissionInteractionListener = new OnEditPermissionInteractionListener();
  399. ((SwitchCompat) shareView.findViewById(R.id.shareViaLinkEditPermissionSwitch)).
  400. setOnCheckedChangeListener(mOnEditPermissionInteractionListener);
  401. }
  402. /**
  403. * Binds listener for user actions that start any update the hide file listing permissions
  404. * for the public link to the views receiving the user events.
  405. *
  406. * @param shareView Root view in the fragment.
  407. */
  408. private void initHideFileListingListener(View shareView) {
  409. mOnHideFileListingPermissionInteractionListener = new OnHideFileListingPermissionInteractionListener();
  410. ((SwitchCompat) shareView.findViewById(R.id.shareViaLinkFileListingPermissionSwitch)).
  411. setOnCheckedChangeListener(mOnHideFileListingPermissionInteractionListener);
  412. }
  413. /**
  414. * Listener for user actions that start any update on the edit permissions for the public link.
  415. */
  416. private class OnEditPermissionInteractionListener implements CompoundButton.OnCheckedChangeListener {
  417. /**
  418. * Called by R.id.shareViaLinkEditPermissionSwitch to set or clear the edit permission.
  419. *
  420. * @param switchView {@link SwitchCompat} toggled by the user, R.id.shareViaLinkEditPermissionSwitch
  421. * @param isChecked New switch state.
  422. */
  423. @Override
  424. public void onCheckedChanged(CompoundButton switchView, boolean isChecked) {
  425. if (!isResumed()) {
  426. // very important, setCheched(...) is called automatically during
  427. // Fragment recreation on device rotations
  428. return;
  429. }
  430. ((FileActivity) getActivity()).getFileOperationsHelper().setUploadPermissionsToShare(mFile, isChecked);
  431. // undo the toggle to grant the view will be correct if the dialog is cancelled
  432. switchView.setOnCheckedChangeListener(null);
  433. switchView.toggle();
  434. switchView.setOnCheckedChangeListener(mOnEditPermissionInteractionListener);
  435. }
  436. }
  437. /**
  438. * Listener for user actions that start any update on the hide file listing permissions for the public link.
  439. */
  440. private class OnHideFileListingPermissionInteractionListener implements CompoundButton.OnCheckedChangeListener {
  441. /**
  442. * Called by R.id.shareViaLinkHideListPermissionSwitch to set or clear the edit permission.
  443. *
  444. * @param switchView {@link SwitchCompat} toggled by the user, R.id.shareViaLinkHideListPermissionSwitch
  445. * @param isChecked New switch state.
  446. */
  447. @Override
  448. public void onCheckedChanged(CompoundButton switchView, boolean isChecked) {
  449. if (!isResumed()) {
  450. // very important, setChecked(...) is called automatically during
  451. // Fragment recreation on device rotations
  452. return;
  453. }
  454. if (mCapabilities.getFilesFileDrop().isTrue()) {
  455. ((FileActivity) getActivity()).getFileOperationsHelper().
  456. setHideFileListingPermissionsToShare(
  457. mPublicShare,
  458. isChecked
  459. );
  460. } else {
  461. // not supported in ownCloud
  462. Snackbar.make(getView(), R.string.files_drop_not_supported, Snackbar.LENGTH_LONG)
  463. .setAction(R.string.learn_more, new View.OnClickListener(){
  464. @Override
  465. public void onClick(View v) {
  466. Intent i = new Intent(Intent.ACTION_VIEW);
  467. i.setData(Uri.parse(getString(R.string.url_server_install)));
  468. startActivity(i);
  469. }
  470. })
  471. .show();
  472. }
  473. // undo the toggle to grant the view will be correct if the dialog is cancelled
  474. switchView.setOnCheckedChangeListener(null);
  475. switchView.toggle();
  476. switchView.setOnCheckedChangeListener(mOnHideFileListingPermissionInteractionListener);
  477. }
  478. }
  479. @Override
  480. public void onActivityCreated(Bundle savedInstanceState) {
  481. super.onActivityCreated(savedInstanceState);
  482. Log_OC.d(TAG, "onActivityCreated");
  483. // Load known capabilities of the server from DB
  484. refreshCapabilitiesFromDB();
  485. // Load data into the list of private shares
  486. refreshUsersOrGroupsListFromDB();
  487. // Load data of public share, if exists
  488. refreshPublicShareFromDB();
  489. }
  490. @Override
  491. public void onAttach(Activity activity) {
  492. super.onAttach(activity);
  493. try {
  494. mListener = (ShareFragmentListener) activity;
  495. } catch (ClassCastException e) {
  496. throw new ClassCastException(activity.toString() + " must implement OnShareFragmentInteractionListener");
  497. }
  498. }
  499. @Override
  500. public void onDetach() {
  501. super.onDetach();
  502. mListener = null;
  503. }
  504. /**
  505. * Get known server capabilities from DB
  506. * <p/>
  507. * Depends on the parent Activity provides a {@link com.owncloud.android.datamodel.FileDataStorageManager}
  508. * instance ready to use. If not ready, does nothing.
  509. */
  510. public void refreshCapabilitiesFromDB() {
  511. if (((FileActivity) mListener).getStorageManager() != null) {
  512. mCapabilities = ((FileActivity) mListener).getStorageManager().getCapability(mAccount.name);
  513. }
  514. }
  515. /**
  516. * Get users and groups from the DB to fill in the "share with" list.
  517. * <p/>
  518. * Depends on the parent Activity provides a {@link com.owncloud.android.datamodel.FileDataStorageManager}
  519. * instance ready to use. If not ready, does nothing.
  520. */
  521. public void refreshUsersOrGroupsListFromDB() {
  522. if (((FileActivity) mListener).getStorageManager() != null) {
  523. // Get Users and Groups
  524. mPrivateShares = ((FileActivity) mListener).getStorageManager().getSharesWithForAFile(
  525. mFile.getRemotePath(),
  526. mAccount.name
  527. );
  528. // Update list of users/groups
  529. updateListOfUserGroups();
  530. }
  531. }
  532. private void updateListOfUserGroups() {
  533. // Update list of users/groups
  534. // TODO Refactoring: create a new {@link ShareUserListAdapter} instance with every call should not be needed
  535. mUserGroupsAdapter = new ShareUserListAdapter(
  536. getActivity(),
  537. R.layout.share_user_item,
  538. mPrivateShares,
  539. this
  540. );
  541. // Show data
  542. TextView noShares = (TextView) getView().findViewById(R.id.shareNoUsers);
  543. ListView usersList = (ListView) getView().findViewById(R.id.shareUsersList);
  544. if (mPrivateShares.size() > 0) {
  545. noShares.setVisibility(View.GONE);
  546. usersList.setVisibility(View.VISIBLE);
  547. usersList.setAdapter(mUserGroupsAdapter);
  548. setListViewHeightBasedOnChildren(usersList);
  549. } else {
  550. noShares.setVisibility(View.VISIBLE);
  551. usersList.setVisibility(View.GONE);
  552. }
  553. // Set Scroll to initial position
  554. ScrollView scrollView = (ScrollView) getView().findViewById(R.id.shareScroll);
  555. scrollView.scrollTo(0, 0);
  556. }
  557. @Override
  558. public void unshareButtonPressed(OCShare share) {
  559. // Unshare
  560. Log_OC.d(TAG, "Unsharing " + share.getSharedWithDisplayName());
  561. mListener.unshareWith(share);
  562. }
  563. @Override
  564. public void editShare(OCShare share) {
  565. // move to fragment to edit share
  566. Log_OC.d(TAG, "Editing " + share.getSharedWithDisplayName());
  567. mListener.showEditShare(share);
  568. }
  569. /**
  570. * Get public link from the DB to fill in the "Share link" section in the UI.
  571. * <p/>
  572. * Takes into account server capabilities before reading database.
  573. * <p/>
  574. * Depends on the parent Activity provides a {@link com.owncloud.android.datamodel.FileDataStorageManager}
  575. * instance ready to use. If not ready, does nothing.
  576. */
  577. public void refreshPublicShareFromDB() {
  578. if (isPublicShareDisabled()) {
  579. hidePublicShare();
  580. } else if (((FileActivity) mListener).getStorageManager() != null) {
  581. // Get public share
  582. mPublicShare = ((FileActivity) mListener).getStorageManager().getFirstShareByPathAndType(
  583. mFile.getRemotePath(),
  584. ShareType.PUBLIC_LINK,
  585. ""
  586. );
  587. // Update public share section
  588. updatePublicShareSection();
  589. }
  590. }
  591. /**
  592. * @return 'True' when public share is disabled in the server
  593. */
  594. private boolean isPublicShareDisabled() {
  595. return (mCapabilities != null &&
  596. mCapabilities.getFilesSharingPublicEnabled().isFalse()
  597. );
  598. }
  599. /**
  600. * Updates in the UI the section about public share with the information in the current public share bound to
  601. * mFile, if any.
  602. */
  603. private void updatePublicShareSection() {
  604. if (mPublicShare != null && ShareType.PUBLIC_LINK.equals(mPublicShare.getShareType())) {
  605. /// public share bound -> expand section
  606. SwitchCompat shareViaLinkSwitch = getShareViaLinkSwitch();
  607. if (!shareViaLinkSwitch.isChecked()) {
  608. // set null listener before setChecked() to prevent infinite loop of calls
  609. shareViaLinkSwitch.setOnCheckedChangeListener(null);
  610. shareViaLinkSwitch.setChecked(true);
  611. shareViaLinkSwitch.setOnCheckedChangeListener(
  612. mOnShareViaLinkSwitchCheckedChangeListener
  613. );
  614. }
  615. getExpirationDateSection().setVisibility(View.VISIBLE);
  616. getPasswordSection().setVisibility(View.VISIBLE);
  617. if (mFile.isFolder() && !mCapabilities.getFilesSharingPublicUpload().isFalse()) {
  618. getEditPermissionSection().setVisibility(View.VISIBLE);
  619. getHideFileListingPermissionSection().setVisibility(View.VISIBLE);
  620. } else {
  621. getEditPermissionSection().setVisibility(View.GONE);
  622. }
  623. // GetLink button
  624. AppCompatButton getLinkButton = getGetLinkButton();
  625. getLinkButton.setVisibility(View.VISIBLE);
  626. getLinkButton.setOnClickListener(new View.OnClickListener() {
  627. @Override
  628. public void onClick(View v) {
  629. //GetLink from the server and show ShareLinkToDialog
  630. ((FileActivity) getActivity()).getFileOperationsHelper().
  631. getFileWithLink(mFile);
  632. }
  633. });
  634. /// update state of expiration date switch and message depending on expiration date
  635. SwitchCompat expirationDateSwitch = getExpirationDateSwitch();
  636. // set null listener before setChecked() to prevent infinite loop of calls
  637. expirationDateSwitch.setOnCheckedChangeListener(null);
  638. long expirationDate = mPublicShare.getExpirationDate();
  639. if (expirationDate > 0) {
  640. if (!expirationDateSwitch.isChecked()) {
  641. expirationDateSwitch.toggle();
  642. }
  643. String formattedDate =
  644. SimpleDateFormat.getDateInstance().format(
  645. new Date(expirationDate)
  646. );
  647. getExpirationDateValue().setText(formattedDate);
  648. } else {
  649. if (expirationDateSwitch.isChecked()) {
  650. expirationDateSwitch.toggle();
  651. }
  652. getExpirationDateValue().setText(R.string.empty);
  653. }
  654. // recover listener
  655. expirationDateSwitch.setOnCheckedChangeListener(mOnExpirationDateInteractionListener);
  656. /// update state of password switch and message depending on password protection
  657. SwitchCompat passwordSwitch = getPasswordSwitch();
  658. // set null listener before setChecked() to prevent infinite loop of calls
  659. passwordSwitch.setOnCheckedChangeListener(null);
  660. if (mPublicShare.isPasswordProtected()) {
  661. if (!passwordSwitch.isChecked()) {
  662. passwordSwitch.toggle();
  663. }
  664. getPasswordValue().setVisibility(View.VISIBLE);
  665. } else {
  666. if (passwordSwitch.isChecked()) {
  667. passwordSwitch.toggle();
  668. }
  669. getPasswordValue().setVisibility(View.INVISIBLE);
  670. }
  671. // recover listener
  672. passwordSwitch.setOnCheckedChangeListener(mOnPasswordInteractionListener);
  673. /// update state of the edit permission switch
  674. SwitchCompat editPermissionSwitch = getEditPermissionSwitch();
  675. // set null listener before setChecked() to prevent infinite loop of calls
  676. editPermissionSwitch.setOnCheckedChangeListener(null);
  677. if (mPublicShare.getPermissions() > OCShare.READ_PERMISSION_FLAG) {
  678. if (!editPermissionSwitch.isChecked()) {
  679. editPermissionSwitch.toggle();
  680. }
  681. getHideFileListingPermissionSection().setVisibility(View.VISIBLE);
  682. } else {
  683. if (editPermissionSwitch.isChecked()) {
  684. editPermissionSwitch.toggle();
  685. }
  686. getHideFileListingPermissionSection().setVisibility(View.GONE);
  687. }
  688. // recover listener
  689. editPermissionSwitch.setOnCheckedChangeListener(mOnEditPermissionInteractionListener);
  690. /// update state of the hide file listing permission switch
  691. SwitchCompat hideFileListingPermissionSwitch = getHideFileListingPermissionSwitch();
  692. // set null listener before setChecked() to prevent infinite loop of calls
  693. hideFileListingPermissionSwitch.setOnCheckedChangeListener(null);
  694. boolean readOnly = (mPublicShare.getPermissions() & OCShare.READ_PERMISSION_FLAG) != 0;
  695. hideFileListingPermissionSwitch.setChecked(!readOnly);
  696. // recover listener
  697. hideFileListingPermissionSwitch.setOnCheckedChangeListener(
  698. mOnHideFileListingPermissionInteractionListener
  699. );
  700. } else {
  701. /// no public share -> collapse section
  702. SwitchCompat shareViaLinkSwitch = getShareViaLinkSwitch();
  703. if (shareViaLinkSwitch.isChecked()) {
  704. shareViaLinkSwitch.setOnCheckedChangeListener(null);
  705. getShareViaLinkSwitch().setChecked(false);
  706. shareViaLinkSwitch.setOnCheckedChangeListener(
  707. mOnShareViaLinkSwitchCheckedChangeListener
  708. );
  709. }
  710. getExpirationDateSection().setVisibility(View.GONE);
  711. getPasswordSection().setVisibility(View.GONE);
  712. getEditPermissionSection().setVisibility(View.GONE);
  713. getHideFileListingPermissionSection().setVisibility(View.GONE);
  714. getGetLinkButton().setVisibility(View.GONE);
  715. }
  716. }
  717. /// BEWARE: next methods will failed with NullPointerException if called before onCreateView() finishes
  718. private SwitchCompat getShareViaLinkSwitch() {
  719. return (SwitchCompat) getView().findViewById(R.id.shareViaLinkSectionSwitch);
  720. }
  721. private View getExpirationDateSection() {
  722. return getView().findViewById(R.id.shareViaLinkExpirationSection);
  723. }
  724. private SwitchCompat getExpirationDateSwitch() {
  725. return (SwitchCompat) getView().findViewById(R.id.shareViaLinkExpirationSwitch);
  726. }
  727. private TextView getExpirationDateValue() {
  728. return (TextView) getView().findViewById(R.id.shareViaLinkExpirationValue);
  729. }
  730. private View getPasswordSection() {
  731. return getView().findViewById(R.id.shareViaLinkPasswordSection);
  732. }
  733. private SwitchCompat getPasswordSwitch() {
  734. return (SwitchCompat) getView().findViewById(R.id.shareViaLinkPasswordSwitch);
  735. }
  736. private TextView getPasswordValue() {
  737. return (TextView) getView().findViewById(R.id.shareViaLinkPasswordValue);
  738. }
  739. private View getEditPermissionSection() {
  740. return getView().findViewById(R.id.shareViaLinkEditPermissionSection);
  741. }
  742. private SwitchCompat getEditPermissionSwitch() {
  743. return (SwitchCompat) getView().findViewById(R.id.shareViaLinkEditPermissionSwitch);
  744. }
  745. private SwitchCompat getHideFileListingPermissionSwitch() {
  746. return (SwitchCompat) getView().findViewById(R.id.shareViaLinkFileListingPermissionSwitch);
  747. }
  748. private View getHideFileListingPermissionSection() {
  749. return getView().findViewById(R.id.shareViaLinkHideFileListingPermissionSection);
  750. }
  751. private AppCompatButton getGetLinkButton() {
  752. return (AppCompatButton) getView().findViewById(R.id.shareViaLinkGetLinkButton);
  753. }
  754. /**
  755. * Hides all the UI elements related to public share
  756. */
  757. private void hidePublicShare() {
  758. getShareViaLinkSwitch().setVisibility(View.GONE);
  759. getExpirationDateSection().setVisibility(View.GONE);
  760. getPasswordSection().setVisibility(View.GONE);
  761. getEditPermissionSection().setVisibility(View.GONE);
  762. getGetLinkButton().setVisibility(View.GONE);
  763. getHideFileListingPermissionSection().setVisibility(View.GONE);
  764. }
  765. public static void setListViewHeightBasedOnChildren(ListView listView) {
  766. ListAdapter listAdapter = listView.getAdapter();
  767. if (listAdapter == null) {
  768. return;
  769. }
  770. int desiredWidth = View.MeasureSpec.makeMeasureSpec(listView.getWidth(), View.MeasureSpec.AT_MOST);
  771. int totalHeight = 0;
  772. View view = null;
  773. for (int i = 0; i < listAdapter.getCount(); i++) {
  774. view = listAdapter.getView(i, view, listView);
  775. if (i == 0) {
  776. view.setLayoutParams(new ViewGroup.LayoutParams(desiredWidth, ViewGroup.LayoutParams.WRAP_CONTENT));
  777. }
  778. view.measure(desiredWidth, View.MeasureSpec.UNSPECIFIED);
  779. totalHeight += view.getMeasuredHeight();
  780. }
  781. ViewGroup.LayoutParams params = listView.getLayoutParams();
  782. params.height = totalHeight + (listView.getDividerHeight() * (listAdapter.getCount() - 1));
  783. listView.setLayoutParams(params);
  784. listView.requestLayout();
  785. }
  786. /**
  787. * Starts a dialog that requests a password to the user to protect a share link.
  788. *
  789. * @param createShare When 'true', the request for password will be followed by the creation of a new
  790. * public link; when 'false', a public share is assumed to exist, and the password
  791. * is bound to it.
  792. */
  793. public void requestPasswordForShareViaLink(boolean createShare) {
  794. SharePasswordDialogFragment dialog = SharePasswordDialogFragment.newInstance(mFile, createShare);
  795. dialog.show(getFragmentManager(), SharePasswordDialogFragment.PASSWORD_FRAGMENT);
  796. }
  797. /**
  798. * Hide share features sections that are not enabled
  799. *
  800. * @param view share file view
  801. */
  802. private void hideNotEnabledShareSections(View view) {
  803. LinearLayout shareWithUsersSection = (LinearLayout) view.findViewById(R.id.shareWithUsersSection);
  804. LinearLayout shareViaLinkSection = (LinearLayout) view.findViewById(R.id.shareViaLinkSection);
  805. boolean shareViaLinkAllowed = getActivity().getResources().getBoolean(R.bool.share_via_link_feature);
  806. boolean shareWithUsersAllowed = getActivity().getResources().getBoolean(R.bool.share_with_users_feature);
  807. // Hide share via link section if it is not enabled
  808. if (!shareViaLinkAllowed) {
  809. shareViaLinkSection.setVisibility(View.GONE);
  810. }
  811. // Hide share with users section if it is not enabled
  812. if (!shareWithUsersAllowed) {
  813. shareWithUsersSection.setVisibility(View.GONE);
  814. }
  815. }
  816. }