ShareFileFragment.java 39 KB

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