diff --git a/src/com/owncloud/android/files/FileOperationsHelper.java b/src/com/owncloud/android/files/FileOperationsHelper.java index f0f66a7e50..15ec1fc3c7 100644 --- a/src/com/owncloud/android/files/FileOperationsHelper.java +++ b/src/com/owncloud/android/files/FileOperationsHelper.java @@ -211,7 +211,7 @@ public class FileOperationsHelper { service.setAction(OperationsService.ACTION_CREATE_SHARE_VIA_LINK); service.putExtra(OperationsService.EXTRA_ACCOUNT, mFileActivity.getAccount()); service.putExtra(OperationsService.EXTRA_REMOTE_PATH, file.getRemotePath()); - service.putExtra(OperationsService.EXTRA_PASSWORD_SHARE, password); + service.putExtra(OperationsService.EXTRA_SHARE_PASSWORD, password); service.putExtra(OperationsService.EXTRA_SEND_INTENT, sendIntent); mWaitingForOpId = mFileActivity.getOperationsServiceBinder().queueNewOperation(service); @@ -366,7 +366,7 @@ public class FileOperationsHelper { updateShareIntent.putExtra(OperationsService.EXTRA_ACCOUNT, mFileActivity.getAccount()); updateShareIntent.putExtra(OperationsService.EXTRA_REMOTE_PATH, file.getRemotePath()); updateShareIntent.putExtra( - OperationsService.EXTRA_PASSWORD_SHARE, + OperationsService.EXTRA_SHARE_PASSWORD, (password == null) ? "" : password ); @@ -374,6 +374,28 @@ public class FileOperationsHelper { } + /** + * Updates a public share on a file to set its expiration date. + * Starts a request to do it in {@link OperationsService} + * + * @param file File which public share will be constrained with an expiration date. + * @param year Year of the date expiration chosen. Negative value to remove current + * expiration date and leave the link unrestricted. + * @param monthOfYear Month of the date chosen [0, 11] + * @param dayOfMonth Day of the date chosen + */ + public void setExpirationDateToShareViaLink(OCFile file, int year, int monthOfYear, int dayOfMonth) { + Intent updateShareIntent = new Intent(mFileActivity, OperationsService.class); + updateShareIntent.setAction(OperationsService.ACTION_UPDATE_SHARE); + updateShareIntent.putExtra(OperationsService.EXTRA_ACCOUNT, mFileActivity.getAccount()); + updateShareIntent.putExtra(OperationsService.EXTRA_REMOTE_PATH, file.getRemotePath()); + updateShareIntent.putExtra(OperationsService.EXTRA_SHARE_EXPIRATION_YEAR, year); + updateShareIntent.putExtra(OperationsService.EXTRA_SHARE_EXPIRATION_MONTH_OF_YEAR, monthOfYear); + updateShareIntent.putExtra(OperationsService.EXTRA_SHARE_EXPIRATION_DAY_OF_MONTH, dayOfMonth); + queueShareIntent(updateShareIntent); + } + + /** * @return 'True' if the server supports the Search Users API */ diff --git a/src/com/owncloud/android/operations/UpdateShareViaLinkOperation.java b/src/com/owncloud/android/operations/UpdateShareViaLinkOperation.java index 64c25df36b..e4a5d853e4 100644 --- a/src/com/owncloud/android/operations/UpdateShareViaLinkOperation.java +++ b/src/com/owncloud/android/operations/UpdateShareViaLinkOperation.java @@ -31,6 +31,8 @@ import com.owncloud.android.lib.resources.shares.ShareType; import com.owncloud.android.lib.resources.shares.UpdateRemoteShareOperation; import com.owncloud.android.operations.common.SyncOperation; +import java.util.Calendar; + /** * Updates an existing public share for a given file @@ -40,21 +42,45 @@ public class UpdateShareViaLinkOperation extends SyncOperation { private String mPath; private String mPassword; + private Calendar mExpirationDate; /** * Constructor + * * @param path Full path of the file/folder being shared. Mandatory argument - * @param password Password to protect a public link share. */ - public UpdateShareViaLinkOperation( - String path, - String password - ) { + public UpdateShareViaLinkOperation(String path) { mPath = path; + mPassword = null; + mExpirationDate = null; + } + + + /** + * Set password to update in public link. + * + * @param password Password to set to the public link. + * Empty string clears the current password. + * Null results in no update applied to the password. + */ + public void setPassword(String password) { mPassword = password; } + + /** + * Set expiration date to update in Share resource. + * + * @param expirationDate Expiration date to set to the public link. + * Start-of-epoch clears the current expiration date. + * Null results in no update applied to the expiration date. + */ + public void setExpirationDate(Calendar expirationDate) { + mExpirationDate = expirationDate; + } + + @Override protected RemoteOperationResult run(OwnCloudClient client) { @@ -70,11 +96,12 @@ public class UpdateShareViaLinkOperation extends SyncOperation { } // Update remote share with password - RemoteOperation operation = new UpdateRemoteShareOperation( + UpdateRemoteShareOperation udpateOp = new UpdateRemoteShareOperation( publicShare.getRemoteId() ); - ((UpdateRemoteShareOperation)operation).setPassword(mPassword); - RemoteOperationResult result = operation.execute(client); + udpateOp.setPassword(mPassword); + udpateOp.setExpirationDate(mExpirationDate); + RemoteOperationResult result = udpateOp.execute(client); /* if (!result.isSuccess() || result.getData().size() <= 0) { @@ -92,8 +119,8 @@ public class UpdateShareViaLinkOperation extends SyncOperation { if (result.isSuccess()) { // Retrieve updated share / save directly with password? -> no; the password is not be saved - operation = new GetRemoteShareOperation(publicShare.getRemoteId()); - result = operation.execute(client); + RemoteOperation getShareOp = new GetRemoteShareOperation(publicShare.getRemoteId()); + result = getShareOp.execute(client); if (result.isSuccess()) { OCShare share = (OCShare) result.getData().get(0); updateData(share); diff --git a/src/com/owncloud/android/services/OperationsService.java b/src/com/owncloud/android/services/OperationsService.java index 6c0dde6834..b1303c5882 100644 --- a/src/com/owncloud/android/services/OperationsService.java +++ b/src/com/owncloud/android/services/OperationsService.java @@ -68,6 +68,7 @@ import com.owncloud.android.operations.UpdateShareViaLinkOperation; import com.owncloud.android.operations.common.SyncOperation; import java.io.IOException; +import java.util.Calendar; import java.util.Iterator; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentLinkedQueue; @@ -89,9 +90,12 @@ public class OperationsService extends Service { public static final String EXTRA_RESULT = "RESULT"; public static final String EXTRA_NEW_PARENT_PATH = "NEW_PARENT_PATH"; public static final String EXTRA_FILE = "FILE"; - public static final String EXTRA_PASSWORD_SHARE = "PASSWORD_SHARE"; + public static final String EXTRA_SHARE_PASSWORD = "SHARE_PASSWORD"; public static final String EXTRA_SHARE_TYPE = "SHARE_TYPE"; public static final String EXTRA_SHARE_WITH = "SHARE_WITH"; + public static final String EXTRA_SHARE_EXPIRATION_YEAR = "SHARE_EXPIRATION_YEAR"; + public static final String EXTRA_SHARE_EXPIRATION_MONTH_OF_YEAR = "SHARE_EXPIRATION_MONTH_OF_YEAR"; + public static final String EXTRA_SHARE_EXPIRATION_DAY_OF_MONTH = "SHARE_EXPIRATION_DAY_OF_MONTH"; public static final String EXTRA_COOKIE = "COOKIE"; @@ -555,7 +559,7 @@ public class OperationsService extends Service { String action = operationIntent.getAction(); if (action.equals(ACTION_CREATE_SHARE_VIA_LINK)) { // Create public share via link String remotePath = operationIntent.getStringExtra(EXTRA_REMOTE_PATH); - String password = operationIntent.getStringExtra(EXTRA_PASSWORD_SHARE); + String password = operationIntent.getStringExtra(EXTRA_SHARE_PASSWORD); Intent sendIntent = operationIntent.getParcelableExtra(EXTRA_SEND_INTENT); if (remotePath.length() > 0) { operation = new CreateShareViaLinkOperation( @@ -567,22 +571,52 @@ public class OperationsService extends Service { } else if (ACTION_UPDATE_SHARE.equals(action)) { String remotePath = operationIntent.getStringExtra(EXTRA_REMOTE_PATH); - String password = operationIntent.getStringExtra(EXTRA_PASSWORD_SHARE); if (remotePath.length() > 0) { - operation = new UpdateShareViaLinkOperation(remotePath, password); + operation = new UpdateShareViaLinkOperation(remotePath); + + String password = operationIntent.getStringExtra(EXTRA_SHARE_PASSWORD); + ((UpdateShareViaLinkOperation)operation).setPassword(password); + + int year = operationIntent.getIntExtra(EXTRA_SHARE_EXPIRATION_YEAR, 0); + if (year > 0) { + // expiration date is set + int monthOfYear = operationIntent.getIntExtra( + EXTRA_SHARE_EXPIRATION_MONTH_OF_YEAR, 0 + ); + int dayOfMonth = operationIntent.getIntExtra( + EXTRA_SHARE_EXPIRATION_DAY_OF_MONTH, 1 + ); + Calendar expirationDate = Calendar.getInstance(); + expirationDate.set(Calendar.YEAR, year); + expirationDate.set(Calendar.MONTH, monthOfYear); + expirationDate.set(Calendar.DAY_OF_MONTH, dayOfMonth); + ((UpdateShareViaLinkOperation)operation).setExpirationDate( + expirationDate + ); + + } else if (year < 0) { + // expiration date to be cleared + Calendar zeroDate = Calendar.getInstance(); + zeroDate.clear(); + ((UpdateShareViaLinkOperation)operation).setExpirationDate( + zeroDate + ); + + } // else, no update on expiration date } - } else if (action.equals(ACTION_CREATE_SHARE_WITH_SHAREE)) { // Create private share with user or group - String remotePath = operationIntent.getStringExtra(EXTRA_REMOTE_PATH); - String shareeName = operationIntent.getStringExtra(EXTRA_SHARE_WITH); - ShareType shareType = (ShareType) operationIntent.getSerializableExtra(EXTRA_SHARE_TYPE); - if (remotePath.length() > 0) { - operation = new CreateShareWithShareeOperation( - remotePath, - shareeName, - shareType - ); - } + } else if (action.equals(ACTION_CREATE_SHARE_WITH_SHAREE)) { + // Create private share with user or group + String remotePath = operationIntent.getStringExtra(EXTRA_REMOTE_PATH); + String shareeName = operationIntent.getStringExtra(EXTRA_SHARE_WITH); + ShareType shareType = (ShareType) operationIntent.getSerializableExtra(EXTRA_SHARE_TYPE); + if (remotePath.length() > 0) { + operation = new CreateShareWithShareeOperation( + remotePath, + shareeName, + shareType + ); + } } else if (action.equals(ACTION_UNSHARE)) { // Unshare file String remotePath = operationIntent.getStringExtra(EXTRA_REMOTE_PATH); diff --git a/src/com/owncloud/android/ui/activity/ShareActivity.java b/src/com/owncloud/android/ui/activity/ShareActivity.java index 46b044041f..9c4d33efe9 100644 --- a/src/com/owncloud/android/ui/activity/ShareActivity.java +++ b/src/com/owncloud/android/ui/activity/ShareActivity.java @@ -154,26 +154,28 @@ public class ShareActivity extends FileActivity super.onRemoteOperationFinish(operation, result); if (result.isSuccess()) { - Log_OC.d(TAG, "Refreshing lists on successful operation"); + Log_OC.d(TAG, "Refreshing view on successful operation"); refreshSharesFromStorageManager(); } - } + + /** + * Updates the view, reading data from {@link com.owncloud.android.datamodel.FileDataStorageManager} + */ private void refreshSharesFromStorageManager() { + ShareFileFragment shareFileFragment = getShareFileFragment(); - if (shareFileFragment != null) { // only if added to the view hierarchy!! - if (shareFileFragment.isAdded()) { - shareFileFragment.refreshUsersOrGroupsListFromDB(); - shareFileFragment.refreshPublicShareFromDB(); - } + if (shareFileFragment != null + && shareFileFragment.isAdded()) { // only if added to the view hierarchy!! + shareFileFragment.refreshUsersOrGroupsListFromDB(); + shareFileFragment.refreshPublicShareFromDB(); } SearchShareesFragment searchShareesFragment = getSearchFragment(); - if (searchShareesFragment != null) { - if (searchShareesFragment.isAdded()) { // only if added to the view hierarchy!! - searchShareesFragment.refreshUsersOrGroupsListFromDB(); - } + if (searchShareesFragment != null && + searchShareesFragment.isAdded()) { // only if added to the view hierarchy!! + searchShareesFragment.refreshUsersOrGroupsListFromDB(); } } diff --git a/src/com/owncloud/android/ui/dialog/ExpirationDatePickerDialogFragment.java b/src/com/owncloud/android/ui/dialog/ExpirationDatePickerDialogFragment.java new file mode 100644 index 0000000000..8e3260a500 --- /dev/null +++ b/src/com/owncloud/android/ui/dialog/ExpirationDatePickerDialogFragment.java @@ -0,0 +1,124 @@ +/** + * ownCloud Android client application + * + * @author David A. Velasco + * Copyright (C) 2015 ownCloud Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +package com.owncloud.android.ui.dialog; + + +import android.app.DatePickerDialog; +import android.app.Dialog; +import android.content.DialogInterface; +import android.os.Bundle; +import android.support.v4.app.DialogFragment; +import android.text.format.DateUtils; +import android.widget.DatePicker; +import android.widget.Toast; + +import com.owncloud.android.datamodel.OCFile; +import com.owncloud.android.ui.activity.FileActivity; + +import java.util.Calendar; +import java.util.Date; + +/** + * Dialog requesting a date after today. + */ +public class ExpirationDatePickerDialogFragment + extends DialogFragment + implements DatePickerDialog.OnDateSetListener { + + /** Tag for FragmentsManager */ + public static final String DATE_PICKER_DIALOG = "DATE_PICKER_DIALOG"; + + /** Constructor arguments */ + private static final String ARG_FILE = "ARG_FILE"; + + /** File to bind an expiration date */ + private OCFile mFile; + + /** + * Factory method to create new instances + * + * @param file File to bind an expiration date + * @return New dialog instance + */ + public static ExpirationDatePickerDialogFragment newInstance(OCFile file) { + Bundle arguments = new Bundle(); + arguments.putParcelable(ARG_FILE, file); + + ExpirationDatePickerDialogFragment dialog = new ExpirationDatePickerDialogFragment(); + dialog.setArguments(arguments); + return dialog; + } + + /** + * {@inheritDoc} + * + * @return A new dialog to let the user choose an expiration date that will be bound to a share link. + */ + @Override + public Dialog onCreateDialog(Bundle savedInstanceState) { + // Load arguments + mFile = getArguments().getParcelable(ARG_FILE); + + // Get current day + final Calendar c = Calendar.getInstance(); + int year = c.get(Calendar.YEAR); + int month = c.get(Calendar.MONTH); + int day = c.get(Calendar.DAY_OF_MONTH); + + // Create a new instance of DatePickerDialog, highlighting "tomorrow" as chosen day + DatePickerDialog dialog = new DatePickerDialog(getActivity(), this, year, month, day + 1); + + // Prevent days in the past may be chosen + DatePicker picker = dialog.getDatePicker(); + picker.setMinDate(System.currentTimeMillis() + DateUtils.DAY_IN_MILLIS - 1000); + + // Enforce spinners view; ignored by MD-based theme in Android >=5, but calendar is REALLY buggy + // in Android < 5, so let's be sure it never appears (in tablets both spinners and calendar are + // shown by default) + picker.setCalendarViewShown(false); + + return dialog; + } + + /** + * Called when the user choses an expiration date. + * + * @param view View instance where the date was chosen + * @param year Year of the date chosen. + * @param monthOfYear Month of the date chosen [0, 11] + * @param dayOfMonth Day of the date chosen + */ + @Override + public void onDateSet(DatePicker view, int year, int monthOfYear, int dayOfMonth) { + + Calendar chosenDate = Calendar.getInstance(); + chosenDate.set(Calendar.YEAR, year); + chosenDate.set(Calendar.MONTH, monthOfYear); + chosenDate.set(Calendar.DAY_OF_MONTH, dayOfMonth); + + ((FileActivity)getActivity()).getFileOperationsHelper().setExpirationDateToShareViaLink( + mFile, + year, + monthOfYear, + dayOfMonth + ); + } +} diff --git a/src/com/owncloud/android/ui/fragment/ShareFileFragment.java b/src/com/owncloud/android/ui/fragment/ShareFileFragment.java index 2ed5d0b2c5..838635aec8 100644 --- a/src/com/owncloud/android/ui/fragment/ShareFileFragment.java +++ b/src/com/owncloud/android/ui/fragment/ShareFileFragment.java @@ -47,10 +47,13 @@ import com.owncloud.android.lib.resources.shares.OCShare; import com.owncloud.android.lib.resources.shares.ShareType; import com.owncloud.android.ui.activity.FileActivity; import com.owncloud.android.ui.adapter.ShareUserListAdapter; +import com.owncloud.android.ui.dialog.ExpirationDatePickerDialogFragment; import com.owncloud.android.utils.DisplayUtils; import com.owncloud.android.utils.MimetypeIconUtil; +import java.text.SimpleDateFormat; import java.util.ArrayList; +import java.util.Date; /** * Fragment for Sharing a file with sharees (users or groups) or creating @@ -95,9 +98,16 @@ public class ShareFileFragment extends Fragment /** Listener for changes on switch to share / unshare publicly */ private CompoundButton.OnCheckedChangeListener mOnShareViaLinkSwitchCheckedChangeListener; - /** Listener for changes on switch to set / clear password on public link */ + /** + * Listener for changes on switch to set / clear password on public link + */ private CompoundButton.OnCheckedChangeListener mOnPasswordSwitchCheckedChangeListener; + /** + * Listener for changes on switch to set / clear expiration date on public link + */ + private CompoundButton.OnCheckedChangeListener mOnExpirationDateSwitchCheckedChangeListener; + /** * Public factory method to create new ShareFileFragment instances. @@ -203,8 +213,7 @@ public class ShareFileFragment extends Fragment shareViaLinkSwitch.setOnCheckedChangeListener(mOnShareViaLinkSwitchCheckedChangeListener); // Switch for expiration date - Switch shareViaLinkExpirationSwitch = (Switch) view.findViewById(R.id.shareViaLinkExpirationSwitch); - shareViaLinkExpirationSwitch.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { + mOnExpirationDateSwitchCheckedChangeListener = new CompoundButton.OnCheckedChangeListener() { @Override public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { if (!isResumed()) { @@ -213,17 +222,26 @@ public class ShareFileFragment extends Fragment return; } if (isChecked) { - // TODO real implementation: update share with expiration date - // show value of expiration date - getExpirationDateValue().setText(R.string.placeholder_timestamp); + ExpirationDatePickerDialogFragment dialog = + ExpirationDatePickerDialogFragment.newInstance(mFile); + dialog.show( + getActivity().getSupportFragmentManager(), + ExpirationDatePickerDialogFragment.DATE_PICKER_DIALOG + ); } else { - // TODO real implementation: update share without expiration date - // empty value - getExpirationDateValue().setText(R.string.empty); + ((FileActivity) getActivity()).getFileOperationsHelper(). + setExpirationDateToShareViaLink(mFile, -1, -1, -1); } + + // undo the toggle to grant the view will be correct if the dialog is cancelled + buttonView.setOnCheckedChangeListener(null); + buttonView.toggle(); + buttonView.setOnCheckedChangeListener(mOnExpirationDateSwitchCheckedChangeListener); } - }); + }; + Switch shareViaLinkExpirationSwitch = (Switch) view.findViewById(R.id.shareViaLinkExpirationSwitch); + shareViaLinkExpirationSwitch.setOnCheckedChangeListener(mOnExpirationDateSwitchCheckedChangeListener); // Switch for password mOnPasswordSwitchCheckedChangeListener = new CompoundButton.OnCheckedChangeListener() { @@ -241,6 +259,11 @@ public class ShareFileFragment extends Fragment ((FileActivity) getActivity()).getFileOperationsHelper(). setPasswordToShareViaLink(mFile, ""); // "" clears } + + // undo the toggle to grant the view will be correct if the dialog is cancelled + buttonView.setOnCheckedChangeListener(null); + buttonView.toggle(); + buttonView.setOnCheckedChangeListener(mOnPasswordSwitchCheckedChangeListener); } }; Switch shareViaLinkPasswordSwitch = (Switch) view.findViewById(R.id.shareViaLinkPasswordSwitch); @@ -278,7 +301,7 @@ public class ShareFileFragment extends Fragment } /** - * Get users and groups from the DB to fill in the "share with" list + * Get users and groups from the DB to fill in the "share with" list. * * Depends on the parent Activity provides a {@link com.owncloud.android.datamodel.FileDataStorageManager} * instance ready to use. If not ready, does nothing. @@ -331,7 +354,7 @@ public class ShareFileFragment extends Fragment /** - * Get public link from the DB to fill in the "Share link" section + * Get public link from the DB to fill in the "Share link" section in the UI. * * Depends on the parent Activity provides a {@link com.owncloud.android.datamodel.FileDataStorageManager} * instance ready to use. If not ready, does nothing. @@ -345,7 +368,7 @@ public class ShareFileFragment extends Fragment "" ); - // Update list of users/groups + // Update public share section updatePublicShareSection(); } } @@ -370,6 +393,32 @@ public class ShareFileFragment extends Fragment getPasswordSection().setVisibility(View.VISIBLE); getGetLinkButton().setVisibility(View.VISIBLE); + /// update state of expiration date switch and message depending on expiration date + /// update state of expiration date switch and message depending on expiration date + Switch expirationDateSwitch = getExpirationDateSwitch(); + // set null listener before setChecked() to prevent infinite loop of calls + expirationDateSwitch.setOnCheckedChangeListener(null); + long expirationDate = mPublicShare.getExpirationDate(); + if (expirationDate > 0) { + if (!expirationDateSwitch.isChecked()) { + expirationDateSwitch.toggle(); + } + String formattedDate = + SimpleDateFormat.getDateInstance().format( + new Date(expirationDate) + ); + getExpirationDateValue().setText(formattedDate); + } else { + if (expirationDateSwitch.isChecked()) { + expirationDateSwitch.toggle(); + } + getExpirationDateValue().setText(R.string.empty); + } + // recover listener + expirationDateSwitch.setOnCheckedChangeListener( + mOnExpirationDateSwitchCheckedChangeListener + ); + /// update state of password switch and message depending on password protection Switch passwordSwitch = getPasswordSwitch(); // set null listener before setChecked() to prevent infinite loop of calls @@ -407,6 +456,7 @@ public class ShareFileFragment extends Fragment } } + /// BEWARE: next methods will failed with NullPointerException if called before onCreateView() finishes private Switch getShareViaLinkSwitch() { @@ -417,6 +467,10 @@ public class ShareFileFragment extends Fragment return getView().findViewById(R.id.shareViaLinkExpirationSection); } + private Switch getExpirationDateSwitch() { + return (Switch) getView().findViewById(R.id.shareViaLinkExpirationSwitch); + } + private TextView getExpirationDateValue() { return (TextView) getView().findViewById(R.id.shareViaLinkExpirationValue); }