OCFile.java 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769
  1. /*
  2. * ownCloud Android client application
  3. *
  4. * @author Bartek Przybylski
  5. * @author David A. Velasco
  6. * Copyright (C) 2012 Bartek Przybylski
  7. * Copyright (C) 2016 ownCloud Inc.
  8. *
  9. * This program is free software: you can redistribute it and/or modify
  10. * it under the terms of the GNU General Public License version 2,
  11. * as published by the Free Software Foundation.
  12. *
  13. * This program is distributed in the hope that it will be useful,
  14. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  16. * GNU General Public License for more details.
  17. *
  18. * You should have received a copy of the GNU General Public License
  19. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  20. *
  21. */
  22. package com.owncloud.android.datamodel;
  23. import android.content.ContentResolver;
  24. import android.content.Context;
  25. import android.net.Uri;
  26. import android.os.Parcel;
  27. import android.os.Parcelable;
  28. import android.support.v4.content.FileProvider;
  29. import com.owncloud.android.R;
  30. import com.owncloud.android.lib.common.network.WebdavUtils;
  31. import com.owncloud.android.lib.common.utils.Log_OC;
  32. import com.owncloud.android.utils.MimeType;
  33. import java.io.File;
  34. import java.util.Locale;
  35. import third_parties.daveKoeller.AlphanumComparator;
  36. public class OCFile implements Parcelable, Comparable<OCFile> {
  37. public static final Parcelable.Creator<OCFile> CREATOR = new Parcelable.Creator<OCFile>() {
  38. @Override
  39. public OCFile createFromParcel(Parcel source) {
  40. return new OCFile(source);
  41. }
  42. @Override
  43. public OCFile[] newArray(int size) {
  44. return new OCFile[size];
  45. }
  46. };
  47. private final static String PERMISSION_SHARED_WITH_ME = "S"; // TODO move to better location
  48. private final static String PERMISSION_CAN_RESHARE = "R";
  49. public static final String PATH_SEPARATOR = "/";
  50. public static final String ROOT_PATH = PATH_SEPARATOR;
  51. private static final String TAG = OCFile.class.getSimpleName();
  52. private long mId;
  53. private long mParentId;
  54. private long mLength;
  55. private long mCreationTimestamp;
  56. private long mModifiedTimestamp;
  57. private long mModifiedTimestampAtLastSyncForData;
  58. private String mRemotePath;
  59. private String mLocalPath;
  60. private String mMimeType;
  61. private boolean mNeedsUpdating;
  62. private long mLastSyncDateForProperties;
  63. private long mLastSyncDateForData;
  64. private boolean mAvailableOffline;
  65. private String mEtag;
  66. private boolean mShareByLink;
  67. private String mPublicLink;
  68. private String mPermissions;
  69. private String mRemoteId;
  70. private boolean mNeedsUpdateThumbnail;
  71. private boolean mIsDownloading;
  72. private String mEtagInConflict; // Save file etag in the server, when there is a conflict. No conflict = null
  73. private boolean mShareWithSharee;
  74. private boolean mIsFavorite;
  75. private boolean mIsEncrypted;
  76. /**
  77. * URI to the local path of the file contents, if stored in the device; cached after first call
  78. * to {@link #getStorageUri()}
  79. */
  80. private Uri mLocalUri;
  81. /**
  82. * Exportable URI to the local path of the file contents, if stored in the device.
  83. * <p>
  84. * Cached after first call, until changed.
  85. */
  86. private Uri mExposedFileUri;
  87. private String mEncryptedFileName;
  88. /**
  89. * Create new {@link OCFile} with given path.
  90. * <p>
  91. * The path received must be URL-decoded. Path separator must be OCFile.PATH_SEPARATOR, and it must be the first character in 'path'.
  92. *
  93. * @param path The remote path of the file.
  94. */
  95. public OCFile(String path) {
  96. resetData();
  97. mNeedsUpdating = false;
  98. if (path == null || path.length() <= 0 || !path.startsWith(PATH_SEPARATOR)) {
  99. throw new IllegalArgumentException("Trying to create a OCFile with a non valid remote path: " + path);
  100. }
  101. mRemotePath = path;
  102. }
  103. /**
  104. * Reconstruct from parcel
  105. *
  106. * @param source The source parcel
  107. */
  108. private OCFile(Parcel source) {
  109. mId = source.readLong();
  110. mParentId = source.readLong();
  111. mLength = source.readLong();
  112. mCreationTimestamp = source.readLong();
  113. mModifiedTimestamp = source.readLong();
  114. mModifiedTimestampAtLastSyncForData = source.readLong();
  115. mRemotePath = source.readString();
  116. mLocalPath = source.readString();
  117. mMimeType = source.readString();
  118. mNeedsUpdating = source.readInt() == 0;
  119. mAvailableOffline = source.readInt() == 1;
  120. mLastSyncDateForProperties = source.readLong();
  121. mLastSyncDateForData = source.readLong();
  122. mEtag = source.readString();
  123. mShareByLink = source.readInt() == 1;
  124. mPublicLink = source.readString();
  125. mPermissions = source.readString();
  126. mRemoteId = source.readString();
  127. mNeedsUpdateThumbnail = source.readInt() == 1;
  128. mIsDownloading = source.readInt() == 1;
  129. mEtagInConflict = source.readString();
  130. mShareWithSharee = source.readInt() == 1;
  131. mIsFavorite = source.readInt() == 1;
  132. mIsEncrypted = source.readInt() == 1;
  133. mEncryptedFileName = source.readString();
  134. }
  135. @Override
  136. public void writeToParcel(Parcel dest, int flags) {
  137. dest.writeLong(mId);
  138. dest.writeLong(mParentId);
  139. dest.writeLong(mLength);
  140. dest.writeLong(mCreationTimestamp);
  141. dest.writeLong(mModifiedTimestamp);
  142. dest.writeLong(mModifiedTimestampAtLastSyncForData);
  143. dest.writeString(mRemotePath);
  144. dest.writeString(mLocalPath);
  145. dest.writeString(mMimeType);
  146. dest.writeInt(mNeedsUpdating ? 1 : 0);
  147. dest.writeInt(mAvailableOffline ? 1 : 0);
  148. dest.writeLong(mLastSyncDateForProperties);
  149. dest.writeLong(mLastSyncDateForData);
  150. dest.writeString(mEtag);
  151. dest.writeInt(mShareByLink ? 1 : 0);
  152. dest.writeString(mPublicLink);
  153. dest.writeString(mPermissions);
  154. dest.writeString(mRemoteId);
  155. dest.writeInt(mNeedsUpdateThumbnail ? 1 : 0);
  156. dest.writeInt(mIsDownloading ? 1 : 0);
  157. dest.writeString(mEtagInConflict);
  158. dest.writeInt(mShareWithSharee ? 1 : 0);
  159. dest.writeInt(mIsFavorite ? 1 : 0);
  160. dest.writeInt(mIsEncrypted ? 1 : 0);
  161. dest.writeString(mEncryptedFileName);
  162. }
  163. public boolean getIsFavorite() {
  164. return mIsFavorite;
  165. }
  166. public void setFavorite(boolean mIsFavorite) {
  167. this.mIsFavorite = mIsFavorite;
  168. }
  169. public boolean isEncrypted() {
  170. return mIsEncrypted;
  171. }
  172. public void setEncrypted(boolean mIsEncrypted) {
  173. this.mIsEncrypted = mIsEncrypted;
  174. }
  175. /**
  176. * Gets the android internal ID of the file
  177. *
  178. * @return the android internal file ID
  179. */
  180. public long getFileId() {
  181. return mId;
  182. }
  183. public String getDecryptedRemotePath() {
  184. return mRemotePath;
  185. }
  186. /**
  187. * Returns the remote path of the file on ownCloud
  188. *
  189. * @return The remote path to the file
  190. */
  191. public String getRemotePath() {
  192. if (isEncrypted() && !isFolder()) {
  193. String parentPath = new File(mRemotePath).getParent();
  194. if (parentPath.endsWith("/")) {
  195. return parentPath + getEncryptedFileName();
  196. } else {
  197. return parentPath + "/" + getEncryptedFileName();
  198. }
  199. } else {
  200. if (isFolder()) {
  201. if (mRemotePath.endsWith("/")) {
  202. return mRemotePath;
  203. } else {
  204. return mRemotePath + "/";
  205. }
  206. } else {
  207. return mRemotePath;
  208. }
  209. }
  210. }
  211. public void setRemotePath(String path) {
  212. mRemotePath = path;
  213. }
  214. /**
  215. * Can be used to check, whether or not this file exists in the database
  216. * already
  217. *
  218. * @return true, if the file exists in the database
  219. */
  220. public boolean fileExists() {
  221. return mId != -1;
  222. }
  223. /**
  224. * Use this to find out if this file is a folder.
  225. *
  226. * @return true if it is a folder
  227. */
  228. public boolean isFolder() {
  229. return mMimeType != null && mMimeType.equals(MimeType.DIRECTORY);
  230. }
  231. /**
  232. * Use this to check if this file is available locally
  233. *
  234. * @return true if it is
  235. */
  236. public boolean isDown() {
  237. return !isFolder() && existsOnDevice();
  238. }
  239. /**
  240. * Use this to check if this file or folder is available locally
  241. *
  242. * @return true if it is
  243. */
  244. public boolean existsOnDevice() {
  245. if (mLocalPath != null && mLocalPath.length() > 0) {
  246. File file = new File(mLocalPath);
  247. return (file.exists());
  248. }
  249. return false;
  250. }
  251. /**
  252. * The path, where the file is stored locally
  253. *
  254. * @return The local path to the file
  255. */
  256. public String getStoragePath() {
  257. return mLocalPath;
  258. }
  259. /**
  260. * The URI to the file contents, if stored locally
  261. *
  262. * @return A URI to the local copy of the file, or NULL if not stored in the device
  263. */
  264. public Uri getStorageUri() {
  265. if (mLocalPath == null || mLocalPath.length() == 0) {
  266. return null;
  267. }
  268. if (mLocalUri == null) {
  269. Uri.Builder builder = new Uri.Builder();
  270. builder.scheme(ContentResolver.SCHEME_FILE);
  271. builder.path(mLocalPath);
  272. mLocalUri = builder.build();
  273. }
  274. return mLocalUri;
  275. }
  276. public Uri getLegacyExposedFileUri(Context context) {
  277. if (mLocalPath == null || mLocalPath.length() == 0) {
  278. return null;
  279. }
  280. if (mExposedFileUri == null) {
  281. return Uri.parse(ContentResolver.SCHEME_FILE + "://" + WebdavUtils.encodePath(mLocalPath));
  282. }
  283. return mExposedFileUri;
  284. }
  285. /*
  286. Partly disabled because not all apps understand paths that we get via this method for now
  287. */
  288. public Uri getExposedFileUri(Context context) {
  289. if (mLocalPath == null || mLocalPath.length() == 0) {
  290. return null;
  291. }
  292. if (mExposedFileUri == null) {
  293. try {
  294. mExposedFileUri = FileProvider.getUriForFile(
  295. context,
  296. context.getString(R.string.file_provider_authority),
  297. new File(mLocalPath));
  298. } catch (IllegalArgumentException ex) {
  299. // Could not share file using FileProvider URI scheme.
  300. // Fall back to legacy URI parsing.
  301. getLegacyExposedFileUri(context);
  302. }
  303. }
  304. return mExposedFileUri;
  305. }
  306. /**
  307. * Can be used to set the path where the file is stored
  308. *
  309. * @param storage_path to set
  310. */
  311. public void setStoragePath(String storage_path) {
  312. mLocalPath = storage_path;
  313. mLocalUri = null;
  314. mExposedFileUri = null;
  315. }
  316. /**
  317. * Get a UNIX timestamp of the file creation time
  318. *
  319. * @return A UNIX timestamp of the time that file was created
  320. */
  321. public long getCreationTimestamp() {
  322. return mCreationTimestamp;
  323. }
  324. /**
  325. * Set a UNIX timestamp of the time the file was created
  326. *
  327. * @param creation_timestamp to set
  328. */
  329. public void setCreationTimestamp(long creation_timestamp) {
  330. mCreationTimestamp = creation_timestamp;
  331. }
  332. /**
  333. * Get a UNIX timestamp of the file modification time.
  334. *
  335. * @return A UNIX timestamp of the modification time, corresponding to the value returned by the server
  336. * in the last synchronization of the properties of this file.
  337. */
  338. public long getModificationTimestamp() {
  339. return mModifiedTimestamp;
  340. }
  341. /**
  342. * Set a UNIX timestamp of the time the time the file was modified.
  343. * <p/>
  344. * To update with the value returned by the server in every synchronization of the properties
  345. * of this file.
  346. *
  347. * @param modification_timestamp to set
  348. */
  349. public void setModificationTimestamp(long modification_timestamp) {
  350. mModifiedTimestamp = modification_timestamp;
  351. }
  352. /**
  353. * Get a UNIX timestamp of the file modification time.
  354. *
  355. * @return A UNIX timestamp of the modification time, corresponding to the value returned by the server
  356. * in the last synchronization of THE CONTENTS of this file.
  357. */
  358. public long getModificationTimestampAtLastSyncForData() {
  359. return mModifiedTimestampAtLastSyncForData;
  360. }
  361. /**
  362. * Set a UNIX timestamp of the time the time the file was modified.
  363. * <p/>
  364. * To update with the value returned by the server in every synchronization of THE CONTENTS
  365. * of this file.
  366. *
  367. * @param modificationTimestamp to set
  368. */
  369. public void setModificationTimestampAtLastSyncForData(long modificationTimestamp) {
  370. mModifiedTimestampAtLastSyncForData = modificationTimestamp;
  371. }
  372. /**
  373. * Returns the filename and "/" for the root directory
  374. *
  375. * @return The name of the file
  376. */
  377. public String getFileName() {
  378. File f = new File(mRemotePath);
  379. return f.getName().length() == 0 ? ROOT_PATH : f.getName();
  380. }
  381. /**
  382. * Sets the name of the file
  383. * <p/>
  384. * Does nothing if the new name is null, empty or includes "/" ; or if the file is the root
  385. * directory
  386. */
  387. public void setFileName(String name) {
  388. Log_OC.d(TAG, "OCFile name changin from " + mRemotePath);
  389. if (name != null && name.length() > 0 && !name.contains(PATH_SEPARATOR) &&
  390. !mRemotePath.equals(ROOT_PATH)) {
  391. String parent = (new File(getRemotePath())).getParent();
  392. parent = (parent.endsWith(PATH_SEPARATOR)) ? parent : parent + PATH_SEPARATOR;
  393. mRemotePath = parent + name;
  394. if (isFolder()) {
  395. mRemotePath += PATH_SEPARATOR;
  396. }
  397. Log_OC.d(TAG, "OCFile name changed to " + mRemotePath);
  398. }
  399. }
  400. public void setEncryptedFileName(String name) {
  401. mEncryptedFileName = name;
  402. }
  403. public String getEncryptedFileName() {
  404. return mEncryptedFileName;
  405. }
  406. /**
  407. * Can be used to get the Mimetype
  408. *
  409. * @return the Mimetype as a String
  410. */
  411. public String getMimetype() {
  412. return mMimeType;
  413. }
  414. /**
  415. * Used internally. Reset all file properties
  416. */
  417. private void resetData() {
  418. mId = -1;
  419. mRemotePath = null;
  420. mParentId = 0;
  421. mLocalPath = null;
  422. mMimeType = null;
  423. mLength = 0;
  424. mCreationTimestamp = 0;
  425. mModifiedTimestamp = 0;
  426. mModifiedTimestampAtLastSyncForData = 0;
  427. mLastSyncDateForProperties = 0;
  428. mLastSyncDateForData = 0;
  429. mAvailableOffline = false;
  430. mNeedsUpdating = false;
  431. mEtag = null;
  432. mShareByLink = false;
  433. mPublicLink = null;
  434. mPermissions = null;
  435. mRemoteId = null;
  436. mNeedsUpdateThumbnail = false;
  437. mIsDownloading = false;
  438. mEtagInConflict = null;
  439. mShareWithSharee = false;
  440. mIsFavorite = false;
  441. }
  442. /**
  443. * Sets the ID of the file
  444. *
  445. * @param file_id to set
  446. */
  447. public void setFileId(long file_id) {
  448. mId = file_id;
  449. }
  450. /**
  451. * Sets the Mime-Type of the
  452. *
  453. * @param mimetype to set
  454. */
  455. public void setMimetype(String mimetype) {
  456. mMimeType = mimetype;
  457. }
  458. /**
  459. * Sets the ID of the parent folder
  460. *
  461. * @param parent_id to set
  462. */
  463. public void setParentId(long parent_id) {
  464. mParentId = parent_id;
  465. }
  466. /**
  467. * Sets the file size in bytes
  468. *
  469. * @param file_len to set
  470. */
  471. public void setFileLength(long file_len) {
  472. mLength = file_len;
  473. }
  474. /**
  475. * Returns the size of the file in bytes
  476. *
  477. * @return The filesize in bytes
  478. */
  479. public long getFileLength() {
  480. return mLength;
  481. }
  482. /**
  483. * Returns the ID of the parent Folder
  484. *
  485. * @return The ID
  486. */
  487. public long getParentId() {
  488. return mParentId;
  489. }
  490. /**
  491. * get remote path of parent file
  492. *
  493. * @return remote path
  494. */
  495. public String getParentRemotePath() {
  496. String parentPath = new File(getRemotePath()).getParent();
  497. return (parentPath.endsWith("/")) ? parentPath : (parentPath + "/");
  498. }
  499. /**
  500. * Check, if this file needs updating
  501. *
  502. * @return
  503. */
  504. public boolean needsUpdatingWhileSaving() {
  505. return mNeedsUpdating;
  506. }
  507. public boolean needsUpdateThumbnail() {
  508. return mNeedsUpdateThumbnail;
  509. }
  510. public void setNeedsUpdateThumbnail(boolean needsUpdateThumbnail) {
  511. this.mNeedsUpdateThumbnail = needsUpdateThumbnail;
  512. }
  513. public long getLastSyncDateForProperties() {
  514. return mLastSyncDateForProperties;
  515. }
  516. public void setLastSyncDateForProperties(long lastSyncDate) {
  517. mLastSyncDateForProperties = lastSyncDate;
  518. }
  519. public long getLastSyncDateForData() {
  520. return mLastSyncDateForData;
  521. }
  522. public void setLastSyncDateForData(long lastSyncDate) {
  523. mLastSyncDateForData = lastSyncDate;
  524. }
  525. public void setAvailableOffline(boolean availableOffline) {
  526. mAvailableOffline = availableOffline;
  527. }
  528. public boolean isAvailableOffline() {
  529. return mAvailableOffline;
  530. }
  531. @Override
  532. public int describeContents() {
  533. return super.hashCode();
  534. }
  535. @Override
  536. public int compareTo(OCFile another) {
  537. if (isFolder() && another.isFolder()) {
  538. return getRemotePath().toLowerCase(Locale.ROOT).compareTo(another.getRemotePath().toLowerCase(Locale.ROOT));
  539. } else if (isFolder()) {
  540. return -1;
  541. } else if (another.isFolder()) {
  542. return 1;
  543. }
  544. return new AlphanumComparator().compare(this, another);
  545. }
  546. @Override
  547. public boolean equals(Object o) {
  548. if (this == o) {
  549. return true;
  550. }
  551. if (o == null || getClass() != o.getClass()) {
  552. return false;
  553. }
  554. OCFile ocFile = (OCFile) o;
  555. return mId == ocFile.mId && mParentId == ocFile.mParentId;
  556. }
  557. @Override
  558. public int hashCode() {
  559. int result = (int) (mId ^ (mId >>> 32));
  560. result = 31 * result + (int) (mParentId ^ (mParentId >>> 32));
  561. return result;
  562. }
  563. @Override
  564. public String toString() {
  565. String asString = "[id=%s, name=%s, mime=%s, downloaded=%s, local=%s, remote=%s, " +
  566. "parentId=%s, availableOffline=%s etag=%s favourite=%s]";
  567. asString = String.format(asString, mId, getFileName(), mMimeType, isDown(),
  568. mLocalPath, mRemotePath, mParentId, mAvailableOffline,
  569. mEtag, mIsFavorite);
  570. return asString;
  571. }
  572. public String getEtag() {
  573. return mEtag;
  574. }
  575. public void setEtag(String etag) {
  576. this.mEtag = (etag != null ? etag : "");
  577. }
  578. public boolean isSharedViaLink() {
  579. return mShareByLink;
  580. }
  581. public void setShareViaLink(boolean shareByLink) {
  582. this.mShareByLink = shareByLink;
  583. }
  584. public String getPublicLink() {
  585. return mPublicLink;
  586. }
  587. public void setPublicLink(String publicLink) {
  588. this.mPublicLink = publicLink;
  589. }
  590. public long getLocalModificationTimestamp() {
  591. if (mLocalPath != null && mLocalPath.length() > 0) {
  592. File f = new File(mLocalPath);
  593. return f.lastModified();
  594. }
  595. return 0;
  596. }
  597. /**
  598. * @return 'True' if the file is hidden
  599. */
  600. public boolean isHidden() {
  601. return getFileName().startsWith(".");
  602. }
  603. public String getPermissions() {
  604. return mPermissions;
  605. }
  606. public void setPermissions(String permissions) {
  607. this.mPermissions = permissions;
  608. }
  609. /**
  610. * The fileid namespaced by the instance id, globally unique
  611. *
  612. * @return globally unique file id: file id + instance id
  613. */
  614. public String getRemoteId() {
  615. return mRemoteId;
  616. }
  617. /**
  618. * The unique id for the file within the instance
  619. *
  620. * @return file id, unique within the instance
  621. */
  622. public String getLocalId() {
  623. return getRemoteId().substring(0, 8).replaceAll("^0*", "");
  624. }
  625. public void setRemoteId(String remoteId) {
  626. this.mRemoteId = remoteId;
  627. }
  628. public boolean isDownloading() {
  629. return mIsDownloading;
  630. }
  631. public void setDownloading(boolean isDownloading) {
  632. this.mIsDownloading = isDownloading;
  633. }
  634. public String getEtagInConflict() {
  635. return mEtagInConflict;
  636. }
  637. public boolean isInConflict() {
  638. return mEtagInConflict != null && !mEtagInConflict.equals("");
  639. }
  640. public void setEtagInConflict(String etagInConflict) {
  641. mEtagInConflict = etagInConflict;
  642. }
  643. public boolean isSharedWithSharee() {
  644. return mShareWithSharee;
  645. }
  646. public void setShareWithSharee(boolean shareWithSharee) {
  647. this.mShareWithSharee = shareWithSharee;
  648. }
  649. public boolean isSharedWithMe() {
  650. String permissions = getPermissions();
  651. return (permissions != null && permissions.contains(PERMISSION_SHARED_WITH_ME));
  652. }
  653. public boolean canReshare() {
  654. String permissions = getPermissions();
  655. return permissions != null && permissions.contains(PERMISSION_CAN_RESHARE);
  656. }
  657. }