Merge pull request #2690 from nextcloud/comments

Iteration 5 for File detail & new sharing - Comments
This commit is contained in:
Tobias Kaminsky 2018-06-20 13:49:54 +02:00 committed by GitHub
commit dc315a9394
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 269 additions and 18 deletions

View file

@ -0,0 +1,112 @@
/*
* Nextcloud Android client application
*
* @author Tobias Kaminsky
* Copyright (C) 2018 Tobias Kaminsky
* Copyright (C) 2018 Nextcloud GmbH.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* 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 <https://www.gnu.org/licenses/>.
*/
package com.owncloud.android.operations;
import android.util.Log;
import com.google.gson.GsonBuilder;
import com.owncloud.android.lib.common.OwnCloudClient;
import com.owncloud.android.lib.common.operations.RemoteOperationResult;
import com.owncloud.android.operations.common.SyncOperation;
import org.apache.commons.httpclient.HttpStatus;
import org.apache.commons.httpclient.methods.PostMethod;
import org.apache.commons.httpclient.methods.StringRequestEntity;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
/**
* Restore a {@link com.owncloud.android.lib.resources.files.FileVersion}.
*/
public class CommentFileOperation extends SyncOperation {
private static final String TAG = CommentFileOperation.class.getSimpleName();
private static final int POST_READ_TIMEOUT = 30000;
private static final int POST_CONNECTION_TIMEOUT = 5000;
private static final String ACTOR_ID = "actorId";
private static final String ACTOR_TYPE = "actorType";
private static final String ACTOR_TYPE_VALUE = "users";
private static final String VERB = "verb";
private static final String VERB_VALUE = "comment";
private static final String MESSAGE = "message";
private String message;
private String fileId;
private String userId;
/**
* Constructor
*
* @param message Comment to store
* @param userId userId to access correct dav endpoint
*/
public CommentFileOperation(String message, String fileId, String userId) {
this.message = message;
this.fileId = fileId;
this.userId = userId;
}
/**
* Performs the operation.
*
* @param client Client object to communicate with the remote ownCloud server.
*/
@Override
protected RemoteOperationResult run(OwnCloudClient client) {
RemoteOperationResult result;
try {
String url = client.getNewWebdavUri(false) + "/comments/files/" + fileId;
PostMethod postMethod = new PostMethod(url);
postMethod.addRequestHeader("Content-type", "application/json");
Map<String, String> values = new HashMap<>();
values.put(ACTOR_ID, userId);
values.put(ACTOR_TYPE, ACTOR_TYPE_VALUE);
values.put(VERB, VERB_VALUE);
values.put(MESSAGE, message);
String json = new GsonBuilder().create().toJson(values, Map.class);
postMethod.setRequestEntity(new StringRequestEntity(json));
int status = client.executeMethod(postMethod, POST_READ_TIMEOUT, POST_CONNECTION_TIMEOUT);
result = new RemoteOperationResult(isSuccess(status), postMethod);
client.exhaustResponse(postMethod.getResponseBodyAsStream());
} catch (IOException e) {
result = new RemoteOperationResult(e);
Log.e(TAG, "Post comment to file with id " + fileId + " failed: " + result.getLogMessage(), e);
}
return result;
}
private boolean isSuccess(int status) {
return status == HttpStatus.SC_CREATED;
}
}

View file

@ -26,8 +26,11 @@ import android.accounts.AuthenticatorException;
import android.accounts.OperationCanceledException;
import android.content.Context;
import android.graphics.PorterDuff;
import android.os.AsyncTask;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.design.widget.Snackbar;
import android.support.design.widget.TextInputEditText;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentActivity;
import android.support.v4.widget.SwipeRefreshLayout;
@ -57,6 +60,7 @@ import com.owncloud.android.lib.resources.files.FileVersion;
import com.owncloud.android.lib.resources.files.ReadFileVersionsOperation;
import com.owncloud.android.lib.resources.status.OCCapability;
import com.owncloud.android.lib.resources.status.OwnCloudVersion;
import com.owncloud.android.operations.CommentFileOperation;
import com.owncloud.android.ui.activity.ComponentsGetter;
import com.owncloud.android.ui.activity.FileActivity;
import com.owncloud.android.ui.adapter.ActivityAndVersionListAdapter;
@ -71,6 +75,7 @@ import java.util.ArrayList;
import butterknife.BindString;
import butterknife.BindView;
import butterknife.ButterKnife;
import butterknife.OnClick;
import butterknife.Unbinder;
public class FileDetailActivitiesFragment extends Fragment implements ActivityListInterface, VersionListInterface.View {
@ -113,14 +118,19 @@ public class FileDetailActivitiesFragment extends Fragment implements ActivityLi
@BindView(android.R.id.list)
public RecyclerView recyclerView;
@BindView(R.id.commentInputField)
public TextInputEditText commentInput;
@BindString(R.string.activities_no_results_headline)
public String noResultsHeadline;
@BindString(R.string.activities_no_results_message)
public String noResultsMessage;
private boolean restoreFileVersionSupported;
private String userId;
private FileOperationsHelper operationsHelper;
private VersionListInterface.CommentCallback callback;
public static FileDetailActivitiesFragment newInstance(OCFile file, Account account) {
FileDetailActivitiesFragment fragment = new FileDetailActivitiesFragment();
@ -154,19 +164,43 @@ public class FileDetailActivitiesFragment extends Fragment implements ActivityLi
fetchAndSetData(null);
swipeListRefreshLayout.setOnRefreshListener(
() -> onRefreshListLayout(swipeListRefreshLayout));
swipeEmptyListRefreshLayout.setOnRefreshListener(
() -> onRefreshListLayout(swipeEmptyListRefreshLayout));
swipeListRefreshLayout.setOnRefreshListener(() -> onRefreshListLayout(swipeListRefreshLayout));
swipeEmptyListRefreshLayout.setOnRefreshListener(() -> onRefreshListLayout(swipeEmptyListRefreshLayout));
AccountManager accountManager = AccountManager.get(getContext());
userId = accountManager.getUserData(account,
com.owncloud.android.lib.common.accounts.AccountUtils.Constants.KEY_USER_ID);
callback = new VersionListInterface.CommentCallback() {
@Override
public void onSuccess() {
commentInput.getText().clear();
fetchAndSetData(null);
}
@Override
public void onError(int error) {
Snackbar.make(recyclerView, error, Snackbar.LENGTH_LONG).show();
}
};
commentInput.getBackground().setColorFilter(
ThemeUtils.primaryAccentColor(getContext()),
PorterDuff.Mode.SRC_ATOP
);
return view;
}
@OnClick(R.id.submitComment)
public void submitComment() {
if (commentInput.getText().toString().trim().length() > 0) {
new SubmitCommentTask(commentInput.getText().toString(), userId, file.getLocalId(),
callback, ownCloudClient).execute();
}
}
private void onRefreshListLayout(SwipeRefreshLayout refreshLayout) {
setLoadingMessage();
if (refreshLayout != null && refreshLayout.isRefreshing()) {
@ -389,4 +423,43 @@ public class FileDetailActivitiesFragment extends Fragment implements ActivityLi
public void onRestoreClicked(FileVersion fileVersion) {
operationsHelper.restoreFileVersion(fileVersion, userId);
}
}
private static class SubmitCommentTask extends AsyncTask<Void, Void, Boolean> {
private String message;
private String userId;
private String fileId;
private VersionListInterface.CommentCallback callback;
private OwnCloudClient client;
private SubmitCommentTask(String message, String userId, String fileId,
VersionListInterface.CommentCallback callback, OwnCloudClient client) {
this.message = message;
this.userId = userId;
this.fileId = fileId;
this.callback = callback;
this.client = client;
}
@Override
protected Boolean doInBackground(Void... voids) {
CommentFileOperation commentFileOperation = new CommentFileOperation(message, fileId, userId);
RemoteOperationResult result = commentFileOperation.execute(client);
return result.isSuccess();
}
@Override
protected void onPostExecute(Boolean success) {
super.onPostExecute(success);
if (success) {
callback.onSuccess();
} else {
callback.onError(R.string.error_comment_file);
}
}
}
}

View file

@ -60,7 +60,7 @@ public class FileFragment extends Fragment {
super.onCreate(savedInstanceState);
Bundle bundle = getArguments();
setFile((OCFile) bundle.getParcelable(EXTRA_FILE));
setFile(bundle.getParcelable(EXTRA_FILE));
}
/**

View file

@ -28,4 +28,10 @@ public interface VersionListInterface {
interface View {
void onRestoreClicked(FileVersion fileVersion);
}
interface CommentCallback {
void onSuccess();
void onError(int error);
}
}

View file

@ -0,0 +1,25 @@
<!--
@author Google LLC
Copyright (C) 2018 Google LLC
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportHeight="24"
android:viewportWidth="24">
<path
android:fillColor="#757575"
android:pathData="M2,21L23,12L2,3V10L17,12L2,14V21Z" />
</vector>

View file

@ -18,36 +18,69 @@
License along with this program. If not, see <http://www.gnu.org/licenses/>.
-->
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_height="match_parent"
android:layout_width="match_parent">
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginEnd="@dimen/zero"
android:layout_marginLeft="@dimen/standard_padding"
android:layout_marginRight="@dimen/zero"
android:layout_marginStart="@dimen/standard_padding"
android:orientation="horizontal">
<android.support.design.widget.TextInputEditText
android:id="@+id/commentInputField"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:hint="@string/new_comment"
android:paddingTop="@dimen/standard_padding" />
<ImageButton
android:id="@+id/submitComment"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="?attr/selectableItemBackgroundBorderless"
android:contentDescription="@string/common_send"
android:paddingBottom="@dimen/standard_padding"
android:paddingEnd="@dimen/standard_padding"
android:paddingLeft="@dimen/standard_padding"
android:paddingRight="@dimen/standard_padding"
android:paddingStart="@dimen/standard_padding"
android:paddingTop="@dimen/standard_padding"
android:src="@drawable/ic_send" />
</LinearLayout>
<android.support.v4.widget.SwipeRefreshLayout
android:footerDividersEnabled="false"
android:id="@+id/swipe_containing_list"
android:layout_height="match_parent"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:footerDividersEnabled="false"
android:visibility="visible">
<android.support.v7.widget.RecyclerView
android:clipToPadding="false"
android:id="@android:id/list"
android:layout_height="match_parent"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clipToPadding="false"
android:scrollbarStyle="outsideOverlay"
android:scrollbars="vertical"
android:visibility="visible" />
android:visibility="visible"/>
</android.support.v4.widget.SwipeRefreshLayout>
<android.support.v4.widget.SwipeRefreshLayout
android:footerDividersEnabled="false"
android:id="@+id/swipe_containing_empty"
android:layout_height="match_parent"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:footerDividersEnabled="false"
android:visibility="visible">
<include layout="@layout/empty_list" />
<include layout="@layout/empty_list"/>
</android.support.v4.widget.SwipeRefreshLayout>
</FrameLayout>
</LinearLayout>

View file

@ -800,6 +800,8 @@
<string name="whats_new_device_credentials_content">Use anything like a pattern, password, pin or your fingerprint to keep your data safe.</string>
<string name="restore">Restore file</string>
<string name="new_version_was_created">New version was created</string>
<string name="new_comment">New comment…</string>
<string name="error_comment_file">Error commenting file</string>
<string name="file_version_restored_successfully">Successfully restored file version.</string>
<string name="file_version_restored_error">Error restoring file version!</string>
<string name="outdated_server">The server has reached end of life, please upgrade!</string>