|
|
@ -11,71 +11,104 @@ import android.content.DialogInterface;
|
|
|
|
import android.content.Intent;
|
|
|
|
import android.content.Intent;
|
|
|
|
import android.media.MediaPlayer;
|
|
|
|
import android.media.MediaPlayer;
|
|
|
|
import android.net.Uri;
|
|
|
|
import android.net.Uri;
|
|
|
|
import android.support.v4.app.FragmentActivity;
|
|
|
|
import android.support.v4.app.Fragment;
|
|
|
|
import android.text.TextUtils;
|
|
|
|
import android.text.TextUtils;
|
|
|
|
import android.view.LayoutInflater;
|
|
|
|
import android.view.LayoutInflater;
|
|
|
|
import android.view.View;
|
|
|
|
import android.view.View;
|
|
|
|
import android.view.View.OnClickListener;
|
|
|
|
import android.view.View.OnClickListener;
|
|
|
|
import android.webkit.MimeTypeMap;
|
|
|
|
import android.webkit.MimeTypeMap;
|
|
|
|
|
|
|
|
import android.widget.ArrayAdapter;
|
|
|
|
import android.widget.LinearLayout;
|
|
|
|
import android.widget.LinearLayout;
|
|
|
|
import android.widget.LinearLayout.LayoutParams;
|
|
|
|
|
|
|
|
import android.widget.TextView;
|
|
|
|
import android.widget.TextView;
|
|
|
|
import android.widget.Toast;
|
|
|
|
import android.widget.Toast;
|
|
|
|
|
|
|
|
|
|
|
|
import com.todoroo.andlib.data.Callback;
|
|
|
|
import com.todoroo.andlib.data.Callback;
|
|
|
|
import com.todoroo.andlib.utility.AndroidUtilities;
|
|
|
|
import com.todoroo.andlib.utility.AndroidUtilities;
|
|
|
|
import com.todoroo.andlib.utility.DateUtilities;
|
|
|
|
import com.todoroo.astrid.actfm.ActFmCameraModule;
|
|
|
|
|
|
|
|
import com.todoroo.astrid.activity.TaskEditFragment;
|
|
|
|
import com.todoroo.astrid.dao.TaskAttachmentDao;
|
|
|
|
import com.todoroo.astrid.dao.TaskAttachmentDao;
|
|
|
|
import com.todoroo.astrid.data.RemoteModel;
|
|
|
|
|
|
|
|
import com.todoroo.astrid.data.SyncFlags;
|
|
|
|
import com.todoroo.astrid.data.SyncFlags;
|
|
|
|
import com.todoroo.astrid.data.Task;
|
|
|
|
import com.todoroo.astrid.data.Task;
|
|
|
|
import com.todoroo.astrid.data.TaskAttachment;
|
|
|
|
import com.todoroo.astrid.data.TaskAttachment;
|
|
|
|
import com.todoroo.astrid.ui.PopupControlSet;
|
|
|
|
import com.todoroo.astrid.helper.TaskEditControlSetBase;
|
|
|
|
|
|
|
|
|
|
|
|
import org.slf4j.Logger;
|
|
|
|
import org.slf4j.Logger;
|
|
|
|
import org.slf4j.LoggerFactory;
|
|
|
|
import org.slf4j.LoggerFactory;
|
|
|
|
import org.tasks.R;
|
|
|
|
import org.tasks.R;
|
|
|
|
import org.tasks.dialogs.DialogBuilder;
|
|
|
|
import org.tasks.dialogs.DialogBuilder;
|
|
|
|
import org.tasks.preferences.ActivityPreferences;
|
|
|
|
import org.tasks.preferences.ActivityPreferences;
|
|
|
|
|
|
|
|
import org.tasks.preferences.DeviceInfo;
|
|
|
|
|
|
|
|
|
|
|
|
import java.io.File;
|
|
|
|
import java.io.File;
|
|
|
|
import java.util.ArrayList;
|
|
|
|
import java.util.ArrayList;
|
|
|
|
|
|
|
|
import java.util.List;
|
|
|
|
|
|
|
|
|
|
|
|
public class FilesControlSet extends PopupControlSet {
|
|
|
|
public class FilesControlSet extends TaskEditControlSetBase {
|
|
|
|
|
|
|
|
|
|
|
|
private static final Logger log = LoggerFactory.getLogger(FilesControlSet.class);
|
|
|
|
private static final Logger log = LoggerFactory.getLogger(FilesControlSet.class);
|
|
|
|
|
|
|
|
|
|
|
|
private final ArrayList<TaskAttachment> files = new ArrayList<>();
|
|
|
|
private final ArrayList<TaskAttachment> files = new ArrayList<>();
|
|
|
|
private final LinearLayout fileDisplayList;
|
|
|
|
|
|
|
|
private final LayoutInflater inflater;
|
|
|
|
private final LayoutInflater inflater;
|
|
|
|
|
|
|
|
private ActivityPreferences preferences;
|
|
|
|
private final TaskAttachmentDao taskAttachmentDao;
|
|
|
|
private final TaskAttachmentDao taskAttachmentDao;
|
|
|
|
|
|
|
|
private final Fragment fragment;
|
|
|
|
|
|
|
|
private final DeviceInfo deviceInfo;
|
|
|
|
|
|
|
|
private final ActFmCameraModule actFmCameraModule;
|
|
|
|
private final DialogBuilder dialogBuilder;
|
|
|
|
private final DialogBuilder dialogBuilder;
|
|
|
|
|
|
|
|
private LinearLayout attachmentContainer;
|
|
|
|
|
|
|
|
private TextView addAttachment;
|
|
|
|
|
|
|
|
|
|
|
|
public FilesControlSet(ActivityPreferences preferences, TaskAttachmentDao taskAttachmentDao,
|
|
|
|
public FilesControlSet(ActivityPreferences preferences, TaskAttachmentDao taskAttachmentDao,
|
|
|
|
FragmentActivity activity, DialogBuilder dialogBuilder) {
|
|
|
|
Fragment fragment, DeviceInfo deviceInfo, ActFmCameraModule actFmCameraModule) {
|
|
|
|
super(preferences, activity, R.layout.control_set_files_dialog, R.layout.control_set_files, R.string.TEA_control_files, dialogBuilder);
|
|
|
|
super(fragment.getActivity(), R.layout.control_set_files);
|
|
|
|
|
|
|
|
this.preferences = preferences;
|
|
|
|
this.taskAttachmentDao = taskAttachmentDao;
|
|
|
|
this.taskAttachmentDao = taskAttachmentDao;
|
|
|
|
|
|
|
|
this.fragment = fragment;
|
|
|
|
|
|
|
|
this.deviceInfo = deviceInfo;
|
|
|
|
|
|
|
|
this.actFmCameraModule = actFmCameraModule;
|
|
|
|
this.dialogBuilder = new DialogBuilder(activity, preferences);
|
|
|
|
this.dialogBuilder = new DialogBuilder(activity, preferences);
|
|
|
|
fileDisplayList = (LinearLayout) getView().findViewById(R.id.files_list);
|
|
|
|
|
|
|
|
inflater = (LayoutInflater) activity.getSystemService(Activity.LAYOUT_INFLATER_SERVICE);
|
|
|
|
inflater = (LayoutInflater) activity.getSystemService(Activity.LAYOUT_INFLATER_SERVICE);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
private void addAttachment(TaskAttachment taskAttachment) {
|
|
|
|
protected void refreshDisplayView() {
|
|
|
|
View fileRow = inflater.inflate(R.layout.file_row, null);
|
|
|
|
fileDisplayList.removeAllViews();
|
|
|
|
fileRow.setTag(taskAttachment);
|
|
|
|
for (final TaskAttachment m : files) {
|
|
|
|
attachmentContainer.addView(fileRow);
|
|
|
|
View fileRow = inflater.inflate(R.layout.file_display_row, null);
|
|
|
|
addAttachment(taskAttachment, fileRow);
|
|
|
|
LayoutParams lp = new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.WRAP_CONTENT);
|
|
|
|
|
|
|
|
setUpFileRow(m, fileRow, fileDisplayList, lp);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
private void addAttachment(final TaskAttachment taskAttachment, final View fileRow) {
|
|
|
|
public void readFromTask(Task task) {
|
|
|
|
TextView nameView = (TextView) fileRow.findViewById(R.id.file_text);
|
|
|
|
super.readFromTask(task);
|
|
|
|
nameView.setTextColor(themeColor);
|
|
|
|
|
|
|
|
String name = taskAttachment.getName();
|
|
|
|
refreshMetadata();
|
|
|
|
nameView.setText(name);
|
|
|
|
refreshDisplayView();
|
|
|
|
nameView.setOnClickListener(new OnClickListener() {
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
|
|
|
public void onClick(View v) {
|
|
|
|
|
|
|
|
showFile(taskAttachment);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
View clearFile = fileRow.findViewById(R.id.remove_file);
|
|
|
|
|
|
|
|
clearFile.setOnClickListener(new OnClickListener() {
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
|
|
|
public void onClick(View v) {
|
|
|
|
|
|
|
|
dialogBuilder.newMessageDialog(R.string.premium_remove_file_confirm)
|
|
|
|
|
|
|
|
.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
|
|
|
public void onClick(DialogInterface dialog, int which) {
|
|
|
|
|
|
|
|
taskAttachmentDao.delete(taskAttachment.getId());
|
|
|
|
|
|
|
|
if (taskAttachment.containsNonNullValue(TaskAttachment.FILE_PATH)) {
|
|
|
|
|
|
|
|
File f = new File(taskAttachment.getFilePath());
|
|
|
|
|
|
|
|
f.delete();
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
files.remove(taskAttachment);
|
|
|
|
|
|
|
|
attachmentContainer.removeView(fileRow);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
.setNegativeButton(android.R.string.cancel, null)
|
|
|
|
|
|
|
|
.show();
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
@Override
|
|
|
@ -112,12 +145,18 @@ public class FilesControlSet extends PopupControlSet {
|
|
|
|
i--;
|
|
|
|
i--;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
@Override
|
|
|
|
protected void readFromTaskOnInitialize() {
|
|
|
|
protected void readFromTaskOnInitialize() {
|
|
|
|
|
|
|
|
attachmentContainer.removeAllViews();
|
|
|
|
|
|
|
|
taskAttachmentDao.getAttachments(model.getUuid(), new Callback<TaskAttachment>() {
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
|
|
|
public void apply(TaskAttachment entry) {
|
|
|
|
|
|
|
|
addAttachment(entry);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
@Override
|
|
|
@ -127,54 +166,18 @@ public class FilesControlSet extends PopupControlSet {
|
|
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
@Override
|
|
|
|
protected void afterInflate() {
|
|
|
|
protected void afterInflate() {
|
|
|
|
LinearLayout fileList = (LinearLayout) getDialogView().findViewById(R.id.files_list);
|
|
|
|
attachmentContainer = (LinearLayout) getView().findViewById(R.id.attachment_container);
|
|
|
|
final LinearLayout finalList = fileList;
|
|
|
|
addAttachment = (TextView) getView().findViewById(R.id.add_attachment);
|
|
|
|
fileList.removeAllViews();
|
|
|
|
addAttachment.setOnClickListener(new OnClickListener() {
|
|
|
|
LayoutParams lp = new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.WRAP_CONTENT);
|
|
|
|
@Override
|
|
|
|
for (final TaskAttachment m : files) {
|
|
|
|
public void onClick(View arg0) {
|
|
|
|
final View fileRow = inflater.inflate(R.layout.file_row, null);
|
|
|
|
startAttachFile();
|
|
|
|
|
|
|
|
}
|
|
|
|
setUpFileRow(m, fileRow, fileList, lp);
|
|
|
|
});
|
|
|
|
View name = fileRow.findViewById(R.id.file_text);
|
|
|
|
}
|
|
|
|
View clearFile = fileRow.findViewById(R.id.remove_file);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
name.setOnClickListener(new OnClickListener() {
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
|
|
|
public void onClick(View v) {
|
|
|
|
|
|
|
|
showFile(m);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
clearFile.setVisibility(View.VISIBLE);
|
|
|
|
public void hideAddAttachmentButton() {
|
|
|
|
clearFile.setOnClickListener(new OnClickListener() {
|
|
|
|
addAttachment.setVisibility(View.GONE);
|
|
|
|
@Override
|
|
|
|
|
|
|
|
public void onClick(View v) {
|
|
|
|
|
|
|
|
dialogBuilder.newMessageDialog(R.string.premium_remove_file_confirm)
|
|
|
|
|
|
|
|
.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
|
|
|
public void onClick(DialogInterface dialog, int which) {
|
|
|
|
|
|
|
|
if (RemoteModel.isValidUuid(m.getUUID())) {
|
|
|
|
|
|
|
|
// TODO: delete
|
|
|
|
|
|
|
|
m.setDeletedAt(DateUtilities.now());
|
|
|
|
|
|
|
|
taskAttachmentDao.saveExisting(m);
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
taskAttachmentDao.delete(m.getId());
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (m.containsNonNullValue(TaskAttachment.FILE_PATH)) {
|
|
|
|
|
|
|
|
File f = new File(m.getFilePath());
|
|
|
|
|
|
|
|
f.delete();
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
files.remove(m);
|
|
|
|
|
|
|
|
refreshDisplayView();
|
|
|
|
|
|
|
|
finalList.removeView(fileRow);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
.setNegativeButton(android.R.string.cancel, null)
|
|
|
|
|
|
|
|
.show();
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public interface PlaybackExceptionHandler {
|
|
|
|
public interface PlaybackExceptionHandler {
|
|
|
@ -206,9 +209,14 @@ public class FilesControlSet extends PopupControlSet {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
});
|
|
|
|
} else if (fileType.startsWith(TaskAttachment.FILE_TYPE_IMAGE)) {
|
|
|
|
} else if (fileType.startsWith(TaskAttachment.FILE_TYPE_IMAGE)) {
|
|
|
|
activity.startActivity(new Intent(Intent.ACTION_VIEW) {{
|
|
|
|
try {
|
|
|
|
setDataAndType(Uri.fromFile(new File(filePath)), fileType);
|
|
|
|
activity.startActivity(new Intent(Intent.ACTION_VIEW) {{
|
|
|
|
}});
|
|
|
|
setDataAndType(Uri.fromFile(new File(filePath)), fileType);
|
|
|
|
|
|
|
|
}});
|
|
|
|
|
|
|
|
} catch(ActivityNotFoundException e) {
|
|
|
|
|
|
|
|
log.error(e.getMessage(), e);
|
|
|
|
|
|
|
|
Toast.makeText(activity, R.string.no_application_found, Toast.LENGTH_SHORT).show();
|
|
|
|
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
} else {
|
|
|
|
String useType = fileType;
|
|
|
|
String useType = fileType;
|
|
|
|
if (fileType.equals(TaskAttachment.FILE_TYPE_OTHER)) {
|
|
|
|
if (fileType.equals(TaskAttachment.FILE_TYPE_OTHER)) {
|
|
|
@ -240,37 +248,83 @@ public class FilesControlSet extends PopupControlSet {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private void setUpFileRow(TaskAttachment m, View row, LinearLayout parent, LayoutParams lp) {
|
|
|
|
private void startAttachFile() {
|
|
|
|
TextView nameView = (TextView) row.findViewById(R.id.file_text);
|
|
|
|
final List<Runnable> runnables = new ArrayList<>();
|
|
|
|
nameView.setTextColor(themeColor);
|
|
|
|
List<String> options = new ArrayList<>();
|
|
|
|
TextView typeView = (TextView) row.findViewById(R.id.file_type);
|
|
|
|
|
|
|
|
String name = getNameString(m);
|
|
|
|
|
|
|
|
String type = getTypeString(m.getName());
|
|
|
|
|
|
|
|
nameView.setText(name);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (TextUtils.isEmpty(type)) {
|
|
|
|
if (deviceInfo.hasCamera() || deviceInfo.hasGallery()) {
|
|
|
|
typeView.setVisibility(View.GONE);
|
|
|
|
runnables.add(new Runnable() {
|
|
|
|
} else {
|
|
|
|
@Override
|
|
|
|
typeView.setText(type);
|
|
|
|
public void run() {
|
|
|
|
|
|
|
|
actFmCameraModule.showPictureLauncher(null);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
options.add(activity.getString(R.string.file_add_picture));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
runnables.add(new Runnable() {
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
|
|
|
public void run() {
|
|
|
|
|
|
|
|
Intent attachFile = new Intent(activity, FileExplore.class);
|
|
|
|
|
|
|
|
fragment.startActivityForResult(attachFile, TaskEditFragment.REQUEST_CODE_ATTACH_FILE);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
options.add(activity.getString(R.string.file_add_sdcard));
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
ArrayAdapter<String> adapter = new ArrayAdapter<>(
|
|
|
|
|
|
|
|
activity,
|
|
|
|
|
|
|
|
android.R.layout.simple_spinner_dropdown_item,
|
|
|
|
|
|
|
|
options.toArray(new String[options.size()]));
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
DialogInterface.OnClickListener listener = new DialogInterface.OnClickListener() {
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
|
|
|
public void onClick(DialogInterface d, int which) {
|
|
|
|
|
|
|
|
runnables.get(which).run();
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
parent.addView(row, lp);
|
|
|
|
// show a menu of available options
|
|
|
|
|
|
|
|
dialogBuilder.newDialog()
|
|
|
|
|
|
|
|
.setAdapter(adapter, listener)
|
|
|
|
|
|
|
|
.show()
|
|
|
|
|
|
|
|
.setOwnerActivity(activity);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private String getNameString(TaskAttachment metadata) {
|
|
|
|
public void attachFile(String file) {
|
|
|
|
String name = metadata.getName();
|
|
|
|
File src = new File(file);
|
|
|
|
int extension = name.lastIndexOf('.');
|
|
|
|
if (!src.exists()) {
|
|
|
|
if (extension < 0) {
|
|
|
|
Toast.makeText(activity, R.string.file_err_copy, Toast.LENGTH_LONG).show();
|
|
|
|
return name;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return name.substring(0, extension);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private String getTypeString(String name) {
|
|
|
|
File dst = new File(preferences.getAttachmentsDirectory() + File.separator + src.getName());
|
|
|
|
int extension = name.lastIndexOf('.');
|
|
|
|
try {
|
|
|
|
if (extension < 0 || extension + 1 >= name.length()) {
|
|
|
|
AndroidUtilities.copyFile(src, dst);
|
|
|
|
return "";
|
|
|
|
} catch (Exception e) {
|
|
|
|
|
|
|
|
log.error(e.getMessage(), e);
|
|
|
|
|
|
|
|
Toast.makeText(activity, R.string.file_err_copy, Toast.LENGTH_LONG).show();
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
String path = dst.getAbsolutePath();
|
|
|
|
|
|
|
|
String name = dst.getName();
|
|
|
|
|
|
|
|
String extension = AndroidUtilities.getFileExtension(name);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
String type = TaskAttachment.FILE_TYPE_OTHER;
|
|
|
|
|
|
|
|
if (!TextUtils.isEmpty(extension)) {
|
|
|
|
|
|
|
|
MimeTypeMap map = MimeTypeMap.getSingleton();
|
|
|
|
|
|
|
|
String guessedType = map.getMimeTypeFromExtension(extension);
|
|
|
|
|
|
|
|
if (!TextUtils.isEmpty(guessedType)) {
|
|
|
|
|
|
|
|
type = guessedType;
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return name.substring(extension + 1).toUpperCase();
|
|
|
|
|
|
|
|
|
|
|
|
createNewFileAttachment(path, name, type);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public void createNewFileAttachment(String path, String fileName, String fileType) {
|
|
|
|
|
|
|
|
TaskAttachment attachment = TaskAttachment.createNewAttachment(model.getUuid(), path, fileName, fileType);
|
|
|
|
|
|
|
|
taskAttachmentDao.createNew(attachment);
|
|
|
|
|
|
|
|
refreshMetadata();
|
|
|
|
|
|
|
|
addAttachment(attachment);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|