ShareFileFragment.java 38 KB

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