Merge pull request #8005 from nextcloud/template

Template Dialog: template needs to be selected
This commit is contained in:
Tobias Kaminsky 2021-02-19 14:55:00 +01:00 committed by GitHub
commit afc6b720c3
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
24 changed files with 357 additions and 160 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.6 KiB

After

Width:  |  Height:  |  Size: 6.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.7 KiB

After

Width:  |  Height:  |  Size: 6.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.2 KiB

After

Width:  |  Height:  |  Size: 6.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.4 KiB

After

Width:  |  Height:  |  Size: 6.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.3 KiB

After

Width:  |  Height:  |  Size: 9.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.4 KiB

After

Width:  |  Height:  |  Size: 9.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.8 KiB

After

Width:  |  Height:  |  Size: 9.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.1 KiB

After

Width:  |  Height:  |  Size: 9 KiB

View file

@ -1,2 +1,2 @@
DO NOT TOUCH; GENERATED BY DRONE
<span class="mdl-layout-title">Lint Report: 240 warnings</span>
<span class="mdl-layout-title">Lint Report: 232 warnings</span>

View file

@ -278,7 +278,7 @@ public class DialogFragmentIT extends AbstractIT {
}
@Override
public void showTemplate(Creator creator) {
public void showTemplate(Creator creator, String headline) {
}

View file

@ -93,7 +93,7 @@ public class PassCodeActivity extends AppCompatActivity implements Injectable {
int elementColor = ThemeUtils.primaryColor(this, true);
ThemeUtils.themeDialogActionButton(binding.cancel);
ThemeUtils.themeBorderlessButton(binding.cancel, ThemeUtils.primaryColor(this, true));
passCodeEditTexts[0] = binding.txt0;
ThemeUtils.colorEditText(passCodeEditTexts[0], elementColor);

View file

@ -26,16 +26,16 @@ import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
import com.bumptech.glide.Glide;
import com.nextcloud.client.account.CurrentAccountProvider;
import com.nextcloud.client.network.ClientFactory;
import com.owncloud.android.R;
import com.owncloud.android.databinding.TemplateButtonBinding;
import com.owncloud.android.datamodel.Template;
import com.owncloud.android.ui.dialog.ChooseRichDocumentsTemplateDialogFragment;
import com.owncloud.android.utils.NextcloudServer;
import com.owncloud.android.utils.ThemeUtils;
import com.owncloud.android.utils.glide.CustomGlideStreamLoader;
import java.util.ArrayList;
@ -43,8 +43,6 @@ import java.util.List;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import butterknife.BindView;
import butterknife.ButterKnife;
/**
* Adapter for handling Templates, used to create files out of it via RichDocuments app
@ -57,6 +55,9 @@ public class RichDocumentsTemplateAdapter extends RecyclerView.Adapter<RichDocum
private ChooseRichDocumentsTemplateDialogFragment.Type type;
private CurrentAccountProvider currentAccountProvider;
private ClientFactory clientFactory;
private Template selectedTemplate;
private final int colorSelected;
private final int colorUnselected;
public RichDocumentsTemplateAdapter(
ChooseRichDocumentsTemplateDialogFragment.Type type,
@ -70,13 +71,19 @@ public class RichDocumentsTemplateAdapter extends RecyclerView.Adapter<RichDocum
this.context = context;
this.currentAccountProvider = currentAccountProvider;
this.clientFactory = clientFactory;
colorSelected = ThemeUtils.primaryColor(context, true);
colorUnselected = context.getResources().getColor(R.color.grey_200);
}
@NonNull
@Override
@NextcloudServer(max = 18) // remove entire class
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
return new ViewHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.template_button, parent, false));
return new RichDocumentsTemplateAdapter.ViewHolder(
TemplateButtonBinding.inflate(LayoutInflater.from(parent.getContext()),
parent,
false)
);
}
@Override
@ -88,6 +95,15 @@ public class RichDocumentsTemplateAdapter extends RecyclerView.Adapter<RichDocum
this.templateList = templateList;
}
public void setTemplateAsActive(Template template) {
selectedTemplate = template;
notifyDataSetChanged();
}
public Template getSelectedTemplate() {
return selectedTemplate;
}
@Override
public int getItemCount() {
return templateList.size();
@ -95,17 +111,12 @@ public class RichDocumentsTemplateAdapter extends RecyclerView.Adapter<RichDocum
public class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
@BindView(R.id.name)
public TextView name;
@BindView(R.id.thumbnail)
public ImageView thumbnail;
private final TemplateButtonBinding binding;
private Template template;
public ViewHolder(View itemView) {
super(itemView);
ButterKnife.bind(this, itemView);
public ViewHolder(@NonNull TemplateButtonBinding binding) {
super(binding.getRoot());
this.binding = binding;
itemView.setOnClickListener(this);
}
@ -143,9 +154,15 @@ public class RichDocumentsTemplateAdapter extends RecyclerView.Adapter<RichDocum
load(template.getThumbnailLink())
.placeholder(placeholder)
.error(placeholder)
.into(thumbnail);
.into(binding.template);
name.setText(template.getName());
binding.templateName.setText(template.getName());
if (template == selectedTemplate) {
binding.templateContainer.setStrokeColor(colorSelected);
} else {
binding.templateContainer.setStrokeColor(colorUnselected);
}
}
}

View file

@ -29,22 +29,20 @@ import android.graphics.drawable.Drawable;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
import com.bumptech.glide.Glide;
import com.nextcloud.client.account.CurrentAccountProvider;
import com.nextcloud.client.network.ClientFactory;
import com.owncloud.android.R;
import com.owncloud.android.databinding.TemplateButtonBinding;
import com.owncloud.android.lib.common.Template;
import com.owncloud.android.lib.common.TemplateList;
import com.owncloud.android.utils.MimeTypeUtil;
import com.owncloud.android.utils.ThemeUtils;
import com.owncloud.android.utils.glide.CustomGlideStreamLoader;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import butterknife.BindView;
import butterknife.ButterKnife;
/**
* Adapter for handling Templates, used to create files out of it via RichDocuments app
@ -57,6 +55,9 @@ public class TemplateAdapter extends RecyclerView.Adapter<TemplateAdapter.ViewHo
private CurrentAccountProvider currentAccountProvider;
private ClientFactory clientFactory;
private String mimetype;
private Template selectedTemplate;
private final int colorSelected;
private final int colorUnselected;
public TemplateAdapter(
String mimetype,
@ -70,12 +71,18 @@ public class TemplateAdapter extends RecyclerView.Adapter<TemplateAdapter.ViewHo
this.context = context;
this.currentAccountProvider = currentAccountProvider;
this.clientFactory = clientFactory;
colorSelected = ThemeUtils.primaryColor(context, true);
colorUnselected = context.getResources().getColor(R.color.grey_200);
}
@NonNull
@Override
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
return new ViewHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.template_button, parent, false));
return new TemplateAdapter.ViewHolder(
TemplateButtonBinding.inflate(LayoutInflater.from(parent.getContext()),
parent,
false)
);
}
@Override
@ -87,23 +94,28 @@ public class TemplateAdapter extends RecyclerView.Adapter<TemplateAdapter.ViewHo
this.templateList = templateList;
}
public void setTemplateAsActive(Template template) {
selectedTemplate = template;
notifyDataSetChanged();
}
public Template getSelectedTemplate() {
return selectedTemplate;
}
@Override
public int getItemCount() {
return templateList.getTemplateList().size();
}
public class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
@BindView(R.id.name)
public TextView name;
@BindView(R.id.thumbnail)
public ImageView thumbnail;
private final TemplateButtonBinding binding;
private Template template;
public ViewHolder(View itemView) {
super(itemView);
ButterKnife.bind(this, itemView);
public ViewHolder(@NonNull TemplateButtonBinding binding) {
super(binding.getRoot());
this.binding = binding;
itemView.setOnClickListener(this);
}
@ -124,11 +136,17 @@ public class TemplateAdapter extends RecyclerView.Adapter<TemplateAdapter.ViewHo
Glide.with(context).using(new CustomGlideStreamLoader(currentAccountProvider, clientFactory))
.load(template.getPreview())
.placeholder(placeholder)
.error(placeholder)
.into(thumbnail);
.placeholder(placeholder)
.error(placeholder)
.into(binding.template);
name.setText(template.getTitle());
binding.templateName.setText(template.getTitle());
if (template == selectedTemplate) {
binding.templateContainer.setStrokeColor(colorSelected);
} else {
binding.templateContainer.setStrokeColor(colorUnselected);
}
}
}

View file

@ -22,19 +22,16 @@
package com.owncloud.android.ui.dialog;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.app.Dialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.graphics.PorterDuff;
import android.os.AsyncTask;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.Window;
import android.view.WindowManager.LayoutParams;
import android.widget.EditText;
import android.widget.Button;
import com.nextcloud.client.account.CurrentAccountProvider;
import com.nextcloud.client.account.User;
@ -42,6 +39,7 @@ import com.nextcloud.client.di.Injectable;
import com.nextcloud.client.network.ClientFactory;
import com.owncloud.android.MainApp;
import com.owncloud.android.R;
import com.owncloud.android.databinding.ChooseTemplateBinding;
import com.owncloud.android.datamodel.FileDataStorageManager;
import com.owncloud.android.datamodel.OCFile;
import com.owncloud.android.datamodel.Template;
@ -72,26 +70,25 @@ import androidx.annotation.NonNull;
import androidx.appcompat.app.AlertDialog;
import androidx.fragment.app.DialogFragment;
import androidx.recyclerview.widget.GridLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import butterknife.BindView;
import butterknife.ButterKnife;
/**
* Dialog to show templates for new documents/spreadsheets/presentations.
*/
public class ChooseRichDocumentsTemplateDialogFragment extends DialogFragment implements DialogInterface.OnClickListener,
public class ChooseRichDocumentsTemplateDialogFragment extends DialogFragment implements View.OnClickListener,
RichDocumentsTemplateAdapter.ClickListener, Injectable {
private static final String ARG_PARENT_FOLDER = "PARENT_FOLDER";
private static final String ARG_TYPE = "TYPE";
private static final String TAG = ChooseRichDocumentsTemplateDialogFragment.class.getSimpleName();
private static final String DOT = ".";
public static final int SINGLE_TEMPLATE = 1;
private RichDocumentsTemplateAdapter adapter;
private OCFile parentFolder;
private OwnCloudClient client;
@Inject CurrentAccountProvider currentAccount;
@Inject ClientFactory clientFactory;
private Button positiveButton;
public enum Type {
DOCUMENT,
@ -99,11 +96,7 @@ public class ChooseRichDocumentsTemplateDialogFragment extends DialogFragment im
PRESENTATION
}
@BindView(R.id.list)
RecyclerView listView;
@BindView(R.id.filename)
EditText fileName;
ChooseTemplateBinding binding;
@NextcloudServer(max = 18) // will be removed in favor of generic direct editing
public static ChooseRichDocumentsTemplateDialogFragment newInstance(OCFile parentFolder, Type type) {
@ -113,7 +106,6 @@ public class ChooseRichDocumentsTemplateDialogFragment extends DialogFragment im
args.putString(ARG_TYPE, type.name());
frag.setArguments(args);
return frag;
}
@Override
@ -124,8 +116,14 @@ public class ChooseRichDocumentsTemplateDialogFragment extends DialogFragment im
AlertDialog alertDialog = (AlertDialog) getDialog();
alertDialog.getButton(AlertDialog.BUTTON_POSITIVE).setTextColor(color);
alertDialog.getButton(AlertDialog.BUTTON_NEGATIVE).setTextColor(color);
positiveButton = alertDialog.getButton(AlertDialog.BUTTON_POSITIVE);
ThemeUtils.themeBorderlessButton(positiveButton, color);
positiveButton.setOnClickListener(this);
positiveButton.setEnabled(false);
ThemeUtils.themeBorderlessButton(alertDialog.getButton(AlertDialog.BUTTON_NEUTRAL), color);
checkEnablingCreateButton();
}
@NonNull
@ -141,37 +139,36 @@ public class ChooseRichDocumentsTemplateDialogFragment extends DialogFragment im
throw new IllegalArgumentException("Activity may not be null");
}
int accentColor = ThemeUtils.primaryAccentColor(getContext());
parentFolder = arguments.getParcelable(ARG_PARENT_FOLDER);
// Inflate the layout for the dialog
LayoutInflater inflater = activity.getLayoutInflater();
@SuppressLint("InflateParams") View view = inflater.inflate(R.layout.choose_template, null);
ButterKnife.bind(this, view);
fileName.requestFocus();
fileName.getBackground().setColorFilter(accentColor, PorterDuff.Mode.SRC_ATOP);
try {
client = clientFactory.create(currentAccount.getUser());
} catch (ClientFactory.CreationException e) {
throw new RuntimeException(e); // we'll NPE without the client
}
parentFolder = arguments.getParcelable(ARG_PARENT_FOLDER);
// Inflate the layout for the dialog
LayoutInflater inflater = requireActivity().getLayoutInflater();
binding = ChooseTemplateBinding.inflate(inflater, null, false);
View view = binding.getRoot();
binding.filename.requestFocus();
ThemeUtils.colorTextInput(binding.filenameContainer, binding.filename, ThemeUtils.primaryColor(getContext()));
Type type = Type.valueOf(arguments.getString(ARG_TYPE));
new FetchTemplateTask(this, client).execute(type);
listView.setHasFixedSize(true);
listView.setLayoutManager(new GridLayoutManager(activity, 2));
binding.list.setHasFixedSize(true);
binding.list.setLayoutManager(new GridLayoutManager(activity, 2));
adapter = new RichDocumentsTemplateAdapter(type, this, getContext(), currentAccount, clientFactory);
listView.setAdapter(adapter);
binding.list.setAdapter(adapter);
// Build the dialog
AlertDialog.Builder builder = new AlertDialog.Builder(activity);
builder.setView(view)
.setNegativeButton(R.string.common_cancel, this)
.setTitle(R.string.select_template);
.setPositiveButton(R.string.create, null)
.setNeutralButton(R.string.common_cancel, null)
.setTitle(getTitle(type));
Dialog dialog = builder.create();
Window window = dialog.getWindow();
@ -183,6 +180,24 @@ public class ChooseRichDocumentsTemplateDialogFragment extends DialogFragment im
return dialog;
}
private int getTitle(Type type) {
if (type == Type.DOCUMENT) {
return R.string.create_new_document;
} else if (type == Type.SPREADSHEET) {
return R.string.create_new_spreadsheet;
} else if (type == Type.PRESENTATION) {
return R.string.create_new_presentation;
}
return R.string.select_template;
}
@Override
public void onDestroyView() {
super.onDestroyView();
binding = null;
}
private void createFromTemplate(Template template, String path) {
new CreateFileFromTemplateTask(this, client, template, path, currentAccount.getUser()).execute();
}
@ -193,22 +208,48 @@ public class ChooseRichDocumentsTemplateDialogFragment extends DialogFragment im
}
@Override
public void onClick(Template template) {
String name = fileName.getText().toString();
public void onClick(View v) {
String name = binding.filename.getText().toString();
String path = parentFolder.getRemotePath() + name;
if (name.isEmpty() || name.equalsIgnoreCase(DOT + template.getExtension())) {
DisplayUtils.showSnackMessage(listView, R.string.enter_filename);
} else if (!name.endsWith(template.getExtension())) {
createFromTemplate(template, path + DOT + template.getExtension());
Template selectedTemplate = adapter.getSelectedTemplate();
if (selectedTemplate == null) {
DisplayUtils.showSnackMessage(binding.list, R.string.select_one_template);
} else if (name.isEmpty() || name.equalsIgnoreCase(DOT + selectedTemplate.getExtension())) {
DisplayUtils.showSnackMessage(binding.list, R.string.enter_filename);
} else if (!name.endsWith(selectedTemplate.getExtension())) {
createFromTemplate(selectedTemplate, path + DOT + selectedTemplate.getExtension());
} else {
createFromTemplate(template, path);
createFromTemplate(selectedTemplate, path);
}
}
@Override
public void onClick(DialogInterface dialog, int which) {
// cancel is handled by dialog itself, no other button available
public void onClick(Template template) {
onTemplateChosen(template);
}
private void onTemplateChosen(Template template) {
adapter.setTemplateAsActive(template);
prefillFilenameIfEmpty(template);
checkEnablingCreateButton();
}
private void prefillFilenameIfEmpty(Template template) {
String name = binding.filename.getText().toString();
if (name.isEmpty() || name.equalsIgnoreCase(DOT + template.getExtension())) {
binding.filename.setText(String.format("%s.%s", template.name, template.extension));
}
binding.filename.setSelection(binding.filename.getText().toString().lastIndexOf('.'));
}
private void checkEnablingCreateButton() {
Template selectedTemplate = adapter.getSelectedTemplate();
String name = binding.filename.getText().toString();
positiveButton.setEnabled(selectedTemplate != null && !name.isEmpty() &&
!name.equalsIgnoreCase(DOT + selectedTemplate.getExtension()));
}
private static class CreateFileFromTemplateTask extends AsyncTask<Void, Void, String> {
@ -268,7 +309,7 @@ public class ChooseRichDocumentsTemplateDialogFragment extends DialogFragment im
if (fragment != null && fragment.isAdded()) {
if (url.isEmpty()) {
DisplayUtils.showSnackMessage(fragment.listView, "Error creating file from template");
DisplayUtils.showSnackMessage(fragment.binding.list, "Error creating file from template");
} else {
Intent collaboraWebViewIntent = new Intent(MainApp.getAppContext(), RichDocumentsEditorWebView.class);
collaboraWebViewIntent.putExtra(ExternalSiteWebView.EXTRA_TITLE, "Collabora");
@ -319,12 +360,18 @@ public class ChooseRichDocumentsTemplateDialogFragment extends DialogFragment im
if (fragment != null) {
if (templateList.isEmpty()) {
DisplayUtils.showSnackMessage(fragment.listView, R.string.error_retrieving_templates);
DisplayUtils.showSnackMessage(fragment.binding.list, R.string.error_retrieving_templates);
} else {
fragment.setTemplateList(templateList);
if (templateList.size() == SINGLE_TEMPLATE) {
fragment.onTemplateChosen(templateList.get(0));
fragment.binding.list.setVisibility(View.GONE);
} else {
String name = DOT + templateList.get(0).getExtension();
fragment.binding.filename.setText(name);
fragment.binding.helperText.setVisibility(View.VISIBLE);
}
String name = DOT + templateList.get(0).getExtension();
fragment.fileName.setText(name);
fragment.setTemplateList(templateList);
}
} else {
Log_OC.e(TAG, "Error streaming file: no previewMediaFragment!");

View file

@ -24,20 +24,19 @@
package com.owncloud.android.ui.dialog;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.app.Dialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.graphics.PorterDuff;
import android.os.AsyncTask;
import android.os.Bundle;
import android.text.Editable;
import android.text.TextWatcher;
import android.view.LayoutInflater;
import android.view.View;
import android.view.Window;
import android.view.WindowManager.LayoutParams;
import android.widget.EditText;
import android.widget.Button;
import com.nextcloud.android.lib.resources.directediting.DirectEditingCreateFileRemoteOperation;
import com.nextcloud.android.lib.resources.directediting.DirectEditingObtainListOfTemplatesRemoteOperation;
@ -47,6 +46,7 @@ import com.nextcloud.client.di.Injectable;
import com.nextcloud.client.network.ClientFactory;
import com.owncloud.android.MainApp;
import com.owncloud.android.R;
import com.owncloud.android.databinding.ChooseTemplateBinding;
import com.owncloud.android.datamodel.FileDataStorageManager;
import com.owncloud.android.datamodel.OCFile;
import com.owncloud.android.lib.common.Creator;
@ -72,26 +72,27 @@ import androidx.annotation.NonNull;
import androidx.appcompat.app.AlertDialog;
import androidx.fragment.app.DialogFragment;
import androidx.recyclerview.widget.GridLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import butterknife.BindView;
import butterknife.ButterKnife;
/**
* Dialog to show templates for new documents/spreadsheets/presentations.
*/
public class ChooseTemplateDialogFragment extends DialogFragment implements DialogInterface.OnClickListener,
public class ChooseTemplateDialogFragment extends DialogFragment implements View.OnClickListener,
TemplateAdapter.ClickListener, Injectable {
private static final String ARG_PARENT_FOLDER = "PARENT_FOLDER";
private static final String ARG_CREATOR = "CREATOR";
private static final String ARG_HEADLINE = "HEADLINE";
private static final String TAG = ChooseTemplateDialogFragment.class.getSimpleName();
private static final String DOT = ".";
public static final int SINGLE_TEMPLATE = 1;
private TemplateAdapter adapter;
private OCFile parentFolder;
private String title;
@Inject ClientFactory clientFactory;
private Creator creator;
@Inject CurrentAccountProvider currentAccount;
private Button positiveButton;
public enum Type {
DOCUMENT,
@ -99,20 +100,16 @@ public class ChooseTemplateDialogFragment extends DialogFragment implements Dial
PRESENTATION
}
@BindView(R.id.list)
RecyclerView listView;
ChooseTemplateBinding binding;
@BindView(R.id.filename)
EditText fileName;
public static ChooseTemplateDialogFragment newInstance(OCFile parentFolder, Creator creator) {
public static ChooseTemplateDialogFragment newInstance(OCFile parentFolder, Creator creator, String headline) {
ChooseTemplateDialogFragment frag = new ChooseTemplateDialogFragment();
Bundle args = new Bundle();
args.putParcelable(ARG_PARENT_FOLDER, parentFolder);
args.putParcelable(ARG_CREATOR, creator);
args.putString(ARG_HEADLINE, headline);
frag.setArguments(args);
return frag;
}
@Override
@ -123,8 +120,14 @@ public class ChooseTemplateDialogFragment extends DialogFragment implements Dial
AlertDialog alertDialog = (AlertDialog) getDialog();
alertDialog.getButton(AlertDialog.BUTTON_POSITIVE).setTextColor(color);
alertDialog.getButton(AlertDialog.BUTTON_NEGATIVE).setTextColor(color);
positiveButton = alertDialog.getButton(AlertDialog.BUTTON_POSITIVE);
ThemeUtils.themeBorderlessButton(positiveButton, color);
positiveButton.setOnClickListener(this);
positiveButton.setEnabled(false);
ThemeUtils.themeBorderlessButton(alertDialog.getButton(AlertDialog.BUTTON_NEUTRAL), color);
checkEnablingCreateButton();
}
@NonNull
@ -140,18 +143,45 @@ public class ChooseTemplateDialogFragment extends DialogFragment implements Dial
throw new IllegalArgumentException("Activity may not be null");
}
int accentColor = ThemeUtils.primaryAccentColor(getContext());
parentFolder = arguments.getParcelable(ARG_PARENT_FOLDER);
creator = arguments.getParcelable(ARG_CREATOR);
title = arguments.getString(ARG_HEADLINE, getString(R.string.select_template));
if (savedInstanceState == null) {
title = arguments.getString(ARG_HEADLINE);
} else {
title = savedInstanceState.getString(ARG_HEADLINE);
}
// Inflate the layout for the dialog
LayoutInflater inflater = activity.getLayoutInflater();
@SuppressLint("InflateParams") View view = inflater.inflate(R.layout.choose_template, null);
ButterKnife.bind(this, view);
LayoutInflater inflater = requireActivity().getLayoutInflater();
binding = ChooseTemplateBinding.inflate(inflater, null, false);
View view = binding.getRoot();
fileName.requestFocus();
fileName.getBackground().setColorFilter(accentColor, PorterDuff.Mode.SRC_ATOP);
binding.filename.requestFocus();
ThemeUtils.colorTextInput(binding.filenameContainer, binding.filename, ThemeUtils.primaryColor(getContext()));
binding.filename.setOnKeyListener((v, keyCode, event) -> {
checkEnablingCreateButton();
return false;
});
binding.filename.addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
// generated method stub
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
// generated method stub
}
@Override
public void afterTextChanged(Editable s) {
checkEnablingCreateButton();
}
});
try {
User user = currentAccount.getUser();
@ -160,16 +190,17 @@ public class ChooseTemplateDialogFragment extends DialogFragment implements Dial
Log_OC.e(TAG, "Loading stream url not possible: " + e);
}
listView.setHasFixedSize(true);
listView.setLayoutManager(new GridLayoutManager(activity, 2));
binding.list.setHasFixedSize(true);
binding.list.setLayoutManager(new GridLayoutManager(activity, 2));
adapter = new TemplateAdapter(creator.getMimetype(), this, getContext(), currentAccount, clientFactory);
listView.setAdapter(adapter);
binding.list.setAdapter(adapter);
// Build the dialog
AlertDialog.Builder builder = new AlertDialog.Builder(activity);
builder.setView(view)
.setNegativeButton(R.string.common_cancel, this)
.setTitle(R.string.select_template);
.setPositiveButton(R.string.create, null)
.setNeutralButton(R.string.common_cancel, null)
.setTitle(title);
Dialog dialog = builder.create();
Window window = dialog.getWindow();
@ -181,6 +212,18 @@ public class ChooseTemplateDialogFragment extends DialogFragment implements Dial
return dialog;
}
@Override
public void onDestroyView() {
super.onDestroyView();
binding = null;
}
@Override
public void onSaveInstanceState(@NonNull Bundle savedInstanceState) {
super.onSaveInstanceState(savedInstanceState);
savedInstanceState.putString(ARG_HEADLINE, title);
}
private void createFromTemplate(Template template, String path) {
new CreateFileFromTemplateTask(this, clientFactory, currentAccount.getUser(), template, path, creator).execute();
}
@ -192,21 +235,47 @@ public class ChooseTemplateDialogFragment extends DialogFragment implements Dial
@Override
public void onClick(Template template) {
String name = fileName.getText().toString();
String path = parentFolder.getRemotePath() + name;
onTemplateChosen(template);
}
private void onTemplateChosen(Template template) {
adapter.setTemplateAsActive(template);
prefillFilenameIfEmpty(template);
checkEnablingCreateButton();
}
private void prefillFilenameIfEmpty(Template template) {
String name = binding.filename.getText().toString();
if (name.isEmpty() || name.equalsIgnoreCase(DOT + template.getExtension())) {
DisplayUtils.showSnackMessage(listView, R.string.enter_filename);
} else if (!name.endsWith(template.getExtension())) {
createFromTemplate(template, path + DOT + template.getExtension());
} else {
createFromTemplate(template, path);
binding.filename.setText(String.format("%s.%s", template.title, template.extension));
}
binding.filename.setSelection(binding.filename.getText().toString().lastIndexOf('.'));
}
@Override
public void onClick(DialogInterface dialog, int which) {
// cancel is handled by dialog itself, no other button available
public void onClick(View v) {
String name = binding.filename.getText().toString();
String path = parentFolder.getRemotePath() + name;
Template selectedTemplate = adapter.getSelectedTemplate();
if (selectedTemplate == null) {
DisplayUtils.showSnackMessage(binding.list, R.string.select_one_template);
} else if (name.isEmpty() || name.equalsIgnoreCase(DOT + selectedTemplate.getExtension())) {
DisplayUtils.showSnackMessage(binding.list, R.string.enter_filename);
} else if (!name.endsWith(selectedTemplate.getExtension())) {
createFromTemplate(selectedTemplate, path + DOT + selectedTemplate.getExtension());
} else {
createFromTemplate(selectedTemplate, path);
}
}
private void checkEnablingCreateButton() {
Template selectedTemplate = adapter.getSelectedTemplate();
String name = binding.filename.getText().toString();
positiveButton.setEnabled(selectedTemplate != null && !name.isEmpty() &&
!name.equalsIgnoreCase(DOT + selectedTemplate.getExtension()));
}
private static class CreateFileFromTemplateTask extends AsyncTask<Void, Void, String> {
@ -284,7 +353,7 @@ public class ChooseTemplateDialogFragment extends DialogFragment implements Dial
if (fragment != null && fragment.isAdded()) {
if (url.isEmpty()) {
DisplayUtils.showSnackMessage(fragment.listView, "Error creating file from template");
DisplayUtils.showSnackMessage(fragment.binding.list, "Error creating file from template");
} else {
Intent editorWebView = new Intent(MainApp.getAppContext(), TextEditorWebView.class);
editorWebView.putExtra(ExternalSiteWebView.EXTRA_TITLE, "Text");
@ -345,12 +414,18 @@ public class ChooseTemplateDialogFragment extends DialogFragment implements Dial
if (fragment != null && fragment.isAdded()) {
if (templateList.templates.isEmpty()) {
DisplayUtils.showSnackMessage(fragment.listView, R.string.error_retrieving_templates);
DisplayUtils.showSnackMessage(fragment.binding.list, R.string.error_retrieving_templates);
} else {
fragment.setTemplateList(templateList);
if (templateList.templates.size() == SINGLE_TEMPLATE) {
fragment.onTemplateChosen(templateList.templates.values().iterator().next());
fragment.binding.list.setVisibility(View.GONE);
} else {
String name = DOT + templateList.templates.values().iterator().next().getExtension();
fragment.binding.filename.setText(name);
fragment.binding.helperText.setVisibility(View.VISIBLE);
}
String name = DOT + templateList.templates.values().iterator().next().getExtension();
fragment.fileName.setText(name);
fragment.setTemplateList(templateList);
}
} else {
Log_OC.e(TAG, "Error streaming file: no previewMediaFragment!");

View file

@ -202,10 +202,10 @@ public class SyncedFolderPreferencesDialogFragment extends DialogFragment {
mNameCollisionPolicySummary = view.findViewById(R.id.setting_instant_name_collision_policy_summary);
mCancel = view.findViewById(R.id.cancel);
ThemeUtils.themeDialogActionButton(mCancel);
ThemeUtils.themeBorderlessButton(mCancel, accentColor);
mSave = view.findViewById(R.id.save);
ThemeUtils.themeDialogActionButton(mSave);
ThemeUtils.themeBorderlessButton(mSave, accentColor);
// Set values
setEnabled(mSyncedFolder.isEnabled());

View file

@ -65,7 +65,7 @@ public interface OCFileListBottomSheetActions {
/**
* open template selection for creator @link Creator
*/
void showTemplate(Creator creator);
void showTemplate(Creator creator, String headline);
/**
* open editor for rich workspace

View file

@ -121,7 +121,7 @@ public class OCFileListBottomSheetDialog extends BottomSheetDialog {
getContext()));
creatorView.setOnClickListener(v -> {
actions.showTemplate(creator);
actions.showTemplate(creator, creatorViewBinding.creatorName.getText().toString());
dismiss();
});

View file

@ -565,8 +565,8 @@ public class OCFileListFragment extends ExtendedListFragment implements
}
@Override
public void showTemplate(Creator creator) {
ChooseTemplateDialogFragment.newInstance(mFile, creator).show(requireActivity().getSupportFragmentManager(),
public void showTemplate(Creator creator, String headline) {
ChooseTemplateDialogFragment.newInstance(mFile, creator, headline).show(requireActivity().getSupportFragmentManager(),
DIALOG_CREATE_DOCUMENT);
}

View file

@ -45,7 +45,6 @@ import android.widget.ImageView;
import android.widget.ProgressBar;
import android.widget.SeekBar;
import com.google.android.material.button.MaterialButton;
import com.google.android.material.floatingactionbutton.FloatingActionButton;
import com.google.android.material.snackbar.Snackbar;
import com.google.android.material.tabs.TabLayout;
@ -569,13 +568,12 @@ public final class ThemeUtils {
));
}
public static void themeDialogActionButton(MaterialButton button) {
public static void themeBorderlessButton(Button button, int primaryColor) {
if (button == null) {
return;
}
Context context = button.getContext();
int accentColor = ThemeUtils.primaryAccentColor(button.getContext());
int disabledColor = ContextCompat.getColor(context, R.color.disabled_text);
button.setTextColor(new ColorStateList(
new int[][]{
@ -583,7 +581,7 @@ public final class ThemeUtils {
new int[]{-android.R.attr.state_enabled}, // disabled
},
new int[]{
accentColor,
primaryColor,
disabledColor
}
));

View file

@ -3,6 +3,8 @@
Nextcloud Android client application
@author Tobias Kaminsky
@author Andy Scherzinger
Copyright (C) 2020 Andy Scherzinger
Copyright (C) 2018 Tobias Kaminsky
Copyright (C) 2018 Nextcloud GmbH.
@ -20,11 +22,22 @@
along with this program. If not, see <https://www.gnu.org/licenses/>.
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="clip_horizontal"
android:orientation="vertical"
android:padding="@dimen/standard_padding">
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="clip_horizontal"
android:orientation="vertical"
android:paddingStart="@dimen/standard_padding"
android:paddingEnd="@dimen/standard_padding">
<TextView
android:id="@+id/helper_text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingStart="@dimen/standard_half_padding"
android:paddingEnd="@dimen/standard_half_padding"
android:text="@string/choose_template_helper_text"
android:textColor="@color/secondary_text_color"
android:visibility="invisible" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/list"
@ -32,11 +45,24 @@
android:layout_height="0dp"
android:layout_weight="1" />
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/filename"
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/filename_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="@string/filename_hint"
android:inputType="text"
android:importantForAutofill="no" />
android:paddingTop="@dimen/standard_padding">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/filename"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:ems="10"
android:gravity="top"
android:importantForAutofill="no"
android:inputType="text"
android:scrollbars="vertical">
</com.google.android.material.textfield.TextInputEditText>
</com.google.android.material.textfield.TextInputLayout>
</LinearLayout>

View file

@ -3,6 +3,8 @@
Nextcloud Android client application
@author Tobias Kaminsky
@author Andy Scherzinger
Copyright (C) 2020 Andy Scherzinger
Copyright (C) 2018 Tobias Kaminsky
Copyright (C) 2018 Nextcloud GmbH.
@ -19,25 +21,35 @@
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:paddingTop="@dimen/standard_padding"
tools:ignore="UseCompoundDrawables">
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:paddingTop="@dimen/standard_padding">
<ImageView
android:id="@+id/thumbnail"
android:layout_width="@dimen/share_icon_size"
android:layout_height="@dimen/share_icon_size"
<com.google.android.material.card.MaterialCardView
android:id="@+id/template_container"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:contentDescription="@string/thumbnail"
android:src="@drawable/file_doc"/>
app:cardElevation="0dp"
app:strokeColor="@color/grey_200"
app:strokeWidth="2dp">
<ImageView
android:id="@+id/template"
android:layout_width="60dp"
android:layout_height="84dp"
android:layout_margin="@dimen/standard_margin"
android:contentDescription="@string/thumbnail"
android:src="@drawable/file_doc" />
</com.google.android.material.card.MaterialCardView>
<TextView
android:id="@+id/name"
android:id="@+id/template_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
@ -45,5 +57,5 @@
android:gravity="center_horizontal"
android:paddingTop="@dimen/standard_half_padding"
android:textColor="@color/text_color"
tools:text="@tools:sample/lorem" />
tools:text="Template" />
</LinearLayout>

View file

@ -22,6 +22,7 @@
<color name="text_color">#E3E3E3</color>
<color name="text_color_inverse">#000000</color>
<color name="disabled_text">#ff6F6F6F</color>
<color name="secondary_text_color">#A5A5A5</color>
<color name="list_divider_background">#222222</color>

View file

@ -954,4 +954,7 @@
<string name="direct_login_failed">Login via direct link failed!</string>
<string name="login_url_helper_text">The link to your %1$s web interface when you open it in the browser.</string>
<string name="brute_force_delay">Delayed due to too many wrong attempts</string>
<string name="create">Create</string>
<string name="select_one_template">Please select one template</string>
<string name="choose_template_helper_text">Please choose a template and enter a file name.</string>
</resources>